Files
homelab-frontend/tasks/2.10-anthropic-request-translation.md
T
Story Crater BotandClaude Opus 5 058f11cf2b
CI / Test (push) Canceled after 0s
CI / Vet (push) Canceled after 0s
CI / Build (push) Canceled after 0s
CI / Security (govulncheck) (push) Canceled after 0s
chore: initial commit of Go API gateway
Baseline for the Kong replacement on api.riotpiao.com. Brings the working
tree under version control for the first time: gateway source, the task
board that drives the agent runs, test fixtures, and K8s manifests.

Anchor the gateway ignore rule to the repo root. Unanchored, "gateway"
also matched the cmd/gateway/ source directory, so the program entrypoint
was excluded from every commit.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-19 20:54:34 -07:00

3.0 KiB

2.10 — Anthropic request translation (GREEN)

Phase: 2 — LLM surfaces Stage: GREEN Depends on: 2.9, 2.1

POST /llm/v1/messages accepts an Anthropic Messages request body and turns it into the dialect-neutral canonical request. The path carries /v1/messages because the Anthropic base-URL convention appends that suffix; the gateway prefix is /llm.

  • POST /llm/v1/messages is accepted and selects its upstream from the body's model field, using the same registry as /v1: reasoning reaches reasoning-predictor.llm-serving:80, ornith:35b and qwen2.5:3b-instruct reach ornith-predictor.llm-serving:80
  • The top-level system field becomes the canonical system instruction; it is a distinct field in this dialect and is not a member of messages
  • Each message content may be a plain string or an array of blocks; a string and a single text block carrying the same characters translate identically
  • max_tokens is REQUIRED on this surface, matching the Anthropic contract; a request without it is rejected as a client error and no upstream is contacted
  • max_tokens above what the upstream can serve is clamped rather than rejected, and the clamp is logged; reasoning has --max-model-len=16384
  • Roles are restricted to user and assistant; any other role, including system inside messages, is a client error naming the offending role
  • stop_sequences becomes the canonical stop sequences, and stream becomes the canonical streaming flag
  • tools, tool_choice, any tool_use or tool_result block, any image block, any cache-control marker, and any user message with more than one content block are rejected as unsupported, naming the feature; none is silently dropped
  • Unknown top-level fields are rejected rather than ignored, so a client cannot believe an unimplemented option took effect
  • Reading the body respects the route's configured body size cap

max_tokens stays optional on /v1/chat/completions. The policy is per-surface: each dialect keeps its own contract, and the canonical request records whichever value resulted.

Verify

curl -s -o /dev/null -w '%{http_code}\n' localhost:8080/llm/v1/messages \
  -H 'content-type: application/json' \
  -d '{"model":"reasoning","max_tokens":64,"system":"be terse","messages":[{"role":"user","content":"hi"}]}'
# expected: 200, reasoning stub hit, system text present in the upstream request

curl -s localhost:8080/llm/v1/messages -H 'content-type: application/json' \
  -d '{"model":"reasoning","messages":[{"role":"user","content":"hi"}]}'
# expected: 400, body names max_tokens as the missing required field, no upstream stub hit

curl -s localhost:8080/llm/v1/messages -H 'content-type: application/json' \
  -d '{"model":"reasoning","max_tokens":8,"messages":[{"role":"system","content":"x"}]}'
# expected: 400, body names the rejected role, no upstream stub hit