Skip to content

fix(openai): preserve instruction roles and tool call data - #442

Closed
Kiro-Durandal wants to merge 1 commit into
su-kaka:masterfrom
Kiro-Durandal:codex/upstream-chat-completions-conversion
Closed

Kiro-Durandal wants to merge 1 commit into
su-kaka:masterfrom
Kiro-Durandal:codex/upstream-chat-completions-conversion

Conversation

@Kiro-Durandal

Copy link
Copy Markdown

修复 Chat Completions → AGY 转换中的工具参数丢失、中途指令降级和部分内容遗漏,并明确报告下述未支持输入。

改动与原因

  • 保留工具参数:为工具调用使用独立的 function 模型,保留原始 arguments(字符串/对象,缺失时默认空字符串),避免 Pydantic 忽略参数后将合法调用降级为 {}。
  • 保留指令性质:按顺序将所有位置的 system/developer 文本汇总到 systemInstruction,避免中途指令被改为普通 user;共享函数仍处理 Anthropic 顶层 system。
  • 保留内容:统一文本转换;支持 refusal 内容块、无正文时的 refusal 字段,以及工具结果文本块拼接;校验图片 data URL 与 Base64。远程图片 URL、音频、文件、未知内容块及空普通消息明确报错,避免静默遗漏。
  • 统一工具名称和选择:声明、调用、结果和命名 tool_choice 使用相同规范化;支持 auto/none/required 及命名函数选择。检查名称冲突、无工具却 required、指定未声明工具及命名选择的类型错误,避免名称不匹配或未处理异常。
  • 明确错误边界:限定 system/developer/user/assistant/tool、函数工具、assistant 的 tool_calls 和文本指令;旧版 function/function_call、assistant 音频历史等不支持输入由模型校验返回 422;转换 ValueError 返回 400。发送前确认 contents 只有 user/model,防止非法角色到达 AGY。
  • 维护与文档:合并重复转换逻辑;README 补充能力与错误说明;附一份离线回归测试。涉及四个运行文件、README 和测试文件。

兼容性取舍

  • Gemini 只有一个顶层指令字段,汇总会丢失指令的历史位置及 system/developer 的角色区别,不是无损语义映射。
  • 上述内容、角色及工具选择校验更严格:部分原先被忽略或交给远端报错的输入会改为本地 400/422;未实现完整 Chat Completions 功能。
  • 保留 fix(converter): 流式并行工具调用 index 恒为 0,调用被归并成一个并进而触发空回 #406 的畸形参数恢复:提取首个 JSON 对象,失败时保留空参数调用。重复 ID、不完整历史、名称回退和末尾 assistant 的处理沿用上游;不保证所有畸形请求都能被 AGY 接受。
三个离线复现示例:参数、指令、未支持内容

以下输入经过请求模型和转换器,比较字段或本地错误;不调用真实模型。示例使用 gemini-3.8-flash-high,实机请求应先确认部署实际提供的模型名。

1. 工具参数丢失

{
  "model": "gemini-3.8-flash-high",
  "messages": [
    {"role": "user", "content": "查询 test 对应的值"},
    {"role": "assistant", "tool_calls": [{
      "id": "call_example", "type": "function",
      "function": {"name": "lookup", "arguments": "{\"key\":\"test\"}"}
    }]},
    {"role": "tool", "tool_call_id": "call_example", "content": "{\"value\":\"OK\"}"}
  ]
}

原有请求模型丢掉 arguments,最终 functionCall.args = {};修改后为 {"key":"test"}。

2. 中途 developer 被降为 user

{
  "model": "gemini-3.8-flash-high",
  "messages": [
    {"role": "user", "content": "你好"},
    {"role": "assistant", "content": "你好"},
    {"role": "developer", "content": "请简短回答"},
    {"role": "user", "content": "继续"}
  ]
}
原有:无 systemInstruction;最后一个 user 包含“请简短回答”和“继续”。
修改后:systemInstruction 包含“请简短回答”;最后一个 user 只含“继续”。

3. 远程图片被静默遗漏

{
  "model": "gemini-3.8-flash-high",
  "messages": [{"role": "user", "content": [
    {"type": "text", "text": "描述这张图片"},
    {"type": "image_url", "image_url": {"url": "https://example.com/image.png"}}
  ]}]
}

原有仅转发文本;修改后在发送前返回 HTTP 400:

{"detail":"Antigravity requires image_url content as a base64 image data URL"}

远程图片 URL 可以是合法的 Chat Completions 输入,但当前转换器仅处理 Base64 图片 data URL;此处是明确报告限制。

验证

基线 731e81e11ea0c96d40fff59ac94b1a0bbffe1101,Windows / Python 3.12.14:

python -m unittest discover -s tests -p test_openai_message_conversion.py -v

15 项全部通过,覆盖上述字段转换、工具选择、既有历史容错、图片编码以及模拟路由的 400/422 和发送前拦截。两个核心测试切回原始上游后,在四个子场景中按预期失败;上述三份示例 JSON 也已直接提取并复核。

版本查询和 AGY 后端均模拟,无需密钥。Anthropic 只验证共享合并函数,未测完整 messages 路由;图片检查只验证数据转换,不验证视觉理解。最新完整贡献分支尚未重做真实 AGY 端到端测试,字段检查也不证明模型指令遵循效果提升。

Black 24.10.0 按仓库配置(行长 100、目标 Python 3.13)检查通过:新测试整文件、运行代码的修改范围(--line-ranges);git diff --check 通过。

su-kaka added a commit that referenced this pull request Oct 27, 2026
@su-kaka

su-kaka commented Oct 27, 2026

Copy link
Copy Markdown
Owner

已部分合并

@su-kaka su-kaka closed this Oct 27, 2026
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