diff --git a/backend/package/yuxi/agents/models.py b/backend/package/yuxi/agents/models.py index be86bc4e..dcb82657 100644 --- a/backend/package/yuxi/agents/models.py +++ b/backend/package/yuxi/agents/models.py @@ -6,6 +6,10 @@ from yuxi.utils import get_docker_safe_url from yuxi.utils.logging_config import logger +def _requires_non_streaming_tool_calls(provider_id: str, model_id: str) -> bool: + return provider_id.startswith("siliconflow") + + def load_chat_model(fully_specified_name: str, **kwargs) -> BaseChatModel: if not fully_specified_name: raise ValueError("model spec 不能为空") @@ -47,10 +51,14 @@ def load_chat_model(fully_specified_name: str, **kwargs) -> BaseChatModel: from langchain_openai import ChatOpenAI + openai_kwargs = dict(kwargs) + if _requires_non_streaming_tool_calls(info.provider_id, info.model_id): + openai_kwargs.setdefault("disable_streaming", "tool_calling") + return ChatOpenAI( model=info.model_id, api_key=SecretStr(api_key), base_url=base_url, stream_usage=True, - **kwargs, + **openai_kwargs, ) diff --git a/backend/package/yuxi/models/providers/builtin.py b/backend/package/yuxi/models/providers/builtin.py index a7cbab45..5a85da51 100644 --- a/backend/package/yuxi/models/providers/builtin.py +++ b/backend/package/yuxi/models/providers/builtin.py @@ -24,12 +24,6 @@ BUILTIN_PROVIDERS: list[dict[str, Any]] = [ # "api_key_env": "GEMINI_API_KEY", # "models_endpoint": "https://generativelanguage.googleapis.com/v1beta/models", # }, - # { - # "provider_id": "lmstudio", - # "display_name": "LM Studio", - # "base_url": "http://localhost:1234/v1", - # "models_endpoint": "http://localhost:1234/v1/models", - # }, { "provider_id": "deepseek", "display_name": "DeepSeek", diff --git a/backend/package/yuxi/models/providers/cache.py b/backend/package/yuxi/models/providers/cache.py index 8bb4fce7..c5e34431 100644 --- a/backend/package/yuxi/models/providers/cache.py +++ b/backend/package/yuxi/models/providers/cache.py @@ -33,7 +33,7 @@ class ModelInfo: # 运行时配置 api_key: str base_url: str - provider_type: str # openai / anthropic / gemini / openrouter / lmstudio + provider_type: str # openai / anthropic / gemini / openrouter # 可选配置 headers: dict[str, str] = field(default_factory=dict) diff --git a/backend/package/yuxi/models/providers/service.py b/backend/package/yuxi/models/providers/service.py index a4b6ee0e..7e4585b3 100644 --- a/backend/package/yuxi/models/providers/service.py +++ b/backend/package/yuxi/models/providers/service.py @@ -20,7 +20,7 @@ from yuxi.storage.postgres.models_business import ModelProvider VALID_MODEL_TYPES = {"chat", "embedding", "rerank"} VALID_MODEL_SOURCES = {"manual", "remote"} -VALID_PROVIDER_TYPES = {"openai", "anthropic", "gemini", "openrouter", "lmstudio"} +VALID_PROVIDER_TYPES = {"openai", "anthropic", "gemini", "openrouter"} _PROVIDER_ID_RE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_-]{1,99}$") diff --git a/backend/package/yuxi/services/chat_service.py b/backend/package/yuxi/services/chat_service.py index 4ccf88e2..d53548df 100644 --- a/backend/package/yuxi/services/chat_service.py +++ b/backend/package/yuxi/services/chat_service.py @@ -231,7 +231,7 @@ def _message_chunk_yuxi_events( "type": "tool_call_delta", "message_id": message_id, "tool_call_id": tool_call_chunk.get("id"), - "name": tool_call_chunk.get("name"), + "name": tool_call_chunk.get("name") or None, "args_delta": args_delta, "index": tool_call_chunk.get("index") if tool_call_chunk.get("index") is not None else 0, **route, @@ -377,7 +377,7 @@ async def _save_ai_message( for tc in tool_calls_data: await conv_repo.add_tool_call( message_id=ai_msg.id, - tool_name=tc.get("name", "unknown"), + tool_name=tc.get("name") or "unknown", tool_input=tc.get("args", {}), status="pending", langgraph_tool_call_id=tc.get("id"), diff --git a/docs/develop-guides/changelog.md b/docs/develop-guides/changelog.md index 6483aad2..915a2a81 100644 --- a/docs/develop-guides/changelog.md +++ b/docs/develop-guides/changelog.md @@ -57,6 +57,7 @@ - 优化智能体对话页细节:状态面板隐藏空 section,待办名称限制为 20 个中文汉字以内,模型选择器展示供应商名称,并收紧附件状态标签与文件编辑浮动操作样式。 - 标准化 Agent run/SSE 执行链路:run 创建时持久化输入消息并提交后入队,worker 统一写入 Redis Stream envelope,SSE 输出 `event/data/id`、心跳注释、`Last-Event-ID` 回放和终止 `end` 事件;前端强制使用 run API 并支持 ask_user_question 中断后以 resume run 恢复;事件 envelope 构造收敛到统一 helper,前端优先使用 envelope 一级 `thread_id` 路由。 - Agent run SSE 新增 `verbose=false` 精简模式:默认仍返回完整事件载荷;精简模式仅在 SSE 输出前重建最小 payload,跳过 `metadata` 和空 `yuxi.agent_state`,将同一 data 内的 `request_id` 外提为单个字段,移除 chunk 中重复的 `meta`、`metadata`、`thread_id`、`response`、空 `namespace` 和图片 base64 等调试字段,保留消息增量、工具调用、工具结果、非空 Agent state、终止状态和 SSE 游标,前端订阅默认使用精简模式。 +- 修复 SiliconFlow MiniMax 工具调用流式兼容:MiniMax 在 OpenAI 兼容流中会在参数增量 chunk 返回空 `function.name`,LangGraph v3 event stream 会将空名称写入最终工具调用并导致工具执行失败;该模型默认对工具调用禁用流式模型响应,保留 LangGraph v3 运行事件并避免 agent-chat 中出现空工具名。 - 收敛后端模块边界:文档解析从 `plugins.parser` 移动到 `knowledge.parser`,内容审查从 `plugins.guard` 移动到 `services.guard`。 - 收敛文件服务边界:文件预览判断抽为独立服务,Viewer 文件系统的 workspace 分支复用用户 workspace 服务,线程运行时上下文解析从泛化 `filesystem_service` 拆出为 agent runtime helper。 - 升级 DeepAgents 到 0.6.7 并适配新版文件系统协议:SubAgentMiddleware 改为显式 subagent spec,Skills prompt 补齐新版占位符;sandbox/skills backend 复用新版 `ReadResult`、`GlobResult`、`GrepResult` 等协议类型,文件权限在 backend 层明确区分 skills、uploads、outputs 与 workspace,保留最小 `CustomCompositeBackend` 以避免非 route glob 误扫其他 route;Agent 上下文压缩改为复用 DeepAgents SummarizationMiddleware,历史摘要与大工具结果统一 offload 到 outputs。 diff --git a/docs/develop-guides/roadmap.md b/docs/develop-guides/roadmap.md index a4d8f7c8..698ed926 100644 --- a/docs/develop-guides/roadmap.md +++ b/docs/develop-guides/roadmap.md @@ -17,29 +17,33 @@ **智能体** -- [ ] 子智能体缺少异步的机制 -- [ ] 子智能体缺少 steer 机制 -- [ ] 子智能体的双向通信,缺少 ask_for_main_agent 的机制 +- [ ] 子智能体缺少异步的机制 +- [ ] 子智能体缺少 steer 机制 +- [ ] 子智能体的双向通信,缺少 ask_for_main_agent 的机制 - [ ] 子智能体与子智能体的通信机制 - [ ] 如何停掉一个子智能体、查看智能体的进度 - [ ] RAG 评估支持 Agent 模式 - [ ] 添加 Agent 独立调用接口,方便后续评估使用 +- [ ] 任务队列 + +**并发与分布式** +- [ ] 提高 FastAPI 的并发能力 **其他** - [ ] 历史对话新增分组(或者叫做项目) - [ ] 集成 Memory,基于 deepagents 的文件后端实现,需要考虑定位 +- [ ] 模型供应商类型继续补齐非 OpenAI 兼容适配,并清理不再支持的 provider type 字样 **仅设想** - [ ] Yuxi-cli 相关的功能,放在后续版本中实现(不是类似于编程助手,而是管理平台的工,等各个 router 接口优化之后) - - - +- [ ] Yuxi-cli 支持 `yuxi upload `,将本地文件夹上传到系统中 ### Bugs - [ ] 目前的知识库的图片存在公开访问风险 - [ ] 点开对话的时候要能够自动定位到尾部,而不是最开始。 - [ ] HTML 渲染侧边模式底部有部分无法渲染,全屏情况下下面有白边 +- [ ] Summary 无法正常触发 --- diff --git a/web/src/components/model-management/ModelProviderManagePanel.vue b/web/src/components/model-management/ModelProviderManagePanel.vue index 8924d2c4..fc1656c1 100644 --- a/web/src/components/model-management/ModelProviderManagePanel.vue +++ b/web/src/components/model-management/ModelProviderManagePanel.vue @@ -741,10 +741,9 @@ defineExpose({ Provider Type openai - anthropic + diff --git a/web/src/utils/modelIcon.js b/web/src/utils/modelIcon.js index 47830647..5d3d3c5c 100644 --- a/web/src/utils/modelIcon.js +++ b/web/src/utils/modelIcon.js @@ -10,7 +10,6 @@ export const modelIcons = { dashscope: `${ICON_BASE}/bailian-color.svg`, deepseek: `${ICON_BASE}/deepseek-color.svg`, google: `${ICON_BASE}/google-color.svg`, - lmstudio: `${ICON_BASE}/lmstudio.svg`, minimax: `${ICON_BASE}/minimax-color.svg`, 'minimax-cn': `${ICON_BASE}/minimax-color.svg`, modelscope: `${ICON_BASE}/modelscope-color.svg`,