chore: initial commit of Go API gateway
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

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]>
This commit is contained in:
Story Crater Bot
2026-08-19 20:54:34 -07:00
co-authored by Claude Opus 5
commit 058f11cf2b
109 changed files with 8992 additions and 0 deletions
@@ -0,0 +1,50 @@
# 2.11 — Anthropic non-streaming response translation (GREEN)
Phase: 2 — LLM surfaces
Stage: GREEN
Depends on: [2.10](2.10-anthropic-request-translation.md), [2.9](2.9-canonical-request-model.md)
A non-streaming `POST /llm/v1/messages` gets an Anthropic Messages response, built
from whatever the upstream returned. Upstreams speak the OpenAI chat-completion shape;
the client on this surface must never see it.
- [ ] The response body is `{"id","type":"message","role":"assistant","content":[...],
"model","stop_reason","stop_sequence","usage":{"input_tokens","output_tokens"}}`
with `type` literally `message` and `role` literally `assistant`
- [ ] `model` echoes the model name the client sent, not an upstream-internal name
- [ ] Upstream `finish_reason` `stop` becomes `stop_reason` `end_turn`, and `length`
becomes `max_tokens`
- [ ] A generation halted by a client-supplied stop sequence reports `stop_reason`
`stop_sequence` and puts the matched string in `stop_sequence`; otherwise
`stop_sequence` is null and present, not omitted
- [ ] Upstream `prompt_tokens` becomes `usage.input_tokens` and `completion_tokens`
becomes `usage.output_tokens`; no other usage fields are invented
- [ ] `reasoning_content`, which vLLM returns as a field separate from `content` for
`reasoning`, becomes a `thinking` content block that PRECEDES the `text` block
- [ ] When `reasoning_content` is absent or empty, no `thinking` block is emitted and
`content` holds only the `text` block
- [ ] When `content` is empty but `reasoning_content` is not, the `thinking` block is
still returned rather than an empty `content` array
- [ ] `Content-Type` is `application/json`, and no OpenAI field name such as `choices`,
`finish_reason` or `object` appears anywhere in the body
`reasoning` runs DeepSeek-R1-Distill-Qwen-32B under vLLM with
`--reasoning-parser=deepseek_r1`, which is why the reasoning text arrives as its own
field and maps cleanly onto a thinking block.
## Verify
```bash
curl -s localhost:8080/llm/v1/messages -H 'content-type: application/json' \
-d '{"model":"reasoning","max_tokens":64,"messages":[{"role":"user","content":"hi"}]}'
# expected: 200, type=message, role=assistant, content[0].type=thinking, content[1].type=text
curl -s localhost:8080/llm/v1/messages -H 'content-type: application/json' \
-d '{"model":"reasoning","max_tokens":64,"messages":[{"role":"user","content":"hi"}]}' \
| grep -c -E '"choices"|"finish_reason"|"object"'
# expected: 0
curl -s localhost:8080/llm/v1/messages -H 'content-type: application/json' \
-d '{"model":"qwen2.5:3b-instruct","max_tokens":4,"messages":[{"role":"user","content":"count to fifty"}]}'
# expected: 200, stop_reason=max_tokens, usage has input_tokens and output_tokens only
```