Adds a provider-priority abstraction (NVIDIA Nemotron 3 Ultra first,
OpenRouter/Gemma 4 fallback) and fixes several real issues found while
testing it live:
- Separate reasoning_content from content in streamed responses --
they were being concatenated, leaking the model's raw chain-of-thought
into what the player saw.
- Tune reasoning_budget/max_tokens down from 16384 (both equal, leaving
zero guaranteed headroom for content) to 1024/1536, based on real
observed usage of 55-175 tokens per turn.
- Detect truncation via finish_reason == "length": NVIDIA flushes
in-progress reasoning into the final content chunk as a compatibility
fallback when cut off, so content can't be trusted even when non-empty.
- Retry transient "Worker local total request limit reached" errors
(confirmed real via NVIDIA forum reports, not our request rate) with
bounded backoff, verified against both retryable and non-retryable
simulated failures.
- Rolling context summarization: fold older turn…
fix: stop DM from stacking dice-roll requests without resolving prior ones
The free-tier Gemma 4 model kept demanding a new check in the same
reply that resolved the previous one, leaving confused/new players
with no breathing room. Explicit numbered rules (name the die inline,
resolve-then-stop-and-ask, no plot escalation on rules questions)
fix this reliably -- verified against the real reported conversation
via app.get_dm_response before committing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feat: switch DM backend from Mistral API to Gemma 4 via OpenRouter
Avoids ZeroGPU/local-inference costs while moving to an open-weight
model: OpenRouter's free-tier google/gemma-4-26b-a4b-it:free, called
through the OpenAI-compatible client. Verified with a real completion
call before committing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
docs: add CLAUDE.md with project architecture and version-pin rationale
gr.Chatbot dropped the tuples format entirely in favor of messages
(list of {role, content} dicts) as the only supported shape. Update
respond()/get_dm_response() accordingly - this also simplifies the
Mistral message-building since the shapes now match directly.
Verified locally: multi-turn conversation runs end-to-end and
gr.Chatbot.postprocess() accepts the resulting history with no
UserWarning raised (checked with warnings escalated to errors).
fix: satisfy HF ZeroGPU startup check with a no-op @spaces.GPU function
Free-tier Spaces default to ZeroGPU hardware and refuse to start
without at least one @spaces.GPU-decorated function, even though this
app does no local GPU work (it only calls the Mistral API). Add a
no-op function called once at import time so the startup check passes
without burning per-request GPU quota on real chat/dice traffic.
feat: add Logfire tracking/tracing for observability
- logfire.configure() with send_to_logfire="if-token-present" so it's
a safe no-op locally without a token, and instrument_httpx() to
auto-capture every Mistral API call (latency, status, errors).
- Wrap get_dm_response in a span and log exceptions instead of only
returning them as a chat string, since friends using the Space
won't otherwise surface failures anywhere visible.
- Document LOGFIRE_TOKEN in .env.example.
Verified locally end-to-end: traces show up in the Logfire project
both with and without a token present.
fix: update app.py for mistralai 2.7.2 and gradio 6.20.0 APIs
- mistralai: MistralClient/ChatMessage were removed; use Mistral client
with client.chat.complete() and plain role/content dicts. Also swap
the now-invalid 'mistral-medium' model id for 'mistral-medium-latest'.
- gradio 6: Blocks() no longer accepts theme (moved to launch()), and
Textbox dropped show_copy_button in favor of buttons=["copy"].
Verified locally: module imports cleanly, a real Mistral API call
succeeds, and the Gradio server launches and serves 200.
chore: add HF Spaces frontmatter to README, pin gradio to match sdk_version
Space config (colorFrom/colorTo/sdk_version/python_version) now
matches what's actually resolved locally. gradio is pinned rather
than dropped so local dev installs stay working.
fix(deploy): fetch full history so push to HF Space isn't shallow
ssh-keyscan against hf.co fails (exit 1) both locally and in CI,
killing the Setup SSH step under bash -e. A real SSH handshake with
StrictHostKeyChecking=accept-new succeeds and returns the same key
every time, so pin that verified key directly.
Added ssh key and changed deployment path to space.