Skip to content

Protocol Conversion

Protocol Conversion and Wire-Model Alignment

Section titled “Protocol Conversion and Wire-Model Alignment”

中文版本:协议转换与 Wire Model 对齐

ProxAI keeps protocol conversion explicit and pair-oriented. The conversion rules are split into focused developer topics so each page answers one maintenance question.

This is the developer entry point for src/translation/. If you are looking for user-facing protocol behavior, start with Protocol Guide.

  • src/protocol/ owns protocol-specific Rust wire models.
  • src/ingress/ owns inbound parsing and normalization before translation.
  • src/translation/ owns pure cross-protocol conversion between an inbound request_protocol and an outbound provider protocol.
  • src/provider/request.rs owns provider request preparation, including model rewrite, projection/summary extraction, and JSON body serialization.
  • src/provider/transport.rs owns outbound HTTP transport, auth headers, upstream URL construction, and send.
  • src/http_support/ owns HTTP carrier helpers such as ByteStream, content-type/header helpers, and response reconstruction.

Do not hide general cross-protocol conversion inside a provider subtree. Provider code may normalize provider-local quirks, but protocol-to-protocol shape changes belong in src/translation/.

Translation APIs should stay pure at the carrier boundary:

  • request translation: (request_protocol, provider_protocol, normalized_payload) -> payload
  • non-streaming response translation: (request_protocol, provider_protocol, payload) -> payload
  • streaming response translation: (request_protocol, provider_protocol, ByteStream) -> ByteStream

Do not pass HTTP Response, Body, provider request structs, or route/model rewrite details into src/translation/. This is the carrier-boundary purity required by Behavior Contract C26.

Use protocol names for wire behavior:

  • openai_responses
  • openai_chat_completions
  • anthropic_messages

Use pair-oriented conversion module names, for example:

  • openai_responses -> anthropic_messages
  • anthropic_messages -> openai_responses

Provider names are user labels and should not be treated as semantic protocol identifiers.

Client protocol
openai_responses / openai_chat_completions / anthropic_messages
Request translation
ingress -> translation/request -> provider/request
Provider
provider protocol + model rewrite + auth + transport
Response translation
upstream -> translation/response | translation/streaming
Client protocol
outbound_response rebuilt via http_support

Each direction is independently translated. ProxAI never round-trips raw provider structures; Anthropic ToolUseBlock becomes Responses FunctionCall, Anthropic ThinkingBlock becomes Responses ReasoningItem, and provider-specific metadata such as signature or tool_use_id is transformed in the process. This implements Behavior Contract C27.

A route may specify request_protocol. If omitted, the route can match any inbound request protocol detected from the actual request path. Provider protocol controls the outbound wire format, so route protocol filtering and protocol conversion are separate decisions.

Set request_protocol only when the same model pattern needs different routing for different request endpoints. If a model pattern matches but the explicit request_protocol differs from the inbound request protocol, ProxAI reports a configuration error instead of silently falling through to a default provider.

Responses ↔ Anthropic translation architecture

Section titled “Responses ↔ Anthropic translation architecture”

ProxAI sits between the client and the upstream provider, performing two-layer translation on every request/response cycle:

This means the proxy never passes through raw provider structures. Each direction is independently translated:

  • Request (client → Anthropic): client’s Responses or Chat payload is translated into an Anthropic Messages request.
  • Response (Anthropic → client): Anthropic’s message response is translated back into a Responses or Chat payload the client understands.

Responses input accepts Vec<InputItem>, where each item can be one of:

VariantPurposeWhen used
EasyInputMessageSimple role + content messageClient composing new user/developer messages
ItemTyped output items (function_call, reasoning, etc.)Multi-turn: client echoing back response items from a previous turn
ItemReferenceReference to a previously returned item by idMulti-turn: referencing items without repeating full content

ProxAI’s Anthropic translation only produces EasyInputMessage. This is because Anthropic’s MessageParam has a simple role + content structure with no concept of echoing back response items. The Item variant exists for native Responses clients that preserve the full response structure across turns.

Example of Item usage (native Responses multi-turn, not translated from Anthropic):

{
    input:[
      {
        type:"message",
        role:"user",
        content:"search"
      },
      {
        type:"function_call",
        id:"fc_1",
        name:"lookup",
        arguments:"{}"
      },
      {
        type:"function_call_output",
        call_id:"fc_1",
        output:"result"
      }
    ]
}

Response items from Anthropic (ToolUseBlock, ThinkingBlock, etc.) are translated into Responses output items (FunctionCall, ReasoningItem, etc.) when sent back to the client. The original Anthropic-specific metadata (tool_use_id, signature, etc.) is transformed in the process, so the client cannot round-trip Anthropic structures verbatim. This is by design — ProxAI guarantees correct translation in both directions, and the client works with the target protocol’s types.

OpenAI Responses and Anthropic-compatible Messages split reasoning controls differently:

  • Responses reasoning.effort maps to Anthropic output_config.effort for supported effort values (low, medium, high, xhigh). This is the preferred Anthropic-side effort field.
  • Responses reasoning.summary (auto, concise, detailed) only maps lossily to Anthropic thinking.display: "summarized"; Anthropic has no equivalent summary granularity.
  • Responses reasoning.summary without an effort maps to Anthropic thinking: {"type":"adaptive", "display":"summarized"}.
  • Responses reasoning.effort plus reasoning.summary maps to output_config.effort plus adaptive thinking.display; ProxAI does not invent a legacy thinking.budget_tokens value.
  • Responses reasoning.effort: "none" | "minimal" maps to Anthropic thinking: {"type":"disabled"} because output_config.effort has no disabled/minimal value.
  • Anthropic -> Responses and Anthropic -> Chat request translation accept the manual thinking.type: "enabled" / budget_tokens mode only as a legacy compatibility fallback. If output_config.effort is absent, ProxAI maps the budget lossily to an effort enum and logs a warning; if output_config.effort is present, it wins and the legacy budget is ignored with a warning.
  • Anthropic thinking.display: "summarized" maps to Responses reasoning.summary: "auto"; display: "omitted" does not request a Responses summary.

When protocol conversion or wire-model alignment rules change:

  1. Update this document.
  2. Update the relevant protocol document under site/src/content/docs/en/ and site/src/content/docs/zh/ if behavior changes for users or examples.
  3. Update README.md and README_CN.md when the change affects user-facing development workflow or configuration.