Provider protocols
octo speaks two wire formats. Provider quirks are encapsulated entirely in
internal/provider/{anthropic,openai}/ — the agent layer never branches on which protocol it’s
talking to.
Anthropic Messages
Section titled “Anthropic Messages”- Auth via
x-api-key+anthropic-versionheaders. systemis a top-level field, not a message.- Content blocks:
[{type: "text", text}]. - The SSE aggregator dispatches on
message_start/content_block_delta/message_delta. - Tool calls land as a
content_block_startof typetool_use, followed byinput_json_deltafragments that accumulate into the final arguments. stop_reason: "tool_use"is the agent-facing signal that a turn wants to call a tool.
OpenAI Chat Completions
Section titled “OpenAI Chat Completions”- Auth via
Authorization: Bearer. systemis carried asmessages[0], not a separate field.- The SSE aggregator parses
chat.completion.chunkobjects, and tolerates a missing[DONE]sentinel — some third-party OpenAI-compatible servers omit it. - Tool calls arrive in
delta.tool_calls[], chunked bytool_calls[i].index; the aggregator concatenates fragments by index before parsing the JSON arguments. finish_reason: "tool_calls"is normalized to"tool_use"on the agent-facing surface, so the agent loop only ever sees one spelling regardless of backend.stream_options.include_usage: trueis sent on every streaming request — DashScope (Bailian) and real OpenAI emit no usage chunk at all without it, so a streamed turn would otherwise report zero tokens. DeepSeek sends usage either way.
Cache token accounting
Section titled “Cache token accounting”Cache semantics differ by protocol, not by vendor:
- Anthropic-protocol endpoints (real Anthropic, and Anthropic-compatible gateways) report
input_tokensas the uncached remainder, withcache_read_input_tokensreported separately. - OpenAI-protocol endpoints (DeepSeek’s default endpoint, DashScope) report
prompt_tokensas the whole input, withcached_tokensas a subset of it.
The agent treats input tokens and cache-read tokens as non-overlapping buckets — context occupancy is their sum — so the OpenAI adapter subtracts cached from prompt before handing counts to the agent layer; the Anthropic adapter needs no adjustment.
Extended reasoning
Section titled “Extended reasoning”Anthropic thinking blocks and OpenAI reasoning_content are unified behind
--reasoning-effort / --show-reasoning: OpenAI-protocol backends receive the effort level
directly as reasoning_effort; Anthropic-protocol backends map it to adaptive thinking or an
explicit token budget, normalized per model family so older models and newer ones both get a
sensible mapping from the same five-level scale.
Next: adding a third protocol, or a new tool, follows the pattern in Extending octo.