Skip to content

Conversation

@dingyi222666
Copy link
Member

@dingyi222666 dingyi222666 commented Sep 21, 2025

此 PR 包含版本更新和重要的工具调用修复。

主要更改

  • 修复了所有适配器中工具调用的处理方式,从已弃用的 additional_kwargs.tool_calls 迁移到标准的 AIMessage.tool_calls 属性
  • 改进了核心模块和搜索服务的错误处理机制
  • 移除了已弃用的配置选项

版本更新

  • 核心包版本更新至 1.3.0-alpha.43
  • 所有适配器包版本更新至 1.3.0-alpha.5
  • 搜索服务版本更新至 1.2.9
  • 向量存储服务版本更新至 1.2.8

Bug 修复

  • 修复了工具调用在不同适配器间的不一致问题
  • 改进了错误处理和空值安全检查
  • 优化了搜索提供程序的稳定性
  • 修复了消息处理中的潜在空指针异常

其他更改

  • 清理了过时的配置选项
  • 改进了代码规范和类型安全
  • 更新了所有包的依赖关系

Close #550

Update all package versions and dependencies across the monorepo for the new release cycle.
- Migrate from deprecated additional_kwargs.tool_calls to AIMessage.tool_calls property
- Update Qwen, Spark, and Zhipu adapters to use standardized tool_calls format
- Remove unused tool_calls handling in shared utilities
- Ensure consistent tool call serialization across all adapters
- Bump core package to 1.3.0-alpha.43
- Bump all adapter packages to 1.3.0-alpha.5
- Bump search-service to 1.2.9
- Bump vector-store-service to 1.2.8
- Update peer dependencies to match new core version
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 21, 2025

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (2)
  • packages/search-service/package.json is excluded by !**/*.json
  • packages/vector-store-service/package.json is excluded by !**/*.json

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

本次变更在多个适配器的 utils 中统一了对 AIMessage 的 tool_calls 处理:仅当原始消息类型为 AI 时才提取并映射 tool_calls,并按各供应商所需格式序列化参数;同步调整了增量/分片消息的附加字段处理与导入声明。

Changes

Cohort / File(s) Summary
统一 AIMessage 导入与类型分支
packages/shared/src/utils.ts, packages/qwen-adapter/src/utils.ts, packages/spark-adapter/src/utils.ts, packages/zhipu-adapter/src/utils.ts
新增从 @langchain/core/messages 导入 AIMessage,并以 rawMessage.getType() === 'ai' 作为 tool_calls 处理入口。
OpenAI/通用消息映射收敛
packages/shared/src/utils.ts
langchainMessageToOpenAIMessage 仅在 AI 消息时填充 tool_calls;移除对非 AI 消息的通用 tool_calls 填充。convertMessageToMessageChunk 去除基于 message.tool_calls 的旧分支。
QWen 适配器工具调用映射
packages/qwen-adapter/src/utils.ts
激活 AI 消息的 tool_calls 映射(含 id、type='function'、function.name 与 JSON 字符串参数);convertDeltaToMessageChunk 不再填充 function_call/tool_calls,仅保留 reasoning_content
Spark 适配器工具调用映射
packages/spark-adapter/src/utils.ts
先构造基础消息,再在 AI 分支条件下填充标准化 tool_calls(id/type/function.name/JSON 参数);移除初始对象字面量中的 tool_calls
Zhipu 适配器工具调用映射
packages/zhipu-adapter/src/utils.ts
取消基于模型名的初始 tool_calls 注入;改为仅在 AI 分支从 AIMessage.tool_calls 映射标准结构(id/type='function'/function.name/JSON 参数)。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Caller
  participant U as Adapter Utils
  participant P as Provider API

  C->>U: langchain Message
  alt rawMessage.getType() == "ai"
    U->>U: 读取 AIMessage.tool_calls
    opt 存在非空 tool_calls
      U->>U: 映射为 {id, type:"function", function:{name, arguments(JSON)}}
      U->>P: 提交 Provider 消息(含 tool_calls)
    end
  else 非 AI
    U->>P: 提交 Provider 消息(不含 tool_calls)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

