适配chat结构

This commit is contained in:
Sirius
2026-08-12 16:03:25 +08:00
parent 3e7a4c9540
commit f65ac095bb
5 changed files with 264 additions and 38 deletions
+32 -9
View File
@@ -135,7 +135,7 @@ export OPENCODE_CONFIG="/path/to/command-code-openai-bridge/examples/opencode.js
3. OpenCode 仍需配置任意非空 API keyBridge 忽略 Authorization,但客户端要求填写)。示例配置使用 `options.apiKey: "local-bridge"`
4. 选择模型 `command-bridge/command-default`(与 `config.yaml` 中的 `models.command-default` 映射一致)。
OpenCode 1.18.15 实际发送的 Chat 请求包含 `stream: true``stream_options.include_usage: true``max_tokens`(默认约 32000)、`store: false``tools`(约 24 个内置工具)、`tool_choice: auto`。Bridge 接受 `store`采样/输出限制参数但不改变无状态行为;未映射参数通过 `X-Command-Code-Ignored-Parameters` 标出。工具 `parameters` 中的 JSON Schema `$schema`draft 2020-12)会在校验前剥离元数据字段,避免 AJV 拒绝 OpenCode 工具定义。
OpenCode 1.18.15 实际发送的 Chat 请求包含 `stream: true``stream_options.include_usage: true``max_tokens`(默认约 32000)、`store: false``tools`(约 24 个内置工具)、`tool_choice: auto`。Bridge 接受 `store: false` 并保持 Chat 无状态;采样输出限制参数虽然在协议层接受,但未映射参数通过 `X-Command-Code-Ignored-Parameters` 标出。工具 `parameters` 中的 JSON Schema `$schema`draft 2020-12)会在校验前剥离元数据字段,避免 AJV 拒绝 OpenCode 工具定义。
OpenCode 还会在会话标题生成、子 agent 和后台任务等场景发起独立请求。这些请求与主 agent 请求共用同一 provider;将 `max_concurrent_requests` 设为大于 `1` 可让它们与多个会话并行执行。
@@ -180,11 +180,11 @@ Bearer Token 会被忽略。所有生成接口只接受文本内容。图片、
Chat 接口接受标准 function tools、`tool_choice``parallel_tool_calls`。Bridge 把工具定义、完整消息历史和工具结果交给 Command Code 决定下一步,校验返回的工具名与 arguments JSON Schema;第一次不合格时在原请求总截止时间内执行一次修复。工具由 API 客户端执行,Bridge 不执行客户端工具,也不直接访问 Obsidian vault。该流程适用于 Obsidian Copilot 的 `localSearch``readNote``getFileTree``writeFile``editFile`,也适用于其他标准 function tools。协议流程参考 [OpenAI Function calling](https://developers.openai.com/api/docs/guides/function-calling)。
`stream` 省略或设为 `false` 时返回普通 JSON。普通文本请求的 `stream: true` 会立即建立 SSE 连接并发送 assistant role 块,随后在 Command Code 的 `text_delta` 到达时立即发送对应 SSE chunk,最后发送 `finish_reason: "stop"`、可选 usage 块和 `[DONE]`。工具模式需要先解析和校验完整决策:最终文本会在校验后作为 content chunk 发送;工具调用会作为 `delta.tool_calls` 发送,并以 `finish_reason: "tool_calls"` 结束。`stream_thinking: true` 只作用于普通文本请求;该兼容格式不是原生 OpenAI reasoning item。传入 `stream_options.include_usage: true` 时,结束前返回 usage 块。
`stream` 省略或设为 `false` 时返回普通 JSON。普通文本请求的 `stream: true` 会立即建立 SSE 连接并发送 assistant role 块,随后在 Command Code 的 `text_delta` 到达时立即发送对应 SSE chunk,最后发送 `finish_reason: "stop"`、可选 usage 块和 `[DONE]`。工具模式需要先解析和校验完整决策:最终文本会在校验后作为 content chunk 发送;工具调用会作为 `delta.tool_calls` 发送,并以 `finish_reason: "tool_calls"` 结束。Chat structured output 会缓存完整正文,只有 JSON 通过校验或单次修复后才发送 content;连续失败会发送 `invalid_structured_output` 错误和 `[DONE]`,不会先发出无法收回的无效 JSON。`stream_thinking: true` 只作用于非结构化普通文本请求;该兼容格式不是原生 OpenAI reasoning item。传入 `stream_options.include_usage: true` 时,结束前返回 usage 块。
为兼容 Obsidian Copilot、OpenCode 和 LangChain OpenAI-format 客户端,Chat 接口还接受 `temperature``max_tokens``max_completion_tokens``top_p``frequency_penalty``presence_penalty``n: 1``store`。这些采样、输出限制与会话存储参数会按 OpenAI 取值范围严格校验。Command Code CLI 没有对应的 headless 参数,Chat Completions 也不保存跨请求会话;Bridge 不会把它们伪装成已生效:服务终端会输出警告,HTTP 响应 `X-Command-Code-Ignored-Parameters`。其他未实现字段仍返回 `400 unsupported_parameter`
为兼容 Obsidian Copilot、OpenCode 和 LangChain OpenAI-format 客户端,Chat 接口还接受 `temperature``max_tokens``max_completion_tokens``top_p``frequency_penalty``presence_penalty``n: 1``store: false``n: 1` 是 Bridge 实际支持的单候选协议行为;`store: false` 保持每次请求使用 `--no-session` 的无状态行为,不创建服务端 Chat session。Command Code 1.18.0 官方 `--help` 没有 temperature、top-p、frequency/presence penalty 或输出 token 上限的 headless 参数,因此前六个采样/输出限制字段只在协议层接受和校验,不会传给 CLI,也不会用提示词伪造。只要请求显式提供其中任一字段,服务终端会输出警告,HTTP 响应 `X-Command-Code-Ignored-Parameters` 会准确列出未实际应用的字段;浏览器可通过 CORS exposed header 读取。其他未实现字段仍返回 `400 unsupported_parameter`
Chat Completions 和 Responses 共用 Command Code NDJSON 执行层。服务终端会在事件到达时立即显示状态、文本和 Command Code 自身的工具调用。普通文本 Chat 会把每个 `text_delta` 直接写入 SSE;外部 function calling 模式会缓存 `finalText`,避免内部 JSON 决策进入客户端正文。Responses 外部 function calling 同样缓存 `finalText`,工具轮次不向客户端输出 assistant 文本。
Chat Completions 和 Responses 共用 Command Code NDJSON 执行层。服务终端会在事件到达时立即显示状态、文本和 Command Code 自身的工具调用。普通文本 Chat 会把每个 `text_delta` 直接写入 SSE;外部 function calling 和 structured output 模式会缓存 `finalText`,避免内部决策或未校验 JSON 进入客户端正文。`response_format` 只约束最终文本:需要调用工具的轮次先按 tools 协议返回,客户端提交工具结果后的最终文本轮次再执行格式校验。Responses 外部 function calling 同样缓存 `finalText`,工具轮次不向客户端输出 assistant 文本。
### Responses
@@ -241,7 +241,30 @@ input items 查询返回标准 `{ object: "list", data, first_id, last_id, has_m
### 结构化输出
支持
Chat Completions 支持标准 `response_format`
```json
{ "response_format": { "type": "text" } }
```
```json
{ "response_format": { "type": "json_object" } }
```
```json
{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "result",
"strict": true,
"schema": { "type": "object" }
}
}
}
```
Responses 支持对应的 `text.format`
```json
{ "text": { "format": { "type": "text" } } }
@@ -264,9 +287,9 @@ input items 查询返回标准 `{ object: "list", data, first_id, last_id, has_m
}
```
Bridge 把格式要求作为独立内部指令发送给 Command Code。最终文本必须能解析为 JSON;`json_object` 要求顶层对象,`json_schema` 使用 Ajv 校验。第一次失败后会在同一 AbortSignal 和原请求总截止时间内顺序执行一次修复,usage 是次真实用量之和。第二次仍失败时返回 `status: "incomplete"``incomplete_details.reason: "structured_output_validation_failed"`
Bridge 把格式要求作为独立内部指令发送给 Command Code。最终文本必须能解析为 JSON;`json_object` 要求顶层对象,`json_schema` 使用 Ajv 校验。第一次失败后会在同一 AbortSignal 和原请求总截止时间内顺序执行一次修复,usage 是次真实用量之和。Chat 第二次仍失败时,非流式请求返回 HTTP 502 `invalid_structured_output`,流式请求发送同 code 的 SSE 错误;Responses 则返回 `status: "incomplete"``incomplete_details.reason: "structured_output_validation_failed"`
这是 Bridge 层的提示、解析、校验和单次修复约束,不具备底层模型原生 Responses Structured Outputs 的解码级保证
这是 Bridge 层的提示、JSON 解析、Ajv 校验和单次修复约束,不是 Command Code 或底层模型提供的解码级原生 structured outputs。
官方协议依据:
@@ -382,11 +405,11 @@ command-code \
- 没有原始 Command Code TUI、颜色布局、动画和键盘交互。
- headless 无法在服务终端进行批准、拒绝、选项选择或文字回答;`ask_user_question` 不能由等待中的 HTTP 客户端处理。
- 终端事件渲染由本项目完成,格式接近日志,无法等同原始 TUI。
- 普通文本 Chat Completions 会实时转发 Command Code 文本 delta;外部 function calling Responses 结构化输出需要等待 Bridge 校验完成。
- 普通文本 Chat Completions 会实时转发 Command Code 文本 delta;外部 function calling 以及 Chat/Responses 结构化输出需要等待 Bridge 校验完成。
- Chat Completions 和 Responses 都只实现外部 function tools,不实现图片、音频、文件输入、Computer Use、web search 等 hosted tools 或 MCP hosted tool。非 function 工具类型返回 400 `unsupported_parameter`。Responses 不实现原生 reasoning item、加密 reasoning 或隐藏思维过程。
- Chat 与 Responses 的外部 function calling 都是 Bridge 通过提示协议、JSON 解析、工具参数 Schema 校验和一次修复实现的兼容层;Command Code CLI 没有公开原生 function calling 输出接口,因此连续两次输出不合格时返回 HTTP 502 `invalid_tool_decision`
- Responses 的本地文件存储只供本 Bridge 使用,没有跨进程锁或多实例一致性保证;并发请求应避免同时更新同一条 Response 链。
- Responses Structured Outputs 是 Bridge 层约束,底层 Command Code 模型仍可能连续两次输出不合格 JSON;此时状态为 incomplete。
- Chat 和 Responses Structured Outputs 是 Bridge 层约束,底层 Command Code 模型仍可能连续两次输出不合格 JSON;Chat 此时返回 `invalid_structured_output`Responses 状态为 incomplete。
- `usage` 使用 Command Code 最终结果提供的真实 input/output tokenResponses 缺失时返回 `null`Chat Completions 为兼容旧行为返回 0。
- 长文本受 HTTP 请求体上限和 Command Code 模型上下文上限共同限制,不会由桥接服务自行截断。
- v1.10.0 的大输入会让 `run_end.nextState` 重复完整提示词。实测约 96 KiB 中文输入时,CLI 退出前可能截断该大事件并丢掉紧随其后的 compact `result` 行。桥接服务会忽略冗余 `run_end`,优先使用 `result.finalText`;若退出码为 0 且 result 缺失,只使用最后一个已完整结束、无工具调用的结构化 turn 文本和真实 turn usage,不从 TUI 文本解析。