Deployed model: Qwen3.8-Flash-Next on the p910-network vLLM host
(docs/model-backend-baseline-2026-09-22.md). Thinking is billed against the same
completion budget as the answer, and live notes show thinking-only turns ending
blank with finish_reason=stop — so a blanket 4096-token thinking allowance is both
slow for greetings and still not safe for hard questions. peterbot/conversation.py
instead bounds the turn with three tiers. The tier bounds the generation shape
only: the model normally decides whether to call use_tools. One output
postcondition prevents a clean text reply from claiming an explicitly requested
attachment or sandbox execution; that request is handed to the worker instead.
| tier | trigger (shape, not topic) | thinking | budget tokens | thinking cap | effort |
|---|---|---|---|---|---|
| casual | short greeting/banter, ≤2 context turns | off | 512 | — | — |
| normal | explain/compare/how-does style questions, long or multi-? messages |
on | 2048 | 1024 | low |
| deep | current facts, research, code/files/club-state verbs, attachments, explicit depth requests | on | 4096 (follows inference.max_tokens, clamped 4096–8192) |
2048 | low |
- Thinking stays on for normal/deep research and ambiguous work. An explicit request to attach files or execute code in the sandbox uses a 512-token non-thinking first attempt. Five idle coding probes all made valid tool calls with p95 2.462 s, versus 44.173 s with thinking. The clean-text postcondition still sends such a request to the worker.
reasoning_effortis only ever sent together withenable_thinking: true; the served vLLM build rejects the combination otherwise. A capped thinking budget therefore always pairs with an effort value.- A non-thinking rescue attempt (blank nudge or continue-prompt) follows any blank, truncated, or malformed first attempt.
One budget covers the whole turn: budget_seconds (scheduler remaining deadline)
or inference.timeout_seconds.
- Thinking attempt:
min(max(remaining − 130 s reserve, remaining/2), 300 s). The 130 s reserve guarantees the rescue can still answer after the model thinks for three minutes. - Rescue attempt: ≤120 s, and never started with <10 s left.
max_tokensis additionally capped atremaining × 20 tok/s(served host does ~40 tok/s aggregate; 20 is the conservative half) so an oversized call never starts near the deadline.- Nothing runs with <5 s left; the turn then returns the safe blank line instead of burning the gateway deadline.
use_toolsauthorizes a handoff only with one call, well-formed{"reason": …}arguments, and a completion finish marker (tool_calls/stop).finish_reason=length/content_filter, a missing finish marker, malformed or invented tool arguments ⇒ retry without thinking, never a handoff, never member-visible.- A clean text promise cannot satisfy an explicit request to attach source/files or compile and test in Peter's sandbox. That answer hands off to real work; malformed tool output still does not authorize a handoff.
- Transport failure on every attempt ⇒
ValueError(MODEL_UNAVAILABLE_REPLY); two blank completions ⇒ the canned retry-line. A ≥40-char truncated answer is kept as a last resort rather than replaced by a canned line. - Reasoning text is never returned to a member.
reply_or_use_tools(..., has_attachments=..., budget_seconds=...) are additive
kwargs. The trusted gateway passes fresh club_context, style_instruction,
durable scoped context, and the request's remaining conversation time to the
model. /ask uses what remains after its history fetch. Attached files go
directly to the isolated work path, so they never enter a casual model turn.
The staged P910 configuration sets ordinary conversation requests to 90 seconds
and substantial work to 600 seconds; live timing remains a release gate.
deploy/probe_model_compat.py runs synthetic prompts only and reports timings,
usage, retries, and route validity (never prompt or reasoning text). --metrics-url
samples vllm:num_requests_running/waiting and KV usage around each row so loaded
samples cannot masquerade as idle ones.
The September 23 voice update answers a bare greeting locally after trusted
Discord admission, with yo or whats good and no model call. The greeting
probe below remains a dated measurement of the previous model-routed path;
ordinary questions still use the tiered model path.
2026-09-23 ~04:20 UTC, host 100.73.210.66:8000, one repeat per row, retries 0, malformed chunks 0.
| case | mode | route (expected) | first useful s | total s | completion/reasoning tok | finish | valid args |
|---|---|---|---|---|---|---|---|
| greeting | none | answer (answer) ✓ | 1.30 | 1.97 | 18/0 | stop | — |
| greeting | low | answer (answer) ✓ | 2.39 | 3.71 | 77/37 | stop | — |
| research | none | handoff (handoff) ✓ | 1.36 | 3.75 | 68/0 | tool_calls | 1 |
| research | low | handoff (handoff) ✓ | 2.97 | 5.08 | 114/42 | tool_calls | 1 |
On September 23, five repetitions per case used the deployed
Qwen3.8-Flash-Next vLLM endpoint and sampled its running/waiting gauges
before every request. All samples began with 0 running and 0 waiting. The
first useful signal is the first answer token or tool-call delta; total time
includes completion and transport. No row retried, failed routing, or contained
a malformed stream chunk.
| case | thinking | valid route | first useful p50/p95 | total p50/p95 | completion tokens, range |
|---|---|---|---|---|---|
| greeting | off | 5/5 answer | 1.270 / 1.295 s | 2.007 / 2.028 s | 21–33 |
| factual | low | 5/5 answer | 1.857 / 1.944 s | 3.079 / 3.387 s | 86–131 |
| research | low | 5/5 handoff | 2.462 / 2.598 s | 3.550 / 3.765 s | 86–115 |
| coding, previous shape | low | 5/5 handoff | 13.347 / 43.085 s | 14.441 / 44.173 s | 529–1998 |
| coding, explicit-work shape | off | 5/5 handoff | 1.469 / 1.483 s | 2.398 / 2.462 s | 48–57 |
The proposed banter target was p50 ≤2 s, p95 ≤5 s: measured p50 missed by 0.007 s while p95 passed. Factual p95 was under the proposed 15 s. Research and explicit coding handoffs were under the usual 5 s and 8 s cutoff proposals in these five samples. These are model-only timings, excluding Discord delivery, queue delay, and worker execution; they are not service-level guarantees.
Accepted control surface, verified against the served build: enable_thinking
false/true, reasoning_effort=low with thinking, and
chat_template_kwargs.thinking_budget; tool deltas arrive with
finish_reason=tool_calls and parseable {"reason":…} args under both thinking
modes. The non-thinking research sample also routed correctly, but one sample does
not overturn the earlier live no-thinking failures, so deep keeps thinking per the
PETER-05 brief.
The raw JSONL probe results contain timing and token metrics but no prompts or reasoning text. The first two runs preceded the explicit-work profile change. The non-thinking coding run was an isolated compatibility/latency test before that profile was deployed.