这份手册用于在完整 Codex subagent 验证前,先用真实 OpenCode Go API Key 验证 adapter 的基础协议链路。
按顺序执行。遇到第一个失败点就停止,记录请求、响应、模型 ID 和 adapter 日志。
cd D:\AI-Tools\codex-opencode-adapter
git status --short
cargo fmt --check
cargo test期望:
- 工作区没有非预期修改。
cargo fmt --check通过。cargo test通过。
单独开一个终端运行:
codex-opencode-adapter init --api-key "<你的 OpenCode Go API Key>"
$env:CODEX_OPENCODE_MAX_CONCURRENCY = "1"
$env:RUST_LOG = "codex_opencode_adapter=debug"
codex-opencode-adapter start第一轮真实验证建议把并发设为 1。串行链路稳定后再提高并发。
另开一个终端:
Invoke-RestMethod http://127.0.0.1:4010/health期望:
status = ok
检查模型列表:
$token = codex-opencode-adapter auth print-local-token
$headers = @{ Authorization = "Bearer $token" }
$model = ((Invoke-RestMethod http://127.0.0.1:4010/v1/models -Headers $headers).data.id | Select-Object -First 1)
$model记录准备测试的模型 ID。它应带 opencode_adapter/<project_key>/opencode-go/<id> 前缀;后续请求都复用 $model。
$body = @{
model = $model
input = "Reply with exactly: adapter-ok"
stream = $false
} | ConvertTo-Json -Depth 20
Invoke-RestMethod http://127.0.0.1:4010/v1/responses `
-Method Post `
-Headers $headers `
-ContentType "application/json" `
-Body $body | ConvertTo-Json -Depth 50期望:
object = responsestatus = completedmodel仍是带opencode_adapter/<project_key>/opencode-go/前缀的 routed model- 输出文本包含
adapter-ok - 如果上游返回 usage,adapter 响应中也保留 usage shape
$body = @{
model = $model
input = "Reply with exactly: stream-ok"
stream = $true
} | ConvertTo-Json -Depth 20
curl.exe -N `
-H "Authorization: Bearer $token" `
-H "Content-Type: application/json" `
-d $body `
http://127.0.0.1:4010/v1/responses期望事件形状:
response.created
response.in_progress
response.output_item.added
response.output_text.delta
response.output_text.done
response.output_item.done
response.completed
[DONE]
记录 token 是否逐步返回,还是接近结束时一次性返回。
验证非流式 tool call 能被 Codex 采纳,并能续传 tool output。
请求:
$body = @{
model = $model
input = "Call the run tool with cmd set to echo tool-ok. Do not answer directly."
stream = $false
tools = @(
@{
type = "function"
name = "run"
description = "Run a shell command"
parameters = @{
type = "object"
properties = @{
cmd = @{ type = "string" }
}
required = @("cmd")
}
}
)
} | ConvertTo-Json -Depth 50
$response = Invoke-RestMethod http://127.0.0.1:4010/v1/responses `
-Method Post `
-Headers $headers `
-ContentType "application/json" `
-Body $body
$response | ConvertTo-Json -Depth 50期望:
- output 中出现
function_call call_id非空- name 是
run - arguments 包含
cmd - adapter 已保存 response state,供下一步续传
续传请求:
$call = $response.output | Where-Object { $_.type -eq "function_call" } | Select-Object -First 1
$continueBody = @{
model = $model
previous_response_id = $response.id
input = @(
@{
type = "function_call_output"
call_id = $call.call_id
output = "tool-ok"
}
)
stream = $false
} | ConvertTo-Json -Depth 50
Invoke-RestMethod http://127.0.0.1:4010/v1/responses `
-Method Post `
-Headers $headers `
-ContentType "application/json" `
-Body $continueBody | ConvertTo-Json -Depth 50期望:
- 不出现
invalid_tool_history - 模型能使用 tool output
- 如果
previous_response_id失败,查看stored_response_not_found
重复上一节续传,但不传 previous_response_id。在 input 中同时带上原始 tool call 和 tool output。
期望诊断:
stateless_tool_history_bypass_state_lookup
期望行为:
- 不因缺少 stored state 失败
build_chat_payload()能修复 self-contained history- 响应仍是合法 Responses shape
重复 function-call 请求,但设为:
stream = true
期望:
- 如果上游先输出文本、后输出 tool call,早期文本不应成为最终 assistant output
- 最终 stream 中出现 tool-call output item
- 终止事件为
response.completed和[DONE]
如果 stream 在正常 finish reason 前结束:
- 记录是否出现
response.incomplete - 记录上游最后一个 chunk
- 记录 adapter 是否有 stream truncation 相关日志
如果测试客户端能请求 custom tool,再验证:
- output item type 是
custom_tool_call - custom tool input 只 finalize 一次
- 续传使用
custom_tool_call_output
如果测试客户端能请求 tool_search,再验证:
- output item type 是
tool_search_call - tool search arguments 保持 JSON shape
- 续传使用
tool_search_output
向一个已知或疑似文本模型发送小型 image/file/audio 输入。
期望:
- 非流式请求返回 Responses object,
status = failed error.code = unsupported_multimodal_input- 流式请求发出
response.failed和[DONE] - 父 agent 收到协议合法 failure,而不是 provider error 断链
临时使用错误的上游 key 或错误的上游 model 调用 /v1/responses 非流式。
当前期望行为:
- HTTP status 可能是非 2xx
- body 仍是 Responses object,
status = failed error.type和error.code是upstream_error
决策点:
如果真实 Codex subagent 把非 2xx 当作断链,即使 body 是 Responses failed,也需要把 /v1/responses 的上游错误改成 HTTP 200 + response.status = failed。不要同时改 /v1/models,除非另有验证证据。
用:
$env:RUST_LOG = "codex_opencode_adapter=debug"重点观察:
stored_response_not_found
tool_history_unique_fallback_hit
tool_history_call_id_ambiguous
tool_history_response_ambiguous
tool_history_call_id_not_found
stateless_tool_history_bypass_state_lookup
解释见 docs/DIAGNOSTICS.md。
每轮真实验证复制一份:
Date:
Adapter commit:
OS / shell:
Codex client:
OpenCode Go base URL:
Model alias used:
Upstream model ID after prefix stripping:
Request type: non-stream | stream
Tool type: none | function | custom | tool_search
Multimodal input: none | image | file | audio
HTTP status:
Responses status:
Terminal stream events:
Output item types:
Usage shape:
Continuation mode: previous_response_id | unique call_id fallback | stateless full history | none
Adapter diagnostics:
Unexpected upstream fields:
Result: pass | fail | unclear
Notes:
出现以下任一情况,停止真实验证,先补 regression test 再修 adapter:
/v1/responses返回 plain{error: ...}- stream 没有任何 terminal Responses event 就结束
- 有效
previous_response_id下 tool output continuation 失败 - tool output continuation 被错误匹配到其他 stored response
- 文本模型 multimodal failure 导致协议断链
- 上游出现当前 tests 未覆盖的新 content/tool shape