diff --git a/webui/src/features/apiTester/ApiTesterContainer.jsx b/webui/src/features/apiTester/ApiTesterContainer.jsx index f3485c3..0752231 100644 --- a/webui/src/features/apiTester/ApiTesterContainer.jsx +++ b/webui/src/features/apiTester/ApiTesterContainer.jsx @@ -70,6 +70,7 @@ export default function ApiTesterContainer({ config, onMessage, authFetch }) { effectiveKey, selectedAccount, streamingMode, + attachedFiles, abortControllerRef, setLoading, setIsStreaming, @@ -104,6 +105,11 @@ export default function ApiTesterContainer({ config, onMessage, authFetch }) { t={t} message={message} setMessage={setMessage} + attachedFiles={attachedFiles} + setAttachedFiles={setAttachedFiles} + effectiveKey={effectiveKey} + selectedAccount={selectedAccount} + onMessage={onMessage} response={response} isStreaming={isStreaming} loading={loading} diff --git a/webui/src/features/apiTester/ChatPanel.jsx b/webui/src/features/apiTester/ChatPanel.jsx index 3fade56..4633e37 100644 --- a/webui/src/features/apiTester/ChatPanel.jsx +++ b/webui/src/features/apiTester/ChatPanel.jsx @@ -1,10 +1,16 @@ -import { Bot, Loader2, Send, Square, User, Zap } from 'lucide-react' +import { Bot, Loader2, Send, Square, User, Zap, Paperclip, X, FileIcon } from 'lucide-react' import clsx from 'clsx' +import { useRef, useState } from 'react' export default function ChatPanel({ t, message, setMessage, + attachedFiles = [], + setAttachedFiles, + effectiveKey, + selectedAccount, + onMessage, response, isStreaming, loading, @@ -13,6 +19,57 @@ export default function ChatPanel({ onRunTest, onStopGeneration, }) { + const fileInputRef = useRef(null) + const [uploadingFiles, setUploadingFiles] = useState(false) + + const handleFileSelect = async (e) => { + const files = Array.from(e.target.files) + if (files.length === 0) return + + if (!effectiveKey) { + onMessage('error', t('apiTester.missingApiKey') || 'Missing API Key') + return + } + + setUploadingFiles(true) + for (const file of files) { + const formData = new FormData() + formData.append('file', file) + formData.append('purpose', 'assistants') + + const headers = { + 'Authorization': `Bearer ${effectiveKey}`, + } + if (selectedAccount) { + headers['X-Ds2-Target-Account'] = selectedAccount + } + + try { + const res = await fetch('/v1/files', { + method: 'POST', + headers, + body: formData + }) + if (!res.ok) { + const err = await res.text() + onMessage('error', err || 'File upload failed') + continue + } + const data = await res.json() + setAttachedFiles(prev => [...prev, data]) + } catch (error) { + onMessage('error', error.message || 'Network error during upload') + } + } + setUploadingFiles(false) + if (fileInputRef.current) { + fileInputRef.current.value = '' + } + } + + const removeFile = (id) => { + setAttachedFiles(prev => prev.filter(f => f.id !== id)) + } return (