小兔挥笔映工具,
AI低语入栈序。
名与参,成一书,
字串装袋不含糊。
轻跃四适配,消息更清楚。 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed PR 标题“chore: 发布 alpha.43/alpha.5 版本并修复工具调用问题”简明扼要地概括了本次变更的两大核心:版本发布和适配器中工具调用处理的修复,且与 raw_summary 中各适配器与核心模块的修改吻合。标题清晰、相关且便于快速识别主要目的。
Description Check ✅ Passed PR 描述详细列出版本更新、将工具调用从 additional_kwargs.tool_calls 迁移到 AIMessage.tool_calls、错误处理改进及其它清理项,这些内容与 pr_objectives 和各文件的 raw_summary 中记录的改动高度一致,因而通过宽松的描述检查。描述覆盖了主要改动点与版本号,相关且具体。

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
packages/spark-adapter/src/utils.ts (3)

84-98: 空数组访问会崩溃:当仅包含 system 且 removeSystemMessage=true 时,result 为空
result[result.length - 1]result[0] 在空数组上会抛异常。需要加长度保护。

-    if (result[result.length - 1].role === 'assistant') {
+    if (result.length > 0 && result[result.length - 1].role === 'assistant') {
         result.push({
             role: 'user',
             content:
                 'Continue what I said to you last message. Follow these instructions.'
         })
     }
 
-    if (result[0].role === 'assistant') {
+    if (result.length > 0 && result[0].role === 'assistant') {
         result.unshift({
             role: 'user',
             content:
                 'Continue what I said to you last time. Follow these instructions.'
         })
     }

154-161: 返回 undefined 与签名不匹配,TS 会报错
formatToolsToSparkTools 在空数组时返回 undefined,但签名是 ChatCompletionTool[]。要么返回空数组,要么放宽签名。

建议放宽返回类型,避免下游误用:

-export function formatToolsToSparkTools(
-    tools: StructuredTool[]
-): ChatCompletionTool[] {
+export function formatToolsToSparkTools(
+    tools: StructuredTool[]
+): ChatCompletionTool[] | undefined {
     if (tools.length < 1) {
         return undefined
     }
     return tools.map(formatToolToSparkTool)
}

如更倾向总是返回数组,也可改为 return [] 并保留原签名。


103-126: 改为写入 AIMessageChunk.tool_calls(已确认 LangChain 支持)

AIMessageChunk/AIMessage 已支持 tool_calls(JS 文档与指南;@langchain/core 在 0.2.9 及后续版本对 OpenAI-formatted tools 有相关支持)。将增量块的 tool_calls 直接写入 chunk.tool_calls,旧版兼容时保留 additional_kwargs 回退。

File: packages/spark-adapter/src/utils.ts
Lines: 103-126

-    if (delta.tool_calls && delta.tool_calls.length > 0) {
-        chunk.additional_kwargs.tool_calls = delta.tool_calls.map(
+    if (delta.tool_calls && delta.tool_calls.length > 0) {
+        // 若 AIMessageChunk 支持 tool_calls 字段,优先使用之;否则维持 additional_kwargs 以兼容旧路径
+        (chunk as any).tool_calls = delta.tool_calls.map(
             (toolCall) => ({
                 id: toolCall.id,
                 type: toolCall.type,
                 function: {
                     name: toolCall.function.name,
                     arguments: toolCall.function.arguments
                 }
             })
         )
     }
packages/qwen-adapter/src/utils.ts (4)

68-77: 为 function 消息补充 name,避免把 name 赋给 tool 消息。

函数调用返回(function/function role)通常需要携带函数名,而 tool 消息一般不需要 name。当前条件将 name 赋给 tool,且 function 未赋值,可能导致供应商拒绝或丢失上下文。

应用以下修正:

-      name:
-          role === 'assistant' || role === 'tool'
-              ? rawMessage.name
-              : undefined,
+      name:
+          role === 'assistant' || role === 'function'
+              ? rawMessage.name
+              : undefined,

102-112: arguments 规范化缺少异常捕获,非 JSON 字符串会抛错。

当前对每个 tool.arguments 直接 JSON.parse,若为普通字符串将报错并中断请求。

应用 try/catch 降级处理:

-                // Remove spaces, new line characters etc.
-                tool.arguments = JSON.stringify(JSON.parse(tool.arguments))
+                // 规范化可解析的 JSON;否则保留原值
+                try {
+                    tool.arguments = JSON.stringify(JSON.parse(tool.arguments))
+                } catch {
+                    /* noop */
+                }

114-116: 潜在空指针:rawMessage.additional_kwargs 可能为 undefined。

直接访问 .images 有 NPE 风险,违背“空值安全检查”目标。

建议使用可选链并保持类型断言:

-        const images = rawMessage.additional_kwargs.images as string[] | null
+        const images = (rawMessage as any).additional_kwargs?.images as string[] | null

193-196: fix: role 可能为 undefined,toLowerCase 会崩溃。

当 delta.role 为空且 defaultRole 未传入时,.toLowerCase() 调用会抛错。

建议保证回退值:

-    const role = (
-        (delta.role?.length ?? 0) > 0 ? delta.role : defaultRole
-    ).toLowerCase()
+    const rawRole =
+        ((delta.role?.length ?? 0) > 0 ? delta.role : defaultRole) ?? 'assistant'
+    const role = rawRole.toLowerCase()
🧹 Nitpick comments (5)
packages/spark-adapter/src/utils.ts (2)

33-46: tool_calls 映射方向正确;arguments 建议做“字符串或对象”双分支以更健壮
当前总是 JSON.stringify(toolCall.args);当上游已提供字符串时会出现二次转义。建议按类型处理,保持与其他适配器一致。

-                msg.tool_calls = toolCalls.map((toolCall) => ({
+                msg.tool_calls = toolCalls.map((toolCall) => ({
                     id: toolCall.id,
                     type: 'function',
                     function: {
                         name: toolCall.name,
-                        arguments: JSON.stringify(toolCall.args)
+                        arguments:
+                            typeof (toolCall as any).args === 'string'
+                                ? (toolCall as any).args
+                                : JSON.stringify((toolCall as any).args)
                     }
                 }))

71-81: 硬编码英文提示文本,建议走 i18n 或最少抽常量
在多语言环境下固定英文回复可能影响体验,且不便于后续调整。

可将固定文案提取到 constants 并/或接入现有 i18n 方案。

Also applies to: 84-98

packages/shared/src/utils.ts (2)

49-49: 建议移除注释掉的代码

这行被注释掉的 function_call 代码已被新的 tool_calls 实现替代,应该被移除以保持代码整洁。

应用以下修改来移除过时的注释代码:

-            //  function_call: rawMessage.additional_kwargs.function_call,

53-59: 确认:msg.tool_calls 的空值删除不会导致错误

delete 语句在赋值之前(53–59 行)执行,但删除不存在的属性无副作用;后续在 rawMessage.getType() === 'ai' 分支(61–74 行)满足条件时会重新赋值,逻辑正确。可选:为可读性将 53–59 行的删除逻辑移到 74 行之后或直接移除初始的 delete(非必须)。

packages/qwen-adapter/src/utils.ts (1)

199-205: additionalKwargs 的类型声明包含未使用字段。

function_call 与 tool_calls 未被填充,易误导维护者。

可精简为仅保留 reasoning_content:

-    const additionalKwargs: {
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/naming-convention
-        function_call?: any
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/naming-convention
-        tool_calls?: any
-        reasoning_content?: string
-    } = {}
+    const additionalKwargs: { reasoning_content?: string } = {}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ef8fbf and cb6d5de.

⛔ Files ignored due to path filters (26)
  • packages/azure-openai-adapter/package.json is excluded by !**/*.json
  • packages/claude-adapter/package.json is excluded by !**/*.json
  • packages/core/package.json is excluded by !**/*.json
  • packages/deepseek-adapter/package.json is excluded by !**/*.json
  • packages/dify-adapter/package.json is excluded by !**/*.json
  • packages/doubao-adapter/package.json is excluded by !**/*.json
  • packages/embeddings-service/package.json is excluded by !**/*.json
  • packages/gemini-adapter/package.json is excluded by !**/*.json
  • packages/hunyuan-adapter/package.json is excluded by !**/*.json
  • packages/image-renderer/package.json is excluded by !**/*.json
  • packages/image-service/package.json is excluded by !**/*.json
  • packages/long-memory/package.json is excluded by !**/*.json
  • packages/mcp-client/package.json is excluded by !**/*.json
  • packages/ollama-adapter/package.json is excluded by !**/*.json
  • packages/openai-adapter/package.json is excluded by !**/*.json
  • packages/openai-like-adapter/package.json is excluded by !**/*.json
  • packages/plugin-common/package.json is excluded by !**/*.json
  • packages/qwen-adapter/package.json is excluded by !**/*.json
  • packages/rwkv-adapter/package.json is excluded by !**/*.json
  • packages/search-service/package.json is excluded by !**/*.json
  • packages/shared/package.json is excluded by !**/*.json
  • packages/spark-adapter/package.json is excluded by !**/*.json
  • packages/variable-extension/package.json is excluded by !**/*.json
  • packages/vector-store-service/package.json is excluded by !**/*.json
  • packages/wenxin-adapter/package.json is excluded by !**/*.json
  • packages/zhipu-adapter/package.json is excluded by !**/*.json
📒 Files selected for processing (4)
  • packages/qwen-adapter/src/utils.ts (3 hunks)
  • packages/shared/src/utils.ts (3 hunks)
  • packages/spark-adapter/src/utils.ts (2 hunks)
  • packages/zhipu-adapter/src/utils.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-15T17:40:57.183Z
Learnt from: dingyi222666
PR: ChatLunaLab/chatluna#546
File: packages/core/src/llm-core/chat/app.ts:114-119
Timestamp: 2025-09-15T17:40:57.183Z
Learning: 在 ChatLuna 的 packages/core/src/llm-core/chat/app.ts 中,displayResponse 的构造只需要保留 content 和 additional_kwargs,tool_calls、tool_call_id、name、id 等元数据在代码库的其他地方处理,无需在 displayResponse 中保留这些字段。

Applied to files:

  • packages/spark-adapter/src/utils.ts
🧬 Code graph analysis (3)
packages/qwen-adapter/src/utils.ts (2)
packages/zhipu-adapter/src/types.ts (1)
  • ChatCompletionResponseMessage (25-44)
packages/qwen-adapter/src/types.ts (1)
  • ChatCompletionResponseMessage (24-45)
packages/zhipu-adapter/src/utils.ts (1)
packages/zhipu-adapter/src/types.ts (1)
  • ChatCompletionResponseMessage (25-44)
packages/spark-adapter/src/utils.ts (1)
packages/spark-adapter/src/types.ts (1)
  • ChatCompletionMessage (110-116)
🔇 Additional comments (8)
packages/spark-adapter/src/utils.ts (2)

2-2: 导入 AIMessage 用于标准化 tool_calls:LGTM
与本 PR 的迁移目标一致,所需类型已正确引入。


48-48: 返回已构造的 msg:LGTM
与上方条件追加 tool_calls 的策略一致。

packages/shared/src/utils.ts (2)

2-2: 正确迁移到标准工具调用接口

添加 AIMessage 导入支持了从旧的 additional_kwargs.tool_calls 到新的标准 AIMessage.tool_calls 属性的迁移。这符合 LangChain 的最新工具调用接口标准。


61-74: LGTM:正确实现类型安全的工具调用处理

这个实现正确地:

  1. 仅对 AI 类型消息提取工具调用
  2. 将工具调用映射为标准格式,包含 id、type 和 function 对象
  3. 正确序列化参数为 JSON 字符串

与 PR 目标和其他适配器的实现保持一致。

packages/zhipu-adapter/src/utils.ts (2)

2-2: LGTM: 正确引入 AIMessage 类型

从 LangChain 的文档可以看出,工具调用处理已经从 AIMessage.additional_kwargs 迁移到标准的 AIMessage.tool_calls 属性。正确引入 AIMessage 类型是实现这一迁移的必要步骤。


48-61: LGTM: 正确实现工具调用处理的标准化迁移

这段代码正确实现了从已弃用的 additional_kwargs.tool_calls 到标准 AIMessage.tool_calls 属性的迁移:

  1. 类型检查: 只有当消息类型为 'ai' 时才处理工具调用
  2. 标准格式: 将工具调用映射为标准的 ToolCall 格式,包含 id、type 为 'function' 和 function 对象(包含 name 和 JSON 字符串化的 arguments)
  3. 空值安全: 检查 toolCalls 是否为数组且长度大于 0

这与其他适配器的实现保持一致,符合 PR 目标中提到的统一工具调用处理的要求。

packages/qwen-adapter/src/utils.ts (2)

1-12: 引入 AIMessage 合理,与下方对 AIMessage.tool_calls 的使用一致。


171-186: 确认 'function' 角色是否为 Qwen 可接受角色;必要时映射为 'tool'。

部分供应商已弃用 'function' 角色,统一使用 'tool'。当前实现返回 'function' 可能导致请求被拒。

若确认不支持 'function',建议调整如下:

-        case 'function':
-            return 'function'
+        case 'function':
+            return 'tool'

并与上文 name 赋值逻辑保持一致(function -> name,tool -> 无 name)。

@dingyi222666 dingyi222666 merged commit 945dc5a into v1-dev Sep 21, 2025
3 of 5 checks passed
@dingyi222666 dingyi222666 deleted the chore/update-version branch September 21, 2025 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

最新版报错

2 participants