diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index d6c71f37..e9b539df 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -194,6 +194,7 @@ def delete_app( def copy_app( app_id: uuid.UUID, new_name: Optional[str] = None, + payload: app_schema.CopyAppRequest = None, db: Session = Depends(get_db), current_user=Depends(get_current_user), ): @@ -205,6 +206,8 @@ def copy_app( - 不影响原应用 """ workspace_id = current_user.current_workspace_id + # body takes precedence over query param for backward compatibility + new_name = (payload.new_name if payload else None) or new_name logger.info( "用户请求复制应用", extra={ @@ -517,7 +520,7 @@ async def draft_run( # 提前验证和准备(在流式响应开始前完成) from app.services.app_service import AppService from app.services.multi_agent_service import MultiAgentService - from app.models import AgentConfig, ModelConfig + from app.models import AgentConfig, ModelConfig, AppRelease from sqlalchemy import select from app.core.exceptions import BusinessException from app.services.draft_run_service import AgentRunService @@ -556,18 +559,29 @@ async def draft_run( service._check_agent_config(app_id) # 2. 获取 Agent 配置 - stmt = select(AgentConfig).where(AgentConfig.app_id == app_id) - agent_cfg = db.scalars(stmt).first() - if not agent_cfg: - raise BusinessException("Agent 配置不存在", BizCode.AGENT_CONFIG_MISSING) + # 共享应用:从最新发布版本读配置快照,而非草稿 + is_shared = app.workspace_id != workspace_id + if is_shared: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING) + release = db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING) + agent_cfg = service._agent_config_from_release(release) + model_config = db.get(ModelConfig, release.default_model_config_id) if release.default_model_config_id else None + else: + stmt = select(AgentConfig).where(AgentConfig.app_id == app_id) + agent_cfg = db.scalars(stmt).first() + if not agent_cfg: + raise BusinessException("Agent 配置不存在", BizCode.AGENT_CONFIG_MISSING) - # 3. 获取模型配置 - model_config = None - if agent_cfg.default_model_config_id: - model_config = db.get(ModelConfig, agent_cfg.default_model_config_id) - if not model_config: - from app.core.exceptions import ResourceNotFoundException - raise ResourceNotFoundException("模型配置", str(agent_cfg.default_model_config_id)) + # 3. 获取模型配置 + model_config = None + if agent_cfg.default_model_config_id: + model_config = db.get(ModelConfig, agent_cfg.default_model_config_id) + if not model_config: + from app.core.exceptions import ResourceNotFoundException + raise ResourceNotFoundException("模型配置", str(agent_cfg.default_model_config_id)) # 流式返回 if payload.stream: @@ -723,7 +737,17 @@ async def draft_run( msg="多 Agent 任务执行成功" ) elif app.type == AppType.WORKFLOW: # 工作流 - config = workflow_service.check_config(app_id) + # 共享应用:从最新发布版本读配置快照,而非草稿 + is_shared = app.workspace_id != workspace_id + if is_shared: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING) + release = db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING) + config = service._workflow_config_from_release(release) + else: + config = workflow_service.check_config(app_id) # 3. 流式返回 if payload.stream: logger.debug( diff --git a/api/app/schemas/app_schema.py b/api/app/schemas/app_schema.py index b63c73f5..8bfedd5a 100644 --- a/api/app/schemas/app_schema.py +++ b/api/app/schemas/app_schema.py @@ -525,6 +525,13 @@ class AppRelease(BaseModel): return int(dt.timestamp() * 1000) if dt else None +# ---------- App Copy Schema ---------- + +class CopyAppRequest(BaseModel): + """复制应用请求""" + new_name: Optional[str] = Field(None, description="新应用名称,不填则使用原名称-副本") + + # ---------- App Share Schemas ---------- class AppShareCreate(BaseModel): diff --git a/api/app/services/app_chat_service.py b/api/app/services/app_chat_service.py index cd9d3e81..58beea53 100644 --- a/api/app/services/app_chat_service.py +++ b/api/app/services/app_chat_service.py @@ -24,6 +24,7 @@ from app.services.model_service import ModelApiKeyService from app.services.multi_agent_orchestrator import MultiAgentOrchestrator from app.services.multimodal_service import MultimodalService from app.services.workflow_service import WorkflowService +from app.schemas import FileType logger = get_business_logger() @@ -156,20 +157,6 @@ class AppChatService: files=processed_files # 传递处理后的文件 ) - # 保存消息 - message_id = self.conversation_service.save_conversation_messages( - conversation_id=conversation_id, - user_message=message, - assistant_message=result["content"], - meta_data={ - "usage": result.get("usage", { - "prompt_tokens": 0, - "completion_tokens": 0, - "total_tokens": 0 - }) - } - ) - ModelApiKeyService.record_api_key_usage(self.db, api_key_obj.id) elapsed_time = time.time() - start_time @@ -191,6 +178,40 @@ class AppChatService: tenant_id=tenant_id, workspace_id=workspace_id ) + # 构建用户消息内容(含多模态文件) + human_meta = { + "files": [] + } + assistant_meta = { + "model": api_key_obj.model_name, + "usage": result.get("usage", {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0}), + "audio_url": None + } + if files: + for f in files: + # url = await MultimodalService(self.db).get_file_url(f) + human_meta["files"].append({ + "type": FileType.IMAGE, + "url": f.url + }) + + # 保存消息 + if audio_url: + assistant_meta["audio_url"] = audio_url + self.conversation_service.add_message( + conversation_id=conversation_id, + role="user", + content=message, + meta_data=human_meta + ) + ai_message = self.conversation_service.add_message( + conversation_id=conversation_id, + role="assistant", + content=result["content"], + meta_data=assistant_meta + ) + message_id = ai_message.id + return { "conversation_id": conversation_id, "message_id": str(message_id), @@ -344,24 +365,6 @@ class AppChatService: elapsed_time = time.time() - start_time - # 保存消息 - self.conversation_service.add_message( - conversation_id=conversation_id, - role="user", - content=message - ) - - self.conversation_service.add_message( - message_id=message_id, - conversation_id=conversation_id, - role="assistant", - content=full_content, - meta_data={ - "model": api_key_obj.model_name, - "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": total_tokens} - } - ) - ModelApiKeyService.record_api_key_usage(self.db, api_key_obj.id) # 发送结束事件(包含 suggested_questions、tts、citations) @@ -373,13 +376,48 @@ class AppChatService: {"model_name": api_key_obj.model_name, "api_key": api_key_obj.api_key, "api_base": api_key_obj.api_base}, {} ) - end_data["audio_url"] = await self.agent_service._generate_tts( + stream_audio_url = await self.agent_service._generate_tts( features_config, full_content, {"model_name": api_key_obj.model_name, "api_key": api_key_obj.api_key, "api_base": api_key_obj.api_base, "provider": api_key_obj.provider}, tenant_id=tenant_id, workspace_id=workspace_id ) + end_data["audio_url"] = stream_audio_url end_data["citations"] = self.agent_service._filter_citations(features_config, []) + + # 保存消息 + human_meta = { + "files":[] + } + assistant_meta = { + "model": api_key_obj.model_name, + "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": total_tokens}, + "audio_url": None + } + + if files: + for f in files: + # url = await MultimodalService(self.db).get_file_url(f) + human_meta["files"].append({ + "type": FileType.IMAGE, + "url": f.url + }) + + if stream_audio_url: + assistant_meta["audio_url"] = stream_audio_url + self.conversation_service.add_message( + conversation_id=conversation_id, + role="user", + content=message, + meta_data=human_meta + ) + self.conversation_service.add_message( + message_id=message_id, + conversation_id=conversation_id, + role="assistant", + content=full_content, + meta_data=assistant_meta + ) yield f"event: end\ndata: {json.dumps(end_data, ensure_ascii=False)}\n\n" logger.info( diff --git a/api/app/services/app_service.py b/api/app/services/app_service.py index 98fdf6c9..68d255f8 100644 --- a/api/app/services/app_service.py +++ b/api/app/services/app_service.py @@ -1426,6 +1426,50 @@ class AppService: logger.info("Agent 配置更新成功", extra={"app_id": str(app_id)}) return agent_cfg + def _agent_config_from_release(self, release: "AppRelease") -> "AgentConfig": + """从发布版本快照重建 AgentConfig 对象(不入库,仅用于运行)""" + cfg = release.config or {} + now = release.created_at or datetime.datetime.now() + agent_cfg = AgentConfig( + id=uuid.uuid4(), + app_id=release.app_id, + system_prompt=cfg.get("system_prompt", ""), + default_model_config_id=release.default_model_config_id, + model_parameters=cfg.get("model_parameters"), + knowledge_retrieval=cfg.get("knowledge_retrieval"), + memory=cfg.get("memory", {}), + variables=cfg.get("variables", []), + tools=cfg.get("tools", []), + skills=cfg.get("skills", {}), + features=cfg.get("features", {}), + is_active=True, + created_at=now, + updated_at=now, + ) + return agent_cfg + + def _workflow_config_from_release(self, release: "AppRelease") -> "WorkflowConfig": + """从发布版本快照重建 WorkflowConfig 对象(不入库,仅用于运行)""" + cfg = release.config or {} + now = release.created_at or datetime.datetime.now() + from app.models.workflow_model import WorkflowConfig as WorkflowConfigModel + # 查出源应用真实的 WorkflowConfig id,供 workflow_executions 外键使用 + real_config = WorkflowConfigRepository(self.db).get_by_app_id(release.app_id) + real_id = real_config.id if real_config else uuid.uuid4() + wf_cfg = WorkflowConfigModel( + id=real_id, + app_id=release.app_id, + nodes=cfg.get("nodes", []), + edges=cfg.get("edges", []), + variables=cfg.get("variables", []), + execution_config=cfg.get("execution_config", {}), + triggers=cfg.get("triggers", []), + is_active=True, + created_at=now, + updated_at=now, + ) + return wf_cfg + def get_agent_config( self, *, @@ -1457,6 +1501,15 @@ class AppService: # 只读操作,允许访问共享应用 self._validate_app_accessible(app, workspace_id) + # 共享应用:返回最新发布版本的配置快照,而非草稿 + if workspace_id and app.workspace_id != workspace_id: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING) + release = self.db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING) + return self._agent_config_from_release(release) + stmt = select(AgentConfig).where( AgentConfig.app_id == app_id, AgentConfig.is_active.is_(True) @@ -1555,6 +1608,16 @@ class AppService: # 只读操作,允许访问共享应用 self._validate_app_accessible(app, workspace_id) + + # 共享应用:返回最新发布版本的配置快照,而非草稿 + if workspace_id and app.workspace_id != workspace_id: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.CONFIG_MISSING) + release = self.db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.CONFIG_MISSING) + return self._workflow_config_from_release(release) + repo = WorkflowConfigRepository(self.db) config = repo.get_by_app_id(app_id) if config: diff --git a/api/app/services/draft_run_service.py b/api/app/services/draft_run_service.py index 92b13bfc..5b8bbfa7 100644 --- a/api/app/services/draft_run_service.py +++ b/api/app/services/draft_run_service.py @@ -37,6 +37,7 @@ from app.services.model_parameter_merger import ModelParameterMerger from app.services.model_service import ModelApiKeyService from app.services.multimodal_service import MultimodalService from app.services.tool_service import ToolService +from app.schemas import FileType logger = get_business_logger() @@ -636,7 +637,13 @@ class AgentRunService: ModelApiKeyService.record_api_key_usage(self.db, api_key_config.get("api_key_id")) - # 9. 保存会话消息 + # 9. 生成 TTS audio_url(在保存消息前生成,以便一并存入 meta_data) + audio_url = await self._generate_tts( + features_config, result["content"], api_key_config, + tenant_id=tenant_id, workspace_id=workspace_id + ) if not sub_agent else None + + # 10. 保存会话消息 if not sub_agent: await self._save_conversation_message( conversation_id=conversation_id, @@ -650,7 +657,9 @@ class AgentRunService: "completion_tokens": 0, "total_tokens": 0 }) - } + }, + files=files, + audio_url=audio_url ) response = { @@ -666,10 +675,7 @@ class AgentRunService: features_config, result["content"], api_key_config, effective_params ) if not sub_agent else [], "citations": self._filter_citations(features_config, result.get("citations", [])), - "audio_url": await self._generate_tts( - features_config, result["content"], api_key_config, - tenant_id=tenant_id, workspace_id=workspace_id - ) if not sub_agent else None, + "audio_url": audio_url, } logger.info( @@ -878,7 +884,13 @@ class AgentRunService: "total_tokens": total_tokens }) - # 10. 保存会话消息 + # 10. 生成 audio_url(在保存消息前生成,以便一并存入 meta_data) + stream_audio_url = await self._generate_tts( + features_config, full_content, api_key_config, + tenant_id=tenant_id, workspace_id=workspace_id + ) if not sub_agent else None + + # 11. 保存会话消息 if not sub_agent: await self._save_conversation_message( conversation_id=conversation_id, @@ -888,10 +900,12 @@ class AgentRunService: user_id=user_id, meta_data={ "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": total_tokens} - } + }, + files=files, + audio_url=stream_audio_url ) - # 11. 发送结束事件(包含 suggested_questions 和 tts) + # 12. 发送结束事件(包含 suggested_questions 和 tts) end_data: Dict[str, Any] = { "conversation_id": conversation_id, "elapsed_time": elapsed_time, @@ -901,10 +915,7 @@ class AgentRunService: end_data["suggested_questions"] = await self._generate_suggested_questions( features_config, full_content, api_key_config, effective_params ) - end_data["audio_url"] = await self._generate_tts( - features_config, full_content, api_key_config, - tenant_id=tenant_id, workspace_id=workspace_id - ) + end_data["audio_url"] = stream_audio_url end_data["citations"] = self._filter_citations(features_config, []) yield self._format_sse_event("end", end_data) @@ -1143,7 +1154,9 @@ class AgentRunService: assistant_message: str, meta_data: dict, app_id: Optional[uuid.UUID] = None, - user_id: Optional[str] = None + user_id: Optional[str] = None, + files: Optional[List[FileInput]] = None, + audio_url: Optional[str] = None ) -> None: """保存会话消息(会话已通过 _ensure_conversation 确保存在) @@ -1162,13 +1175,26 @@ class AgentRunService: conv_uuid = uuid.UUID(conversation_id) # 保存消息(会话已经存在) + human_meta = { + "files": [] + } + if files: + for f in files: + # url = await MultimodalService(self.db).get_file_url(f) + human_meta["files"].append({ + "type": FileType.IMAGE, + "url": f.url + }) # 保存用户消息 conversation_service.add_message( conversation_id=conv_uuid, role="user", - content=user_message + content=user_message, + meta_data=human_meta ) - # 保存助手消息 + # 保存助手消息(含 audio_url) + if audio_url: + meta_data["audio_url"] = audio_url conversation_service.add_message( conversation_id=conv_uuid, role="assistant", diff --git a/api/app/services/multimodal_service.py b/api/app/services/multimodal_service.py index 1f0e1cc2..f0c7cee2 100644 --- a/api/app/services/multimodal_service.py +++ b/api/app/services/multimodal_service.py @@ -41,7 +41,8 @@ TEXT_MIME = ['text/plain', 'text/x-markdown'] PDF_MIME = ['application/pdf'] DOC_MIME = [ 'application/msword', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/zip' ] XLSX_MIME = [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', @@ -590,7 +591,7 @@ class MultimodalService: return file_content.decode("utf-8") elif file_mime_type in PDF_MIME: return await self._extract_pdf_text(file_content) - elif file_mime_type in DOC_MIME: + elif file_mime_type in DOC_MIME and file.file_type.endswith(('docx', 'doc')): return await self._extract_word_text(file_content) elif file_mime_type in XLSX_MIME and file.file_type.endswith(("xlsx", "xls")): return await self._extract_xlsx_text(file_content) diff --git a/web/package.json b/web/package.json index b6840854..db6a8408 100644 --- a/web/package.json +++ b/web/package.json @@ -46,7 +46,7 @@ "lexical": "^0.39.0", "mammoth": "^1.12.0", "mermaid": "^11.12.1", - "pdfjs-dist": "^4.4.168", + "pdfjs-dist": "4.10.38", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^15.0.0", diff --git a/web/src/api/application.ts b/web/src/api/application.ts index 6035afe2..5225115e 100644 --- a/web/src/api/application.ts +++ b/web/src/api/application.ts @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 13:59:45 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-13 17:07:54 + * @Last Modified time: 2026-03-18 20:01:29 */ import { request } from '@/utils/request' import type { ApplicationModalData } from '@/views/ApplicationManagement/types' @@ -137,7 +137,7 @@ export const getExperienceConfig = (share_token: string) => { }) } // Export application -export const appExport = (app_id: string, appName: string, data?: { release_version: string }) => { +export const appExport = (app_id: string, appName: string, data?: { release_id: string }) => { return request.getDownloadFile(`/apps/${app_id}/export`, `${appName}.yml`, data) } // Import application diff --git a/web/src/components/Chat/ChatContent.tsx b/web/src/components/Chat/ChatContent.tsx index 15dcd496..2f2552db 100644 --- a/web/src/components/Chat/ChatContent.tsx +++ b/web/src/components/Chat/ChatContent.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2025-12-10 16:46:17 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-17 14:11:24 + * @Last Modified time: 2026-03-18 20:48:03 */ import { type FC, useRef, useEffect, useState } from 'react' import clsx from 'clsx' @@ -33,7 +33,7 @@ const ChatContent: FC = ({ const audioRef = useRef(null) const [playingIndex, setPlayingIndex] = useState(null) - const handlePlay = (index: number, audioUrl: string) => { + const handlePlay = (index: number, audio_url: string) => { if (playingIndex === index) { audioRef.current?.pause() setPlayingIndex(null) @@ -42,7 +42,7 @@ const ChatContent: FC = ({ if (audioRef.current) { audioRef.current.pause() } - const audio = new Audio(audioUrl) + const audio = new Audio(audio_url) audioRef.current = audio audio.play() setPlayingIndex(index) @@ -108,6 +108,48 @@ const ChatContent: FC = ({ {labelFormat(item)} } + {item.meta_data?.files && item.meta_data?.files.length > 0 &&
+ {item.meta_data?.files?.map((file) => { + if (file.type.includes('image')) { + return ( +
+ {file.name} +
+ ) + } + if (file.type.includes('video')) { + return ( +
+
+ ) + } + if (file.type.includes('audio')) { + return ( +
+
+ ) + } + return ( +
+ {(file.type.includes('doc') || file.type.includes('docx') || file.type.includes('word') || file.type.includes('wordprocessingml.document')) &&
} + {(file.type.includes('pdf')) &&
} + {(file.type.includes('excel') || file.type.includes('spreadsheetml.sheet') || file.type.includes('csv')) &&
} +
+
{file.name}
+
{file.type} · {file.size}
+
+
+ ) + })} +
} {/* Message bubble */}
= ({ {/* Render message content using Markdown component */} - {item.audioUrl && <> + {item.meta_data?.audio_url && <> {playingIndex !== index - ? handlePlay(index, item.audioUrl!)} /> + ? handlePlay(index, item.meta_data?.audio_url!)} /> :
handlePlay(index, item.audioUrl!)} + onClick={() => handlePlay(index, item.meta_data?.audio_url!)} /> } diff --git a/web/src/components/Chat/types.ts b/web/src/components/Chat/types.ts index 9fb77ed7..a1d0a7e1 100644 --- a/web/src/components/Chat/types.ts +++ b/web/src/components/Chat/types.ts @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2025-12-10 16:45:54 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-17 13:46:24 + * @Last Modified time: 2026-03-18 20:47:42 */ import { type ReactNode } from 'react' @@ -22,9 +22,11 @@ export interface ChatItem { created_at?: number | string; status?: string; subContent?: Record[]; - files?: any[]; error?: string; - audioUrl?: string; + meta_data?: { + audio_url?: string; + files?: any[]; + }, } /** diff --git a/web/src/components/DocumentPreview/index.tsx b/web/src/components/DocumentPreview/index.tsx index 02345d13..247f713e 100644 --- a/web/src/components/DocumentPreview/index.tsx +++ b/web/src/components/DocumentPreview/index.tsx @@ -4,7 +4,7 @@ * @Author: yujiangping * @Date: 2026-03-16 19:01:12 * @LastEditors: yujiangping - * @LastEditTime: 2026-03-17 16:19:45 + * @LastEditTime: 2026-03-18 18:35:53 */ import { useState, useEffect, useRef, useCallback, type FC } from 'react'; import { Spin, Alert, Button, Table, InputNumber, Image } from 'antd'; @@ -21,10 +21,9 @@ import { cookieUtils } from '@/utils/request'; import mammoth from 'mammoth'; import * as XLSX from 'xlsx'; import * as pdfjsLib from 'pdfjs-dist'; -import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs?url'; -// 设置 pdf.js worker -pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker; +// 设置 pdf.js worker - 使用 CDN 避免 Vite 打包动态 import 问题 +pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.10.38/pdf.worker.min.mjs'; interface DocumentPreviewProps { fileUrl: string; @@ -287,7 +286,20 @@ const DocumentPreview: FC = ({ setError(false); setErrorMessage(''); try { + // .doc 旧格式 mammoth 不支持,使用 Office Online Viewer + if (getFileExtension() === '.doc') { + setHtmlContent(''); + setLoading(false); + return; + } const arrayBuffer = await fetchFileBuffer(fileUrl); + // 校验是否为有效的 docx(ZIP 格式,前两字节为 PK) + const header = new Uint8Array(arrayBuffer.slice(0, 4)); + if (header[0] !== 0x50 || header[1] !== 0x4B) { + // 不是 ZIP/docx 格式,可能是 HTML 错误页或 JSON 响应 + const text = new TextDecoder().decode(arrayBuffer.slice(0, 200)); + throw new Error(`文件内容不是有效的 docx 格式: ${text.substring(0, 100)}`); + } const result = await mammoth.convertToHtml({ arrayBuffer }); setHtmlContent(result.value); setLoading(false); @@ -492,12 +504,22 @@ const DocumentPreview: FC = ({ {/* Word 预览 */} {isWordFile() && !error && !loading && ( -
-
-
+ getFileExtension() === '.doc' ? ( + /* .doc 旧格式前端无法解析,提示下载 */ +
+
+

.doc 格式暂不支持在线预览,请下载后查看

+ +
+
+ ) : ( +
+
+
+ ) )} {/* Excel 预览 */} diff --git a/web/src/views/ApplicationConfig/Cluster.tsx b/web/src/views/ApplicationConfig/Cluster.tsx index 97ae939a..f4dd5a26 100644 --- a/web/src/views/ApplicationConfig/Cluster.tsx +++ b/web/src/views/ApplicationConfig/Cluster.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 16:29:33 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-17 14:48:57 + * @Last Modified time: 2026-03-18 19:49:09 */ import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react' import { useTranslation } from 'react-i18next' @@ -30,7 +30,7 @@ import RadioGroupCard from '@/components/RadioGroupCard' import { getModelListUrl } from '@/api/models' import ModelConfigModal from './components/ModelConfigModal' import type { Application } from '@/views/ApplicationManagement/types' -import FeaturesConfig from './components/FeaturesConfig' +// import FeaturesConfig from './components/FeaturesConfig' const tagColors = ['processing', 'warning', 'default'] const MAX_LENGTH = 5; @@ -187,15 +187,15 @@ const Cluster = forwardRef { - form.setFieldValue('features', value) - } + // const handleSaveFeaturesConfig = (value: FeaturesConfigForm) => { + // form.setFieldValue('features', value) + // } return ( - + {/* */} @@ -295,6 +295,7 @@ const Cluster = forwardRef } diff --git a/web/src/views/ApplicationConfig/ReleasePage.tsx b/web/src/views/ApplicationConfig/ReleasePage.tsx index efa62578..efaedea3 100644 --- a/web/src/views/ApplicationConfig/ReleasePage.tsx +++ b/web/src/views/ApplicationConfig/ReleasePage.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 16:29:41 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-18 14:30:41 + * @Last Modified time: 2026-03-18 20:57:24 */ import { type FC, useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; @@ -71,7 +71,7 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres } const handleExport = () => { if (!selectedVersion) return - appExport(data.id, data.name, {release_version: selectedVersion.id}) + appExport(data.id, data.name, { release_id: selectedVersion.id}) } return (
@@ -132,7 +132,7 @@ const ReleasePage: FC<{data: Application; refresh: () => void}> = ({data, refres {data?.type !== 'multi_agent' && } {data.current_release_id !== selectedVersion.id && } - + {data?.type !== 'multi_agent' && } } diff --git a/web/src/views/ApplicationConfig/TestChat/index.tsx b/web/src/views/ApplicationConfig/TestChat/index.tsx index b7ce167e..ad7931e2 100644 --- a/web/src/views/ApplicationConfig/TestChat/index.tsx +++ b/web/src/views/ApplicationConfig/TestChat/index.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-03-13 17:27:52 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-03-18 20:54:35 + */ import { type FC, useState, useRef, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { App } from 'antd' @@ -116,7 +122,9 @@ const TestChat: FC = ({ role: 'user', content: message, created_at: Date.now(), - files + meta_data: { + files + }, }]) } @@ -136,7 +144,7 @@ const TestChat: FC = ({ const lastMsg = newList[newList.length - 1] if (lastMsg.role === 'assistant') { lastMsg.content += content; - lastMsg.audioUrl = audio_url + lastMsg.meta_data = {audio_url} } return newList }) @@ -428,7 +436,7 @@ const TestChat: FC = ({ status, error, content: newList[lastIndex].content === '' ? null : newList[lastIndex].content, - audioUrl: audio_url + meta_data: { audio_url: audio_url } } } return newList diff --git a/web/src/views/ApplicationConfig/components/Chat.tsx b/web/src/views/ApplicationConfig/components/Chat.tsx index b8fba5a6..ee5bc53a 100644 --- a/web/src/views/ApplicationConfig/components/Chat.tsx +++ b/web/src/views/ApplicationConfig/components/Chat.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 16:27:39 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-17 15:27:57 + * @Last Modified time: 2026-03-18 20:52:33 */ /** * Chat debugging component for application testing @@ -92,7 +92,9 @@ const Chat: FC = ({ role: 'user', content: message, created_at: Date.now(), - files + meta_data: { + files + }, }; updateChatList(prev => prev.map(item => ({ ...item, @@ -142,7 +144,7 @@ const Chat: FC = ({ { ...lastMsg, content: lastMsg.content + (content || ''), - audioUrl: audio_url + meta_data: { audio_url } } ] } diff --git a/web/src/views/ApplicationConfig/components/ConfigHeader.tsx b/web/src/views/ApplicationConfig/components/ConfigHeader.tsx index bab3fd74..e2e1cf6f 100644 --- a/web/src/views/ApplicationConfig/components/ConfigHeader.tsx +++ b/web/src/views/ApplicationConfig/components/ConfigHeader.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 16:27:52 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-18 15:40:53 + * @Last Modified time: 2026-03-18 21:25:23 */ import { type FC, useRef, useMemo, useCallback } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; @@ -183,6 +183,7 @@ const ConfigHeader: FC = ({ appRef?.current?.handleSaveFeaturesConfig?.(value) onFeaturesChange?.(value) }, [appRef, onFeaturesChange]) + return ( <>
@@ -211,9 +212,9 @@ const ConfigHeader: FC = ({ className={styles.tabs} />
- {application?.type === 'workflow' + {application?.type === 'workflow' && source !== 'sharing' ?
- + diff --git a/web/src/views/ApplicationConfig/components/FeaturesConfig/FileUploadSettingModal.tsx b/web/src/views/ApplicationConfig/components/FeaturesConfig/FileUploadSettingModal.tsx index 3579497a..eae1a5a5 100644 --- a/web/src/views/ApplicationConfig/components/FeaturesConfig/FileUploadSettingModal.tsx +++ b/web/src/views/ApplicationConfig/components/FeaturesConfig/FileUploadSettingModal.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-03-05 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-17 18:10:47 + * @Last Modified time: 2026-03-18 20:29:28 */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, InputNumber, Flex, Switch, Row, Col, Radio } from 'antd'; @@ -27,22 +27,22 @@ const fileTypeOptions = [ { type: 'document', icon:
, - formats: 'TXT, MD, MDX, MARKDOWN, PDF, DOC, DOCX', + formats: 'TXT, PDF, DOC, DOCX, XLSX, CSV, JSON', }, { type: 'image', icon:
, - formats: 'JPG, JPEG, PNG, GIF, WEBP, SVG', + formats: 'JPG, JPEG, PNG, GIF, WEBP', }, { type: 'audio', icon:
, - formats: 'MP3, M4A, WAV, AMR, MPGA', + formats: 'MP3, M4A, WAV, OGG, FLAC', }, { type: 'video', icon:
, - formats: 'MP4, MOV, MPEG, WEBM', + formats: 'MP4, MOV, AVI, WEBM', }, ]; @@ -80,7 +80,7 @@ const FileUploadSettingModal = forwardRef { setVisible(true); if (values) { - const methods = values.allowed_transfer_methods + const methods = values.allowed_transfer_methods || ['local_file', 'remote_url'] const transferMethod = Array.isArray(methods) ? methods.length === 2 ? 'both' : methods[0] : methods diff --git a/web/src/views/ApplicationManagement/index.tsx b/web/src/views/ApplicationManagement/index.tsx index c9f57268..f67d9160 100644 --- a/web/src/views/ApplicationManagement/index.tsx +++ b/web/src/views/ApplicationManagement/index.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-03 16:34:12 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-18 10:50:33 + * @Last Modified time: 2026-03-18 21:00:12 */ /** * Application Management Page @@ -143,7 +143,7 @@ const ApplicationManagement: React.FC = () => { +
{ role: 'user', content: message, created_at: Date.now(), - files + meta_data: { + files + }, }]) } @@ -185,7 +187,7 @@ const Conversation: FC = () => { { ...lastMsg, content: lastMsg.content + content, - audioUrl: audio_url + meta_data: { audio_url } } ] } diff --git a/web/src/views/Workflow/components/Chat/Chat.tsx b/web/src/views/Workflow/components/Chat/Chat.tsx index 37cb215e..ad46d2dc 100644 --- a/web/src/views/Workflow/components/Chat/Chat.tsx +++ b/web/src/views/Workflow/components/Chat/Chat.tsx @@ -2,7 +2,7 @@ * @Author: ZhaoYing * @Date: 2026-02-06 21:10:56 * @Last Modified by: ZhaoYing - * @Last Modified time: 2026-03-18 14:34:20 + * @Last Modified time: 2026-03-18 20:46:35 */ /** * Workflow Chat Component @@ -63,9 +63,12 @@ const Chat = forwardRef { setOpen(true) - if (data?.features) setFeatures(data.features) } + useEffect(() => { + if (data?.features && open) setFeatures(data.features) + }, [open, data?.features]) + useEffect(() => { if (open && graphRef.current && toolbarRef.current) { getVariables() @@ -148,10 +151,14 @@ const Chat = forwardRef [...prev, { role: 'user', content: message, created_at: Date.now(), + meta_data: { + files + }, }]) setChatList(prev => [...prev, { role: 'assistant', @@ -335,7 +342,6 @@ const Chat = forwardRef