From c3af128b81c29a0d02c7630013838c0491599aa8 Mon Sep 17 00:00:00 2001 From: renjianbo <263303411@qq.com> Date: Mon, 27 Jul 2026 00:43:17 +0800 Subject: [PATCH] fix: moonshot temperature=1, 20MB upload limit, file input FileList fix, rate limiter off - workflow_integration: force temperature=1.0 for moonshot provider (Kimi K3 reasoning models reject other values), both agent node and orchestrator node - config: LOCAL_FILE_WRITE_MAX_BYTES 2MB -> 20MB (image uploads) - main: temporarily disable RateLimiterMiddleware - useFileUpload: copy FileList before resetting input (Chromium clears reference) - AgentChat/AgentChatPreview: attach button via ChatInputArea ref --- backend/app/agent_runtime/workflow_integration.py | 13 +++++++++++-- backend/app/core/config.py | 2 +- backend/app/main.py | 3 ++- frontend/src/components/AgentChatPreview.vue | 5 ++++- frontend/src/composables/useFileUpload.ts | 10 ++++++---- frontend/src/views/AgentChat.vue | 3 ++- 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/backend/app/agent_runtime/workflow_integration.py b/backend/app/agent_runtime/workflow_integration.py index f2e24a9..4464a5d 100644 --- a/backend/app/agent_runtime/workflow_integration.py +++ b/backend/app/agent_runtime/workflow_integration.py @@ -64,10 +64,15 @@ async def run_agent_node( formatted_prompt = raw_prompt # 3. 构建 Agent 配置 + _provider = node_data.get("provider", "openai") + temperature = float(node_data.get("temperature", 0.7)) + # Kimi K3 等推理模型强制要求 temperature=1 + if _provider == "moonshot": + temperature = 1.0 llm_config = AgentLLMConfig( - provider=node_data.get("provider", "openai"), + provider=_provider, model=node_data.get("model", "gpt-4o-mini"), - temperature=float(node_data.get("temperature", 0.7)), + temperature=temperature, max_iterations=int(node_data.get("max_iterations", 10)), ) # 允许节点内联 api_key/base_url @@ -244,6 +249,10 @@ async def run_orchestrator_node( tools_whitelist = cfg.get("tools", tools_whitelist) break + # Kimi K3 等推理模型强制要求 temperature=1 + if provider == "moonshot": + temperature = 1.0 + agent_configs.append(OrchestratorAgentConfig( id=agent.id, name=agent.name or "Agent", diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 13b50ec..f12d574 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -34,7 +34,7 @@ class Settings(BaseSettings): # 本地文件工具(file_read / file_write):允许读写的根目录。空字符串表示使用「backend 的上一级目录」作为仓库根。 LOCAL_FILE_TOOLS_ROOT: str = "" LOCAL_FILE_READ_MAX_BYTES: int = 2_097_152 # 单次读取上限(默认 2MB) - LOCAL_FILE_WRITE_MAX_BYTES: int = 2_097_152 # 单次写入内容上限(UTF-8 字节) + LOCAL_FILE_WRITE_MAX_BYTES: int = 20_971_520 # 单次写入内容上限 20MB(含图片上传) # http_request 工具:写入 LLM 上下文的响应体最大字符数(HTML/JSON 过大时截断,避免超过模型 context) HTTP_REQUEST_MAX_BODY_CHARS: int = 32_000 diff --git a/backend/app/main.py b/backend/app/main.py index 1b98f32..cd3f171 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -191,7 +191,8 @@ app.add_middleware( ) # API 限流中间件(在 CORS 之后、日志之前) -app.add_middleware(RateLimiterMiddleware) +# TODO: 暂时关闭限流,需要时恢复 +# app.add_middleware(RateLimiterMiddleware) # 安全响应头中间件(HSTS + X-Frame-Options + X-Content-Type-Options 等) app.add_middleware(SecurityHeadersMiddleware) diff --git a/frontend/src/components/AgentChatPreview.vue b/frontend/src/components/AgentChatPreview.vue index 7577822..3b951ab 100644 --- a/frontend/src/components/AgentChatPreview.vue +++ b/frontend/src/components/AgentChatPreview.vue @@ -142,6 +142,7 @@ | null>(null) +function handleAttachFile() { inputAreaRef.value?.fileInputEl?.click() } const { stripMarkdownForTTS } = useMarkdown() const { isSpeaking, speak: ttsSpeak, stop: ttsStop } = useTTS() diff --git a/frontend/src/composables/useFileUpload.ts b/frontend/src/composables/useFileUpload.ts index a529fea..bb7eb93 100644 --- a/frontend/src/composables/useFileUpload.ts +++ b/frontend/src/composables/useFileUpload.ts @@ -155,10 +155,12 @@ export function useFileUpload() { async function onFilesSelected(e: Event): Promise { const input = e.target as HTMLInputElement - const files = input.files - input.value = '' // Reset so the same file can be re-selected - if (files?.length) return uploadFiles(Array.from(files)) - return [] + const rawFiles = input.files + if (!rawFiles?.length) return [] + // 必须先复制再重置 input,否则 Chromium 系清空 FileList 引用 + const fileArr = Array.from(rawFiles) + input.value = '' + return uploadFiles(fileArr) } function handleAttachFile() { diff --git a/frontend/src/views/AgentChat.vue b/frontend/src/views/AgentChat.vue index ec589e5..d266769 100644 --- a/frontend/src/views/AgentChat.vue +++ b/frontend/src/views/AgentChat.vue @@ -486,7 +486,8 @@ import { useTTS } from '@/composables/useTTS' import { useSpeechRecognition } from '@/composables/useSpeechRecognition' const { renderMarkdown, stripMarkdownForTTS } = useMarkdown() -const { pendingAttachments, inputDragOver, uploadFiles, removeAttachment, clearAttachments, buildAttachmentContext, onFilesSelected, handleAttachFile } = useFileUpload() +const { pendingAttachments, inputDragOver, uploadFiles, removeAttachment, clearAttachments, buildAttachmentContext, onFilesSelected } = useFileUpload() +function handleAttachFile() { inputAreaRef.value?.fileInputEl?.click() } const { isSpeaking, speak: ttsSpeak, stop: ttsStop } = useTTS() const { isListening, transcript, error: voiceError, supported: voiceSupported, start: voiceStart, stop: voiceStop } = useSpeechRecognition() const modelConfigStore = useModelConfigStore()