Hermes Decision Trace

SharedChat Adapter Tool Bridge 与 systemd 托管修复记录

SharedChat Codex Adapter 已从“纯文本 Chat→Responses 转换器”升级为可支持 Hermes agent 工具调用的 bridge;SC-GPT-5.5(high) 已可通过 Hermes → CPA 8317 → SharedChat Adapter 8329 → SharedChat Responses 完成 tool_calls 发起与工具结果回灌。

🧭
推荐路径

保留 sharedchat-codex-adapter.service 作为 systemd --user 托管入口,继续通过 CPA alias SC-GPT-5.5(high) 使用,不再手动裸跑 Python 进程。

🔎
关键依据

本地转换回归、SharedChat 上游 function_call 探针、adapter 8329 本地请求、CPA 8317 真实链路、双轮工具回灌均已验证通过。

🛠️
落地方式

代码落在 /home/ht/code/sharedchat_codex_adapter.py,回归测试落在 /home/ht/code/test_sharedchat_codex_adapter_bridge.py,服务单元为 /home/ht/.config/systemd/user/sharedchat-codex-adapter.service

证据摘要

  • 本地转换回归、SharedChat 上游 function_call 探针、adapter 8329 本地请求、CPA 8317 真实链路、双轮工具回灌均已验证通过。

行动清单

后续如果 SC 模型再次声称“没有工具”,先查当前会话实际模型与 provider,再查 adapter service 状态和 CPA alias,不要先怀疑 WebUI 工具注册。

边界 / 风险

streaming tool_calls 不是原生 delta

当前 tools 场景下 adapter 用 non-stream upstream + synthetic SSE。这保证 Hermes tool loop 正确,但不提供真实逐 token / 逐 delta tool_call streaming。 处理建议:除非明确需要低延迟 tool_call delta,否则保持当前保守实现。

上游 Responses 事件格式未来可能变化

output[].type=function_call call_id name arguments 如果 SharedChat 上游更换 Responses 格式,需要更新 _responses_tool_calls()

SC 模型是否主动调用工具仍受模型策略影响

bridge 已确保工具 schema 可达,但模型是否选择调用工具仍由模型行为决定。若需要强制调用,应使用 tool_choice

完整记录

SharedChat Adapter Tool Bridge 与 systemd 托管修复记录

背景

本轮问题来自 WebUI 中一开始使用 SC-GPT-5.5(high) 时,助手回复“没有 terminal/web_search/skill_view 等工具”;切换到 gpt-5.5 后工具立即可用。排查后确认:Hermes/WebUI 工具注册本身正常,问题集中在 SC-GPT-5.5(high) 的 provider 路径。

实际链路为:

Hermes WebUI → custom:cpa-codex → CLIProxyAPI http://127.0.0.1:8317/v1/chat/completions → SharedChat Adapter http://127.0.0.1:8329/v1/chat/completions → https://new.sharedchat.cc/codex/v1/responses

CPA 配置中 alias 映射:

openai-compatibility: - name: SharedChat-Adapter base-url: http://127.0.0.1:8329/v1 models: - name: gpt-5.5 alias: SC-GPT-5.5(high)

根因

旧版 /home/ht/code/sharedchat_codex_adapter.py 是最小文本 adapter,仅转换:

  • model
  • messages -> input
  • max_tokens / max_completion_tokens -> max_output_tokens
  • temperature
  • top_p
  • stream

但没有处理:

  • OpenAI Chat tools
  • OpenAI Chat tool_choice
  • parallel_tool_calls
  • assistant 历史 tool_calls
  • role=tool 工具结果
  • Responses function_call 回 Chat message.tool_calls

因此 Hermes 虽然把工具 schema 交给模型请求,但经过 adapter 时被静默丢弃,上游模型看不到工具定义,只能以普通文本模型行为回复。

本次修改

1. 补 Chat Completions → Responses tools bridge

新增转换能力:

  • Chat tools[{type:function,function:{...}}] → Responses tools[{type:function,name,description,parameters,strict}]
  • Chat tool_choice → Responses tool_choice
  • parallel_tool_calls 透传
  • role=tool → Responses function_call_output
  • assistant prior tool_calls → Responses function_call

2. 补 Responses → Chat tool_calls bridge

新增转换能力:

  • Responses output[].type=function_call → OpenAI Chat message.tool_calls[]
  • function call call_id/name/arguments 保持 OpenAI-compatible 结构
  • 存在 tool_calls 时,finish_reason=tool_calls
  • 普通文本仍保持 message.contentfinish_reason=stop

3. 处理 tools 场景 streaming

为了避免半成品 streaming tool delta 破坏 Hermes agent loop:

  • 无 tools 的普通 streaming:继续使用原 Responses SSE → Chat chunk 路径。
  • 有 tools 的 streaming:adapter 改为先非流式请求上游,再合成 OpenAI SSE,输出 role/content/tool_calls/final chunk。

这是当前最稳的兼容策略:优先保证工具调用闭环,而不是追求低延迟 token streaming。

4. 增加本地回归测试

新增:

/home/ht/code/test_sharedchat_codex_adapter_bridge.py

覆盖:

  • tools 不再被静默丢弃
  • tool_choice 正确转换
  • assistant tool_calls 与 role=tool 能回灌到 Responses input
  • Responses function_call 能转回 OpenAI Chat tool_calls
  • 普通文本响应不受影响

systemd 托管状态

服务单元:

/home/ht/.config/systemd/user/sharedchat-codex-adapter.service

当前单元内容:

