Files
code_of_conquest_dnd/api
Phillip Tarrant f7c51b79da fix(api): best-effort call logging + non-JSON 200 → ModelError (final-review)
- call_log._default_write: swallow any exception from the default file/
  stdout sink and report to stderr, so a log-write failure (disk full,
  bad permissions, misconfigured CALL_LOG_PATH) never turns a successful
  narration into a 500 (charter §13). Injected write= sinks (used by
  tests) are left to surface their own errors.
- ollama_client.chat: catch ValueError alongside httpx.HTTPError in the
  retry loop so a 200 response with a non-JSON body (JSONDecodeError is
  a ValueError subclass) counts as a failed attempt and falls through to
  ModelError after the one retry, instead of escaping chat() uncaught
  (charter §12 — one retry, then ModelError, nothing else escapes).
- main.py: update stale docstrings — /dm/narrate is now fully wired
  (routing + model call + logging); the other four roles remain stubs.

Regression tests added for both fixes (TDD: watched RED, then GREEN).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 08:45:39 -05:00
..
2026-07-09 11:17:54 -05:00
2026-07-09 11:17:54 -05:00

/api — FastAPI proxy

The guarding proxy from charter §4. Not a generic API — it exists to keep the API key, the prompts, and the model routing server-side, and to log every call from day one.

Stack: Python, FastAPI. Models: Ollama (dev/staging) → Replicate (prod), routed per-role server-side.

Role endpoints (charter §4)

POST /dm/narrate      Narrator    — scene/outcome prose        (good model)
POST /dm/adjudicate   Adjudicator — free text → legal action   (small/fast, strict JSON)
POST /dm/improvise    Improviser  — minor sandboxed event       (charter §7 limits)
POST /npc/speak       NPC         — voice one character          (bounded moves, §6)
POST /party/banter    Banter      — companion callbacks          (cacheable, §9)

The client does not know which model serves a role or what the prompt is.

Responsibilities

  • Auth · metering (retrofit later — middleware + Stripe webhook, §4)
  • Prompt ownership — prompts live in prompts/, never in the client
  • Model routing — role → model is config, a deploy not a client patch
  • Logging — log seed and full prompt with every call (§10) for replay/eval

Contracts

Every AI response is untrusted (§2). Parse, validate, be ready to discard. One retry on parse failure, then authored fallback (§12).

See docs/ for endpoint schemas, model routing, and deploy notes.