diff --git a/frontend/src/locale/de.json b/frontend/src/locale/de.json index d7900af1..78d6ec39 100644 --- a/frontend/src/locale/de.json +++ b/frontend/src/locale/de.json @@ -1677,14 +1677,6 @@ "link": "Link", "urlLink": "URL-Link", "repoUrl": "Repository-URL", - "reddit": { - "id": "Client-ID", - "secret": "Client-Secret", - "agent": "User-Agent", - "searchQueries": "Suchanfragen", - "numberOfPosts": "Anzahl der Beiträge", - "addQuery": "Anfrage hinzufügen" - }, "drag": { "title": "Anhänge hier ablegen", "description": "Loslassen, um deine Anhänge hochzuladen" @@ -1706,8 +1698,6 @@ "clear": "Löschen", "ingestionFailed": "Aufnahme fehlgeschlagen." }, - "showAdvanced": "Erweiterte Optionen anzeigen", - "hideAdvanced": "Erweiterte Optionen ausblenden", "ingestors": { "local_file": { "label": "Datei hochladen", @@ -1725,26 +1715,6 @@ "label": "GitHub", "heading": "Inhalt von GitHub hinzufügen" }, - "reddit": { - "label": "Reddit", - "heading": "Inhalt von Reddit hinzufügen" - }, - "google_drive": { - "label": "Google Drive", - "heading": "Von Google Drive hochladen" - }, - "s3": { - "label": "Amazon S3", - "heading": "Inhalt von Amazon S3 hinzufügen" - }, - "share_point": { - "label": "SharePoint", - "heading": "Von SharePoint hochladen" - }, - "confluence": { - "label": "Confluence", - "heading": "Von Confluence hochladen" - }, "wiki": { "label": "Neues Wiki", "heading": "Lebendiges Wiki erstellen" @@ -1798,15 +1768,8 @@ }, "fields": { "url": "URL", - "awsAccessKeyId": "AWS-Zugriffsschlüssel-ID", - "awsSecretAccessKey": "Geheimer AWS-Zugriffsschlüssel", - "bucket": "Bucket-Name", - "prefix": "Pfadpräfix (optional)", - "region": "AWS-Region", - "endpointUrl": "Benutzerdefinierte Endpunkt-URL (optional)", "initialContent": "Anfangsinhalt (optional)" }, - "newCredentials": "Neue Zugangsdaten eingeben", "github": { "privateHint": "Privates Repository? Verbinde dein GitHub-Konto, um es zu synchronisieren.", "connectedHint": "Private Repositorys werden über deine GitHub-Verbindung ({{account}}) synchronisiert.", diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 4c03953c..f9f58131 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -1683,14 +1683,6 @@ "link": "Link", "urlLink": "URL Link", "repoUrl": "Repository URL", - "reddit": { - "id": "Client ID", - "secret": "Client Secret", - "agent": "User agent", - "searchQueries": "Search queries", - "numberOfPosts": "Number of posts", - "addQuery": "Add Query" - }, "drag": { "title": "Drop attachments here", "description": "Release to upload your attachments" @@ -1712,8 +1704,6 @@ "clear": "Clear", "ingestionFailed": "Ingestion failed." }, - "showAdvanced": "Show advanced options", - "hideAdvanced": "Hide advanced options", "ingestors": { "local_file": { "label": "Upload File", @@ -1731,26 +1721,6 @@ "label": "GitHub", "heading": "Add content from GitHub" }, - "reddit": { - "label": "Reddit", - "heading": "Add content from Reddit" - }, - "google_drive": { - "label": "Google Drive", - "heading": "Upload from Google Drive" - }, - "s3": { - "label": "Amazon S3", - "heading": "Add content from Amazon S3" - }, - "share_point": { - "label": "SharePoint", - "heading": "Upload from SharePoint" - }, - "confluence": { - "label": "Confluence", - "heading": "Upload from Confluence" - }, "wiki": { "label": "New wiki", "heading": "Create a living wiki" @@ -1804,15 +1774,8 @@ }, "fields": { "url": "URL", - "awsAccessKeyId": "AWS Access Key ID", - "awsSecretAccessKey": "AWS Secret Access Key", - "bucket": "Bucket Name", - "prefix": "Path Prefix (optional)", - "region": "AWS Region", - "endpointUrl": "Custom Endpoint URL (optional)", "initialContent": "Initial content (optional)" }, - "newCredentials": "Enter new credentials", "github": { "privateHint": "Private repository? Connect your GitHub account to sync it.", "connectedHint": "Private repositories sync from your GitHub connection ({{account}}).", diff --git a/frontend/src/locale/es.json b/frontend/src/locale/es.json index f2065bf4..62aa406c 100644 --- a/frontend/src/locale/es.json +++ b/frontend/src/locale/es.json @@ -1677,14 +1677,6 @@ "link": "Enlace", "urlLink": "Enlace URL", "repoUrl": "URL del Repositorio", - "reddit": { - "id": "ID del Cliente", - "secret": "Secreto del Cliente", - "agent": "Agente de usuario", - "searchQueries": "Consultas de búsqueda", - "numberOfPosts": "Número de publicaciones", - "addQuery": "Agregar Consulta" - }, "drag": { "title": "Suelta los archivos adjuntos aquí", "description": "Suelta para subir tus archivos adjuntos" @@ -1706,8 +1698,6 @@ "clear": "Limpiar", "ingestionFailed": "La ingesta falló." }, - "showAdvanced": "Mostrar opciones avanzadas", - "hideAdvanced": "Ocultar opciones avanzadas", "ingestors": { "local_file": { "label": "Subir archivo", @@ -1725,26 +1715,6 @@ "label": "GitHub", "heading": "Agregar contenido desde GitHub" }, - "reddit": { - "label": "Reddit", - "heading": "Agregar contenido desde Reddit" - }, - "google_drive": { - "label": "Google Drive", - "heading": "Subir desde Google Drive" - }, - "s3": { - "label": "Amazon S3", - "heading": "Agregar contenido desde Amazon S3" - }, - "share_point": { - "label": "SharePoint", - "heading": "Subir desde SharePoint" - }, - "confluence": { - "label": "Confluence", - "heading": "Subir desde Confluence" - }, "wiki": { "label": "Nueva wiki", "heading": "Crear una wiki viva" @@ -1798,15 +1768,8 @@ }, "fields": { "url": "URL", - "awsAccessKeyId": "ID de clave de acceso de AWS", - "awsSecretAccessKey": "Clave de acceso secreta de AWS", - "bucket": "Nombre del bucket", - "prefix": "Prefijo de ruta (opcional)", - "region": "Región de AWS", - "endpointUrl": "URL de endpoint personalizada (opcional)", "initialContent": "Contenido inicial (opcional)" }, - "newCredentials": "Introducir credenciales nuevas", "github": { "privateHint": "¿Repositorio privado? Conecta tu cuenta de GitHub para sincronizarlo.", "connectedHint": "Los repositorios privados se sincronizan desde tu conexión de GitHub ({{account}}).", diff --git a/frontend/src/locale/jp.json b/frontend/src/locale/jp.json index e6ccba46..512fcfc4 100644 --- a/frontend/src/locale/jp.json +++ b/frontend/src/locale/jp.json @@ -1666,14 +1666,6 @@ "link": "リンク", "urlLink": "URLリンク", "repoUrl": "リポジトリURL", - "reddit": { - "id": "クライアントID", - "secret": "クライアントシークレット", - "agent": "ユーザーエージェント", - "searchQueries": "検索クエリ", - "numberOfPosts": "投稿数", - "addQuery": "クエリを追加" - }, "drag": { "title": "添付ファイルをここにドロップ", "description": "リリースして添付ファイルをアップロード" @@ -1695,8 +1687,6 @@ "clear": "クリア", "ingestionFailed": "取り込みに失敗しました。" }, - "showAdvanced": "詳細オプションを表示", - "hideAdvanced": "詳細オプションを非表示", "ingestors": { "local_file": { "label": "ファイルをアップロード", @@ -1714,26 +1704,6 @@ "label": "GitHub", "heading": "GitHubからコンテンツを追加" }, - "reddit": { - "label": "Reddit", - "heading": "Redditからコンテンツを追加" - }, - "google_drive": { - "label": "Google Drive", - "heading": "Google Driveからアップロード" - }, - "s3": { - "label": "Amazon S3", - "heading": "Amazon S3からコンテンツを追加" - }, - "share_point": { - "label": "SharePoint", - "heading": "SharePointからアップロード" - }, - "confluence": { - "label": "Confluence", - "heading": "Confluenceからアップロード" - }, "wiki": { "label": "新しいWiki", "heading": "リビングWikiを作成" @@ -1787,15 +1757,8 @@ }, "fields": { "url": "URL", - "awsAccessKeyId": "AWS アクセスキー ID", - "awsSecretAccessKey": "AWS シークレットアクセスキー", - "bucket": "バケット名", - "prefix": "パスプレフィックス(任意)", - "region": "AWS リージョン", - "endpointUrl": "カスタムエンドポイント URL(任意)", "initialContent": "初期コンテンツ(任意)" }, - "newCredentials": "新しい認証情報を入力", "github": { "privateHint": "プライベートリポジトリですか?同期するには GitHub アカウントを接続してください。", "connectedHint": "プライベートリポジトリは GitHub 接続({{account}})から同期されます。", diff --git a/frontend/src/locale/ru.json b/frontend/src/locale/ru.json index 4007f104..751e8822 100644 --- a/frontend/src/locale/ru.json +++ b/frontend/src/locale/ru.json @@ -1761,14 +1761,6 @@ "link": "Ссылка", "urlLink": "URL ссылка", "repoUrl": "URL репозитория", - "reddit": { - "id": "ID клиента", - "secret": "Секрет клиента", - "agent": "Пользовательский агент", - "searchQueries": "Поисковые запросы", - "numberOfPosts": "Количество сообщений", - "addQuery": "Добавить запрос" - }, "drag": { "title": "Перетащите вложения сюда", "description": "Отпустите, чтобы загрузить ваши вложения" @@ -1790,8 +1782,6 @@ "clear": "Очистить", "ingestionFailed": "Не удалось загрузить данные." }, - "showAdvanced": "Показать расширенные настройки", - "hideAdvanced": "Скрыть расширенные настройки", "ingestors": { "local_file": { "label": "Загрузить файл", @@ -1809,26 +1799,6 @@ "label": "GitHub", "heading": "Добавить контент из GitHub" }, - "reddit": { - "label": "Reddit", - "heading": "Добавить контент из Reddit" - }, - "google_drive": { - "label": "Google Drive", - "heading": "Загрузить из Google Drive" - }, - "s3": { - "label": "Amazon S3", - "heading": "Добавить контент из Amazon S3" - }, - "share_point": { - "label": "SharePoint", - "heading": "Загрузить из SharePoint" - }, - "confluence": { - "label": "Confluence", - "heading": "Загрузить из Confluence" - }, "wiki": { "label": "Новая вики", "heading": "Создать живую вики" @@ -1882,15 +1852,8 @@ }, "fields": { "url": "URL", - "awsAccessKeyId": "Идентификатор ключа доступа AWS", - "awsSecretAccessKey": "Секретный ключ доступа AWS", - "bucket": "Имя бакета", - "prefix": "Префикс пути (необязательно)", - "region": "Регион AWS", - "endpointUrl": "Пользовательский URL эндпоинта (необязательно)", "initialContent": "Исходное содержимое (необязательно)" }, - "newCredentials": "Ввести новые учётные данные", "github": { "privateHint": "Приватный репозиторий? Подключите аккаунт GitHub, чтобы синхронизировать его.", "connectedHint": "Приватные репозитории синхронизируются через ваше подключение GitHub ({{account}}).", diff --git a/frontend/src/locale/zh-TW.json b/frontend/src/locale/zh-TW.json index 4dcf1061..49fe67e3 100644 --- a/frontend/src/locale/zh-TW.json +++ b/frontend/src/locale/zh-TW.json @@ -1666,14 +1666,6 @@ "link": "連結", "urlLink": "URL 連結", "repoUrl": "儲存庫 URL", - "reddit": { - "id": "客戶端ID", - "secret": "客戶端密鑰", - "agent": "使用者代理", - "searchQueries": "搜尋查詢", - "numberOfPosts": "貼文數量", - "addQuery": "新增查詢" - }, "drag": { "title": "將附件拖放到此處", "description": "釋放以上傳您的附件" @@ -1695,8 +1687,6 @@ "clear": "清除", "ingestionFailed": "匯入失敗。" }, - "showAdvanced": "顯示進階選項", - "hideAdvanced": "隱藏進階選項", "ingestors": { "local_file": { "label": "上傳檔案", @@ -1714,26 +1704,6 @@ "label": "GitHub", "heading": "從GitHub新增內容" }, - "reddit": { - "label": "Reddit", - "heading": "從Reddit新增內容" - }, - "google_drive": { - "label": "Google Drive", - "heading": "從Google Drive上傳" - }, - "s3": { - "label": "Amazon S3", - "heading": "從Amazon S3新增內容" - }, - "share_point": { - "label": "SharePoint", - "heading": "從SharePoint上傳" - }, - "confluence": { - "label": "Confluence", - "heading": "從Confluence上傳" - }, "wiki": { "label": "新 Wiki", "heading": "建立動態 Wiki" @@ -1787,15 +1757,8 @@ }, "fields": { "url": "URL", - "awsAccessKeyId": "AWS 存取金鑰 ID", - "awsSecretAccessKey": "AWS 秘密存取金鑰", - "bucket": "儲存貯體名稱", - "prefix": "路徑前綴(選填)", - "region": "AWS 區域", - "endpointUrl": "自訂端點 URL(選填)", "initialContent": "初始內容(選填)" }, - "newCredentials": "輸入新的憑證", "github": { "privateHint": "私人儲存庫?連結你的 GitHub 帳號即可同步。", "connectedHint": "私人儲存庫透過你的 GitHub 連線({{account}})同步。", diff --git a/frontend/src/locale/zh.json b/frontend/src/locale/zh.json index 020d5dce..8b36f724 100644 --- a/frontend/src/locale/zh.json +++ b/frontend/src/locale/zh.json @@ -1666,14 +1666,6 @@ "link": "链接", "urlLink": "URL 链接", "repoUrl": "存储库 URL", - "reddit": { - "id": "客户端ID", - "secret": "客户端密钥", - "agent": "用户代理", - "searchQueries": "搜索查询", - "numberOfPosts": "帖子数量", - "addQuery": "添加查询" - }, "drag": { "title": "将附件拖放到此处", "description": "释放以上传您的附件" @@ -1695,8 +1687,6 @@ "clear": "清除", "ingestionFailed": "导入失败。" }, - "showAdvanced": "显示高级选项", - "hideAdvanced": "隐藏高级选项", "ingestors": { "local_file": { "label": "上传文件", @@ -1714,26 +1704,6 @@ "label": "GitHub", "heading": "从GitHub添加内容" }, - "reddit": { - "label": "Reddit", - "heading": "从Reddit添加内容" - }, - "google_drive": { - "label": "Google Drive", - "heading": "从Google Drive上传" - }, - "s3": { - "label": "Amazon S3", - "heading": "从Amazon S3添加内容" - }, - "share_point": { - "label": "SharePoint", - "heading": "从SharePoint上传" - }, - "confluence": { - "label": "Confluence", - "heading": "从Confluence上传" - }, "wiki": { "label": "新建 Wiki", "heading": "创建动态 Wiki" @@ -1787,15 +1757,8 @@ }, "fields": { "url": "URL", - "awsAccessKeyId": "AWS 访问密钥 ID", - "awsSecretAccessKey": "AWS 秘密访问密钥", - "bucket": "存储桶名称", - "prefix": "路径前缀(可选)", - "region": "AWS 区域", - "endpointUrl": "自定义端点 URL(可选)", "initialContent": "初始内容(可选)" }, - "newCredentials": "输入新的凭据", "github": { "privateHint": "私有仓库?连接你的 GitHub 账号即可同步。", "connectedHint": "私有仓库通过你的 GitHub 连接({{account}})同步。", diff --git a/frontend/src/upload/Upload.test.tsx b/frontend/src/upload/Upload.test.tsx index 5af0f062..96b0df5c 100644 --- a/frontend/src/upload/Upload.test.tsx +++ b/frontend/src/upload/Upload.test.tsx @@ -51,25 +51,6 @@ vi.mock('../connectors/useConnectorLauncher', () => ({ default: () => ({ launch, modals: null }), })); -// The file picker reports its default account and a pick as it mounts. -vi.mock('../components/FilePicker', async () => { - const { useEffect } = await import('react'); - return { - FilePicker: function Picker(props: { - onConnectionChange: (id: string | null) => void; - onSelectionChange: (files: string[], folders?: string[]) => void; - onFirstPickName: (name: string) => void; - }) { - useEffect(() => { - props.onConnectionChange('drive-1'); - props.onSelectionChange(['file-1'], []); - props.onFirstPickName('Handbook'); - }, []); - return null; - }, - }; -}); - import Upload from './Upload'; Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true }); @@ -308,92 +289,57 @@ describe('Upload source-type tiles', () => { }); }); - it('picks the saved account once connections finish loading', async () => { - connectorsState.catalog = [ - { - key: 's3', - icon: 's3', - sync_ingestor: 's3', - auth_kind: 'api_key', - available: true, - missing_settings: [], - credential_fields: [ - { key: 'aws_access_key_id', label: 'Access key ID', secret: false }, - ], - }, - ]; - const props = { - receivedFile: [], - setModalState: vi.fn(), - isOnboarding: false, - renderTab: null, - close: vi.fn(), - initialIngestor: 's3' as const, - }; - await act(async () => root.render()); - const triggers = () => - Array.from( - document.body.querySelectorAll('[data-slot="select-trigger"]'), - ).map((el) => el.textContent); - expect(triggers()).not.toContain('modals.uploadDoc.newCredentials'); - connectorsState.connections = [ - { - id: 'k1', - connector_key: 's3', - status: 'connected', - account_label: '…WXYZ', - }, - ]; - await act(async () => root.render()); - expect(triggers()).toContain('settings.connectors.detail.keyEnding'); - connectorsState.catalog = []; - connectorsState.connections = []; - }); - - it("sends the picker's account, not one cleared by the type change", async () => { - connectorsState.catalog = [ - { - key: 'google_drive', - icon: 'drive', - sync_ingestor: 'google_drive', - auth_kind: 'oauth', - available: true, - missing_settings: [], - }, - ]; - const sent: FormData[] = []; + it('sends a crawler source to the remote ingest with its URL', async () => { + const sent: { url: string; body: FormData }[] = []; class FakeXhr { upload = { addEventListener() {} }; + url = ''; addEventListener() {} - open() {} + open(_method: string, url: string) { + this.url = url; + } setRequestHeader() {} send(body: FormData) { - sent.push(body); + sent.push({ url: this.url, body }); } } vi.stubGlobal('XMLHttpRequest', FakeXhr); - await act(async () => - root.render( - , - ), + await render(); + const crawler = tiles().find((tile) => + tile.textContent?.includes('modals.uploadDoc.ingestors.crawler.label'), + )!; + await act(async () => crawler.click()); + const type = async (input: HTMLInputElement, value: string) => { + const setter = Object.getOwnPropertyDescriptor( + HTMLInputElement.prototype, + 'value', + )!.set!; + await act(async () => { + setter.call(input, value); + input.dispatchEvent(new Event('input', { bubbles: true })); + }); + }; + const inputs = Array.from( + document.body.querySelectorAll('input[type="text"]'), + ); + // The source's name first, then the crawler's URL field. + await type(inputs[0], 'Docs site'); + await type( + document.body.querySelector('input[name="url"]')!, + 'https://docs.example', ); const train = Array.from(document.body.querySelectorAll('button')).find( (b) => b.textContent === 'modals.uploadDoc.train', )!; + expect(train.disabled).toBe(false); await act(async () => train.click()); expect(sent).toHaveLength(1); - expect(JSON.parse(String(sent[0].get('data'))).connection_id).toBe( - 'drive-1', - ); + expect(sent[0].url).toContain('/api/remote'); + expect(sent[0].body.get('source')).toBe('crawler'); + expect(JSON.parse(String(sent[0].body.get('data')))).toEqual({ + url: 'https://docs.example', + }); vi.unstubAllGlobals(); - connectorsState.catalog = []; }); it('leaves the disabled Train button on the default variant', async () => { diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index 16b2042e..e3df3605 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -1,7 +1,6 @@ import { ChevronLeft, FileText, Lock, Plug } from 'lucide-react'; import { envVar } from '@/env'; -import { cn } from '@/lib/utils'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { nanoid } from '@reduxjs/toolkit'; import type { FileRejection } from 'react-dropzone'; import { useTranslation } from 'react-i18next'; @@ -14,20 +13,9 @@ import { Alert, AlertDescription } from '../components/ui/alert'; import { Button } from '../components/ui/button'; import { Input } from '../components/ui/input'; import { FormField as UiFormField } from '../components/ui/form-field'; -import { Label } from '../components/ui/label'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '../components/ui/select'; -import { Switch } from '../components/ui/switch'; import { Textarea } from '../components/ui/textarea'; import { Modal } from '../components/ui/modal'; -import { Separator } from '../components/ui/separator'; import { OptionCard } from '../components/ui/option-card'; -import ConnectorSetupNotice from '../connectors/ConnectorSetupNotice'; import useConnectorLauncher from '../connectors/useConnectorLauncher'; import { connectorName } from '../connectors/i18n'; import { intlLocale } from '../utils/dateTimeUtils'; @@ -53,7 +41,6 @@ import { setSourceDocs, } from '../preferences/preferenceSlice'; import { - CONNECTION_INGESTORS, IngestorDefaultConfigs, IngestorFormSchemas, getIngestorSchema, @@ -64,8 +51,6 @@ import { addUploadTask, updateUploadTask } from './uploadSlice'; import { FormField, IngestorConfig, IngestorType } from './types/ingestor'; -import { FilePicker } from '../components/FilePicker'; -import GoogleDrivePicker from '../components/GoogleDrivePicker'; import { FILE_UPLOAD_ACCEPT } from '../constants/fileUpload'; import RetrievalOptions, { DEFAULT_RETRIEVAL_OPTIONS, @@ -86,7 +71,6 @@ function Upload({ close, onSuccessfulUpload = () => undefined, selectUploadedDoc = true, - initialIngestor, }: { receivedFile: File[]; setModalState: (state: ActiveState) => void; @@ -101,8 +85,6 @@ function Upload({ * uploading never repoints the conversation the user left open. */ selectUploadedDoc?: boolean; - /** Open straight on this source type's form (Connect from a connector card). */ - initialIngestor?: IngestorType; }) { const token = useSelector(selectToken); const selectedDocs = useSelector(selectSelectedDocs); @@ -124,82 +106,18 @@ function Upload({ // an unaccepted type), shown under the dropzone. const [rejectedFiles, setRejectedFiles] = useState([]); const [activeTab, setActiveTab] = useState(true); - const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); const [retrievalOptions, setRetrievalOptions] = useState(DEFAULT_RETRIEVAL_OPTIONS); const { graphRAGAvailable, hybridAvailable, availableModels } = useRetrievalAvailability(token); - // File picker state - const [selectedFiles, setSelectedFiles] = useState([]); - const [selectedFolders, setSelectedFolders] = useState([]); - // The connection (account) the source syncs from. Pickers report it; S3 - // and Reddit pick it here ('' means "enter new credentials"). - const [connectionId, setConnectionId] = useState(null); - const renderFormFields = () => { if (!ingestor.type) return null; const ingestorSchema = getIngestorSchema(ingestor.type as IngestorType); if (!ingestorSchema) return null; - const schema: FormField[] = ingestorSchema.fields; - - const generalFields = schema.filter( - (field: FormField) => - !field.advanced && !(usingSavedKeys && credentialKeys.has(field.name)), - ); - const advancedFields = schema.filter((field: FormField) => field.advanced); - return (
- {keyAccounts.length > 0 && ( - - - - )} -
- {generalFields.map((field: FormField) => renderField(field))} -
- - {advancedFields.length > 0 && ( -
-
- -
- {advancedFields.map((field: FormField) => renderField(field))} -
-
-
- )} + {ingestorSchema.fields.map((field: FormField) => renderField(field))}
); }; @@ -227,81 +145,6 @@ function Upload({ required={isRequired} /> ); - case 'number': - return ( - - handleIngestorChange( - field.name as keyof IngestorConfig['config'], - Number(e.target.value), - ) - } - required={isRequired} - /> - ); - case 'enum': { - const currentValue = String( - ingestor.config[field.name as keyof typeof ingestor.config] ?? '', - ); - return ( - - - - ); - } - case 'boolean': - return ( -
- - { - handleIngestorChange( - field.name as keyof IngestorConfig['config'], - checked, - ); - }} - /> -
- ); case 'textarea': return ( ); - case 'remote_file_picker': - return ( - { - setSelectedFiles(selectedFileIds); - setSelectedFolders(selectedFolderIds); - }} - onFirstPickName={prefillName} - onConnectionChange={setConnectionId} - provider={ingestor.type as unknown as string} - token={token} - initialSelectedFiles={selectedFiles} - initialSelectedFolders={selectedFolders} - /> - ); - case 'google_drive_picker': - // Google's own picker needs the client id in the browser; without it - // the server-side file browser lists the same Drive. - return envVar('VITE_GOOGLE_CLIENT_ID') ? ( - { - setSelectedFiles(selectedFileIds); - setSelectedFolders(selectedFolderIds); - }} - onFirstPickName={prefillName} - onConnectionChange={setConnectionId} - token={token} - /> - ) : ( - { - setSelectedFiles(selectedFileIds); - setSelectedFolders(selectedFolderIds); - }} - onFirstPickName={prefillName} - onConnectionChange={setConnectionId} - provider="google_drive" - token={token} - initialSelectedFiles={selectedFiles} - initialSelectedFolders={selectedFolders} - /> - ); - case 'share_point_picker': - return ( - { - setSelectedFiles(selectedFileIds); - setSelectedFolders(selectedFolderIds); - }} - onFirstPickName={prefillName} - onConnectionChange={setConnectionId} - provider="share_point" - token={token} - initialSelectedFiles={selectedFiles} - initialSelectedFolders={selectedFolders} - /> - ); - case 'confluence_picker': - return ( - { - setSelectedFiles(selectedFileIds); - setSelectedFolders(selectedFolderIds); - }} - onFirstPickName={prefillName} - onConnectionChange={setConnectionId} - provider="confluence" - token={token} - initialSelectedFiles={selectedFiles} - initialSelectedFolders={selectedFolders} - /> - ); default: return null; } @@ -484,50 +235,6 @@ function Upload({ if (!connectorsLoaded) dispatch(loadConnectors({ token })); }, [connectorsLoaded, dispatch, token]); - useEffect(() => { - if (initialIngestor) handleIngestorTypeChange(initialIngestor); - // Only the type the modal opened with; later picks are the user's. - }, []); - - /** The name field follows the first picked item until the user edits it. */ - const prefillName = (name: string) => { - if (nameTouched || !name) return; - setIngestor((prev) => (prev.name ? prev : { ...prev, name })); - }; - - const connectorFor = (type: IngestorType | null) => - type && CONNECTION_INGESTORS.includes(type) - ? connectorCatalog.find((c) => c.sync_ingestor === type) - : undefined; - const selectedConnector = connectorFor(ingestor.type); - const needsSetup = !!selectedConnector && !selectedConnector.available; - // S3 and Reddit keep their keys on a connection: once an account is - // chosen the key fields go away ("enter secrets once"). - const keyAccounts = - selectedConnector?.auth_kind === 'api_key' - ? connections.filter( - (c) => - c.connector_key === selectedConnector.key && - c.status === 'connected', - ) - : []; - const credentialKeys = new Set( - (selectedConnector?.credential_fields ?? []).map((f) => f.key), - ); - const usingSavedKeys = - keyAccounts.length > 0 && !!connectionId && connectionId !== 'new'; - - // Default to the first saved account, also when the connections arrive - // after the modal opened on S3 or Reddit, but never over a choice the - // user made in the account picker. - const accountPicked = useRef(false); - const firstKeyAccount = keyAccounts[0]?.id; - useEffect(() => { - if (selectedConnector?.auth_kind !== 'api_key' || accountPicked.current) - return; - setConnectionId(firstKeyAccount ?? 'new'); - }, [ingestor.type, selectedConnector?.auth_kind, firstKeyAccount]); - const ingestorOptions: IngestorOption[] = IngestorFormSchemas.map( (schema) => ({ label: schema.label, @@ -541,9 +248,6 @@ function Upload({ setIngestor({ type: null, name: '', config: {} }); setfiles([]); setRejectedFiles([]); - setSelectedFiles([]); - setSelectedFolders([]); - setShowAdvancedOptions(false); setRetrievalOptions(DEFAULT_RETRIEVAL_OPTIONS); setNameTouched(false); }, []); @@ -824,62 +528,11 @@ function Upload({ JSON.stringify(optionsToConfig(retrievalOptions)), ); - const ingestorSchema = getIngestorSchema(ingestor.type as IngestorType); - if (!ingestorSchema) { - handleTaskFailure(clientTaskId); - return; - } - - const schema: FormField[] = ingestorSchema.fields; - const hasLocalFilePicker = schema.some( - (field: FormField) => field.type === 'local_file_picker', - ); - const hasRemoteFilePicker = schema.some( - (field: FormField) => field.type === 'remote_file_picker', - ); - const hasGoogleDrivePicker = schema.some( - (field: FormField) => field.type === 'google_drive_picker', - ); - const hasSharePointPicker = schema.some( - (field: FormField) => field.type === 'share_point_picker', - ); - const hasConfluencePicker = schema.some( - (field: FormField) => field.type === 'confluence_picker', - ); - - let configData: Record = { ...ingestor.config }; - - if (hasLocalFilePicker) { - files.forEach((file) => { - formData.append('file', file); - }); - } else if ( - hasRemoteFilePicker || - hasGoogleDrivePicker || - hasSharePointPicker || - hasConfluencePicker - ) { - configData = { - provider: ingestor.type as string, - connection_id: connectionId, - file_ids: selectedFiles, - folder_ids: selectedFolders, - }; - } - - if (usingSavedKeys) { - configData = Object.fromEntries( - Object.entries(configData).filter(([key]) => !credentialKeys.has(key)), - ); - configData.connection_id = connectionId; - } - formData.append('data', JSON.stringify(configData)); + formData.append('data', JSON.stringify(ingestor.config)); const apiHost: string = envVar('VITE_API_HOST'); - const endpoint = - ingestor.type === 'local_file' - ? `${apiHost}/api/upload` - : `${apiHost}/api/remote`; + // Local files go through uploadFile; everything else is fetched remotely. + const endpoint = `${apiHost}/api/remote`; const xhr = new XMLHttpRequest(); @@ -1041,72 +694,14 @@ function Upload({ if (!isPrescreenConfigValid(retrievalOptions)) return true; if (!ingestor.type) return true; - if (needsSetup) return true; - const ingestorSchemaForValidation = getIngestorSchema( - ingestor.type as IngestorType, - ); - if (!ingestorSchemaForValidation) return true; - const schema: FormField[] = ingestorSchemaForValidation.fields; - const hasLocalFilePicker = schema.some( - (field: FormField) => field.type === 'local_file_picker', - ); - const hasRemoteFilePicker = schema.some( - (field: FormField) => field.type === 'remote_file_picker', - ); - const hasGoogleDrivePicker = schema.some( - (field: FormField) => field.type === 'google_drive_picker', - ); - const hasSharePointPicker = schema.some( - (field: FormField) => field.type === 'share_point_picker', - ); - const hasConfluencePicker = schema.some( - (field: FormField) => field.type === 'confluence_picker', - ); - - if (hasLocalFilePicker) { - if (files.length === 0) { - return true; - } - } else if ( - hasRemoteFilePicker || - hasGoogleDrivePicker || - hasSharePointPicker || - hasConfluencePicker - ) { - if (selectedFiles.length === 0 && selectedFolders.length === 0) { - return true; - } - } - - const ingestorSchemaForFields = getIngestorSchema( - ingestor.type as IngestorType, - ); - if (!ingestorSchemaForFields) return false; - const formFields: FormField[] = ingestorSchemaForFields.fields; - for (const field of formFields) { - if (usingSavedKeys && credentialKeys.has(field.name)) continue; - if (field.required) { - // Validate only required fields - const value = - ingestor.config[field.name as keyof typeof ingestor.config]; - - if (typeof value === 'string' && !value.trim()) { - return true; - } - - if ( - typeof value === 'number' && - (value === null || value === undefined || value <= 0) - ) { - return true; - } - - if (typeof value === 'boolean' && value === undefined) { - return true; - } - } - } - return false; + const schema = getIngestorSchema(ingestor.type); + if (!schema) return true; + return schema.fields.some((field: FormField) => { + if (field.type === 'local_file_picker') return files.length === 0; + if (!field.required) return false; + const value = ingestor.config[field.name]; + return typeof value === 'string' && !value.trim(); + }); }; const handleIngestorChange = ( key: keyof IngestorConfig['config'], @@ -1121,11 +716,6 @@ function Upload({ })); }; const handleIngestorTypeChange = (type: IngestorType | null) => { - // The account resets with the type, here rather than in an effect: the - // new type's picker reports its default account as it mounts, and an - // effect would run after that and clear it. - accountPicked.current = false; - setConnectionId(null); if (type === null) { setIngestor({ type: null, @@ -1259,10 +849,6 @@ function Upload({ ) : null; - const renderSetupNotice = () => - selectedConnector && needsSetup ? ( - - ) : null; if (handedOver) return <>{connectModals}; return ( @@ -1331,9 +917,9 @@ function Upload({ required={true} className="w-full" /> - {needsSetup ? renderSetupNotice() : renderFormFields()} + {renderFormFields()} {ingestor.type === 'github' && renderGitHubHandOver()} - {ingestor.type !== 'wiki' && !needsSetup && ( + {ingestor.type !== 'wiki' && ( )} - - {ingestor.type && - getIngestorSchema(ingestor.type as IngestorType)?.fields.some( - (field: FormField) => field.advanced, - ) && ( - - )} )} diff --git a/frontend/src/upload/types/ingestor.ts b/frontend/src/upload/types/ingestor.ts index efba3522..07031c35 100644 --- a/frontend/src/upload/types/ingestor.ts +++ b/frontend/src/upload/types/ingestor.ts @@ -2,24 +2,9 @@ import CrawlerIcon from '../../assets/crawler.svg'; import FileUploadIcon from '../../assets/file_upload.svg'; import UrlIcon from '../../assets/url.svg'; import GithubIcon from '../../assets/github.svg'; -import RedditIcon from '../../assets/reddit.svg'; -import DriveIcon from '../../assets/drive.svg'; -import S3Icon from '../../assets/s3.svg'; -import SharePoint from '../../assets/sharepoint.svg'; -import ConfluenceIcon from '../../assets/confluence.svg'; import BookIcon from '../../assets/book-mono.svg'; -export type IngestorType = - | 'confluence' - | 'crawler' - | 'github' - | 'reddit' - | 'url' - | 'google_drive' - | 'local_file' - | 's3' - | 'share_point' - | 'wiki'; +export type IngestorType = 'crawler' | 'github' | 'url' | 'local_file' | 'wiki'; export interface IngestorConfig { type: IngestorType | null; @@ -34,17 +19,7 @@ export type IngestorFormData = { data: string; }; -export type FieldType = - | 'string' - | 'number' - | 'enum' - | 'boolean' - | 'textarea' - | 'local_file_picker' - | 'remote_file_picker' - | 'google_drive_picker' - | 'share_point_picker' - | 'confluence_picker'; +export type FieldType = 'string' | 'textarea' | 'local_file_picker'; export interface FormField { name: string; @@ -53,8 +28,6 @@ export interface FormField { labelKey?: string; type: FieldType; required?: boolean; - advanced?: boolean; - options?: { label: string; value: string }[]; } export interface IngestorSchema { @@ -66,9 +39,8 @@ export interface IngestorSchema { } /** - * Add Source tiles: what needs no account. The connection types sync from - * the Connectors page; their forms open only when a connector card hands - * one over (`initialIngestor`). + * Add knowledge tiles: the source types that need no account. Services + * that sync into Knowledge are connected through the connect wizard. */ export const UPLOAD_AND_WEB_INGESTORS: IngestorType[] = [ 'local_file', @@ -77,13 +49,6 @@ export const UPLOAD_AND_WEB_INGESTORS: IngestorType[] = [ 'github', 'wiki', ]; -export const CONNECTION_INGESTORS: IngestorType[] = [ - 'google_drive', - 'share_point', - 'confluence', - 's3', - 'reddit', -]; export const IngestorFormSchemas: IngestorSchema[] = [ { @@ -145,141 +110,6 @@ export const IngestorFormSchemas: IngestorSchema[] = [ }, ], }, - { - key: 'reddit', - label: 'Reddit', - icon: RedditIcon, - heading: 'Add content from Reddit', - fields: [ - { - name: 'client_id', - label: 'Client ID', - labelKey: 'modals.uploadDoc.reddit.id', - type: 'string', - required: true, - }, - { - name: 'client_secret', - label: 'Client Secret', - labelKey: 'modals.uploadDoc.reddit.secret', - type: 'string', - required: true, - }, - { - name: 'user_agent', - label: 'User Agent', - labelKey: 'modals.uploadDoc.reddit.agent', - type: 'string', - required: true, - }, - { - name: 'search_queries', - label: 'Search Queries', - labelKey: 'modals.uploadDoc.reddit.searchQueries', - type: 'string', - required: true, - }, - { - name: 'number_posts', - label: 'Number of Posts', - labelKey: 'modals.uploadDoc.reddit.numberOfPosts', - type: 'number', - required: true, - }, - ], - }, - { - key: 'google_drive', - label: 'Google Drive', - icon: DriveIcon, - heading: 'Upload from Google Drive', - fields: [ - { - name: 'files', - label: 'Select Files from Google Drive', - type: 'google_drive_picker', - required: true, - }, - ], - }, - { - key: 's3', - label: 'Amazon S3', - icon: S3Icon, - heading: 'Add content from Amazon S3', - fields: [ - { - name: 'aws_access_key_id', - label: 'AWS Access Key ID', - labelKey: 'modals.uploadDoc.fields.awsAccessKeyId', - type: 'string', - required: true, - }, - { - name: 'aws_secret_access_key', - label: 'AWS Secret Access Key', - labelKey: 'modals.uploadDoc.fields.awsSecretAccessKey', - type: 'string', - required: true, - }, - { - name: 'bucket', - label: 'Bucket Name', - labelKey: 'modals.uploadDoc.fields.bucket', - type: 'string', - required: true, - }, - { - name: 'prefix', - label: 'Path Prefix (optional)', - labelKey: 'modals.uploadDoc.fields.prefix', - type: 'string', - required: false, - }, - { - name: 'region', - label: 'AWS Region', - labelKey: 'modals.uploadDoc.fields.region', - type: 'string', - required: false, - }, - { - name: 'endpoint_url', - label: 'Custom Endpoint URL (optional)', - labelKey: 'modals.uploadDoc.fields.endpointUrl', - type: 'string', - required: false, - }, - ], - }, - { - key: 'share_point', - label: 'Share Point', - icon: SharePoint, - heading: 'Upload from Share Point', - fields: [ - { - name: 'files', - label: 'Select Files from Share Point', - type: 'share_point_picker', - required: true, - }, - ], - }, - { - key: 'confluence', - label: 'Confluence', - icon: ConfluenceIcon, - heading: 'Upload from Confluence', - fields: [ - { - name: 'files', - label: 'Select Pages from Confluence', - type: 'confluence_picker', - required: true, - }, - ], - }, { key: 'wiki', label: 'New wiki', @@ -303,52 +133,8 @@ export const IngestorDefaultConfigs: Record< > = { crawler: { name: '', config: { url: '' } }, url: { name: '', config: { url: '' } }, - reddit: { - name: '', - config: { - client_id: '', - client_secret: '', - user_agent: '', - search_queries: '', - number_posts: 10, - }, - }, github: { name: '', config: { repo_url: '' } }, - google_drive: { - name: '', - config: { - file_ids: '', - folder_ids: '', - recursive: true, - }, - }, local_file: { name: '', config: { files: [] } }, - s3: { - name: '', - config: { - aws_access_key_id: '', - aws_secret_access_key: '', - bucket: '', - prefix: '', - region: 'us-east-1', - endpoint_url: '', - }, - }, - share_point: { - name: '', - config: { - file_ids: '', - folder_ids: '', - recursive: true, - }, - }, - confluence: { - name: '', - config: { - file_ids: '', - folder_ids: '', - }, - }, wiki: { name: '', config: {