[Unit] Description=SharedChat Codex Chat-to-Responses Adapter After=network-online.target Wants=network-online.target [Service] Type=simple EnvironmentFile=%h/.config/sharedchat-codex-adapter/env WorkingDirectory=/home/ht/code ExecStart=/usr/bin/python3 /home/ht/code/sharedchat_codex_adapter.py Restart=on-failure RestartSec=5 NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=read-only ReadWritePaths=/home/ht/code /home/ht/.config/sharedchat-codex-adapter [Install] WantedBy=default.target

已执行并确认:

systemctl --user daemon-reload systemctl --user enable sharedchat-codex-adapter.service systemctl --user restart sharedchat-codex-adapter.service

运行态:

LoadState=loaded UnitFileState=enabled ActiveState=active SubState=running MainPID=2406477 NRestarts=0

监听:

127.0.0.1:8329

健康检查:

{ "status": "ok", "upstream_base": "https://new.sharedchat.cc/codex/v1", "models": ["gpt-5.5", "gpt-5.4"] }

验证记录

1. 语法与本地回归测试

命令:

python3 -m py_compile /home/ht/code/sharedchat_codex_adapter.py /home/ht/code/test_sharedchat_codex_adapter_bridge.py python3 /home/ht/code/test_sharedchat_codex_adapter_bridge.py

结果:

ok test_chat_tool_result_and_prior_tool_call_map_to_responses_items ok test_chat_tools_are_forwarded_to_responses_shape ok test_responses_function_call_maps_to_chat_tool_calls ok test_text_response_still_maps_to_chat_content

2. SharedChat 上游 function_call 探针

直接请求 https://new.sharedchat.cc/codex/v1/responses,携带 Responses tools 与强制 tool_choice,上游返回:

{ "type": "function_call", "name": "get_current_time", "arguments": "{\"timezone\":\"Asia/Shanghai\"}" }

说明上游本身支持 function calling;旧问题不是上游能力缺失,而是 adapter 未桥接。

3. Adapter 本地 8329 探针

请求:

POST http://127.0.0.1:8329/v1/chat/completions

返回关键字段:

{ "finish_reason": "tool_calls", "message": { "role": "assistant", "content": "", "tool_calls": [ { "type": "function", "function": { "name": "get_current_time", "arguments": "{\"timezone\":\"Asia/Shanghai\"}" } } ] } }

4. CPA 8317 真实链路探针

请求模型:

SC-GPT-5.5(high)

返回:

{ "status": "ok", "first_finish": "tool_calls", "first_has_tool_calls": true, "tool_name": "get_current_time", "second_finish": "stop", "second_content": "当前上海时间是 **2026-06-02 21:40:00 CST**。" }

这证明完整两轮 agent loop 已闭环:

  1. 模型发起 tool_call。
  2. Hermes/调用方执行工具。
  3. tool result 回灌给模型。
  4. 模型基于工具结果继续自然语言回答。

影响范围

已改善

  • SC-GPT-5.5(high) 可以像正常 Hermes agent 模型一样使用工具。
  • 不再因为 adapter 丢弃 tools 而误报“没有工具”。
  • CPA alias 路径不需要改,Hermes 配置不需要改。

未改变

  • CPA 主服务 8317 未重启、未修改。
  • Hermes WebUI 未重启、未修改。
  • SharedChat 上游凭证和 base URL 未变。
  • 普通文本 Chat/SSE 行为保持兼容。

风险与边界

风险 1:streaming tool_calls 不是原生 delta

当前 tools 场景下 adapter 用 non-stream upstream + synthetic SSE。这保证 Hermes tool loop 正确,但不提供真实逐 token / 逐 delta tool_call streaming。

处理建议:除非明确需要低延迟 tool_call delta,否则保持当前保守实现。

风险 2:上游 Responses 事件格式未来可能变化

当前解析兼容常见字段:

  • output[].type=function_call
  • call_id
  • name
  • arguments

如果 SharedChat 上游更换 Responses 格式,需要更新 _responses_tool_calls()

风险 3:SC 模型是否主动调用工具仍受模型策略影响

bridge 已确保工具 schema 可达,但模型是否选择调用工具仍由模型行为决定。若需要强制调用,应使用 tool_choice

回滚方式

如果需要回滚 adapter 代码:

  1. 恢复 /home/ht/code/sharedchat_codex_adapter.py 到上一版。
  2. 执行:
systemctl --user restart sharedchat-codex-adapter.service curl -fsS http://127.0.0.1:8329/health

如果只想临时绕开 SC 模型:

  • Hermes 默认模型改回 CPA Codex/OAuth 主链路模型,例如 gpt-5.5
  • 或在会话中手动切换到已确认支持 tools 的模型。

后续建议

  1. 保留本地测试文件,后续改 adapter 时先跑:
python3 /home/ht/code/test_sharedchat_codex_adapter_bridge.py
  1. 如果继续接入新的 Responses-only provider,复用这套 bridge,不要再写只转文本的最小 adapter。
  1. 若后续要把 SC-GPT-5.5(high) 设回默认模型,建议先在 Hermes WebUI 中用真实工具任务做一次人工 smoke:例如“查本机时间/查文件/查 GitHub release”,确认模型能发起工具调用。

关键路径索引

  • Adapter:/home/ht/code/sharedchat_codex_adapter.py
  • 回归测试:/home/ht/code/test_sharedchat_codex_adapter_bridge.py
  • systemd unit:/home/ht/.config/systemd/user/sharedchat-codex-adapter.service
  • env file:/home/ht/.config/sharedchat-codex-adapter/env
  • CPA config:/home/ht/cli-proxy-api/config.yaml
  • Adapter health:http://127.0.0.1:8329/health
  • CPA endpoint:http://127.0.0.1:8317/v1/chat/completions