Spaces:
Sleeping
Sleeping
| import { memo, useState } from 'react'; | |
| import NodeShell from '../components/NodeShell.jsx'; | |
| import { useWorkflow } from '../context/WorkflowContext.jsx'; | |
| import { uploadContextFile } from '../lib/api.js'; | |
| import { getNodeAccent } from '../lib/nodeRegistry.js'; | |
| function arrayBufferToBase64(buffer) { | |
| const bytes = new Uint8Array(buffer); | |
| let binary = ''; | |
| bytes.forEach((byte) => { | |
| binary += String.fromCharCode(byte); | |
| }); | |
| return window.btoa(binary); | |
| } | |
| function KnowledgeAnswerFlowNode({ id, data, selected, type }) { | |
| const { backendUrl, getNodeHandles, patchNodeData } = useWorkflow(); | |
| const handles = getNodeHandles(type, data); | |
| const runtime = data.runtime || {}; | |
| const [isUploading, setIsUploading] = useState(false); | |
| const [uploadError, setUploadError] = useState(''); | |
| const handleUpload = async (event) => { | |
| const file = event.target.files?.[0]; | |
| event.target.value = ''; | |
| if (!file) { | |
| return; | |
| } | |
| setIsUploading(true); | |
| setUploadError(''); | |
| try { | |
| const contentBase64 = arrayBufferToBase64(await file.arrayBuffer()); | |
| const context = await uploadContextFile(backendUrl, file.name, contentBase64); | |
| patchNodeData(id, { | |
| source: 'uploaded', | |
| contextPath: context.context_path || '', | |
| originalPath: context.original_path || '', | |
| contextFilename: context.filename || file.name, | |
| contextCharacters: context.characters || 0, | |
| }); | |
| } catch (error) { | |
| console.error('Failed to upload knowledge context:', error); | |
| setUploadError(error instanceof Error ? error.message : 'Не удалось загрузить knowledge-файл'); | |
| } finally { | |
| setIsUploading(false); | |
| } | |
| }; | |
| return ( | |
| <NodeShell | |
| nodeId={id} | |
| title={data.title} | |
| accent={getNodeAccent(type)} | |
| selected={selected} | |
| status={runtime.status} | |
| inputs={handles.inputs} | |
| outputs={handles.outputs} | |
| > | |
| <div className="field-stack"> | |
| <label className="field-stack"> | |
| <span className="field-label">Источник знаний</span> | |
| <select | |
| className="nodrag node-input" | |
| value={data.source || 'uploaded'} | |
| onChange={(event) => patchNodeData(id, { source: event.target.value })} | |
| > | |
| <option value="uploaded">Загруженный файл</option> | |
| <option value="rag">RAG-заглушка</option> | |
| </select> | |
| </label> | |
| {data.source !== 'rag' ? ( | |
| <label className="field-stack"> | |
| <span className="field-label">Knowledge-файл для этой ноды</span> | |
| <input | |
| className="nodrag node-input" | |
| type="file" | |
| accept=".doc,.docx,.md,.txt" | |
| disabled={isUploading} | |
| onChange={handleUpload} | |
| /> | |
| <small> | |
| {data.contextPath | |
| ? `${data.contextFilename || 'Загруженный файл'} -> ${data.contextPath}` | |
| : 'Файл к этой ноде не привязан'} | |
| </small> | |
| </label> | |
| ) : null} | |
| <div className="node-note"> | |
| {runtime.answer | |
| ? `Ответ: ${runtime.answer}` | |
| : 'Отвечает на последний вопрос пользователя по knowledge-файлу этой ноды или RAG-заглушке.'} | |
| </div> | |
| {isUploading ? <div className="node-note">Загружаю и извлекаю текст...</div> : null} | |
| {uploadError ? <div className="node-error">{uploadError}</div> : null} | |
| {runtime.error ? <div className="node-error">{runtime.error}</div> : null} | |
| </div> | |
| </NodeShell> | |
| ); | |
| } | |
| export default memo(KnowledgeAnswerFlowNode); | |