Skip to content

Dialect translation

hlid's core principle is passthrough-first — when the agent's dialect matches the backend's, requests flow through unchanged, byte for byte. Translation happens only when dialects differ.

The matrix

Agent speaksBackend speaksWhat happens
OpenAI ChatOpenAI ChatPassthrough — bytes forwarded as-is
Anthropic MessagesAnthropic MessagesPassthrough — bytes forwarded as-is
Anthropic MessagesOpenAI ChatTranslate — Messages ⇄ Chat Completions
OpenAI ChatAnthropic MessagesTranslate — Chat Completions ⇄ Messages

Most real traffic is passthrough. llama.cpp speaks every dialect an agent does, and same-vendor agent/backend pairs match by definition. The cross-dialect case collapses to one boundary — OpenAI-family ⇄ Anthropic-family — and hlid implements both directions of it.

What translation covers

  • System promptssystem field ⇄ system message
  • Multi-turn conversations — role mapping across the full history
  • Tool usetool_use/tool_result blocks ⇄ tool_calls/tool messages
  • Streaming — incremental SSE translation, event by event: OpenAI chunks ⇄ message_start / content_block_delta / message_delta / message_stop
  • Token usage — mapped into the response shape the agent expects
  • Finish reasonsstop_reasonfinish_reason semantics

All of it is driven by golden-test fixtures — recorded request/response pairs in tests/fixtures/ that pin the exact expected output. If a translation is wrong, the fix starts with a fixture that proves it.

Degraded inputs

Backends don't always send complete responses. The translator tolerates minimal and empty-content responses (see tests/fixtures/degraded/) rather than failing the request.

Seeing it happen

Every capture in /hlid/requests carries a translated flag plus the inbound and backend dialects, so you can verify which path a request took:

json
{
  "inbound_dialect": "anthropic-messages",
  "backend_dialect": "openai-chat",
  "translated": true
}

MIT licensed · built at the workbench