Repository navigation
fix(serve): accept absent/null assistant content, max_completion_tokens alias, gate the GGUF router (supersedes #5411) - #5544
Conversation
…(PMAT-801 follow-up) OpenAI-compatible clients (e.g. the pmcp-agent SDK loop) omit `content` on the assistant tool-call turn they echo back in the next request, which the OpenAI API permits. ChatMessage.content was REQUIRED on deserialize, so turn 2 of every tool loop was answered with HTTP 422 (`missing field content`). - ChatMessage deserializes through a private ChatMessageWire (serde try_from); an absent or null `content` on an ASSISTANT message reads as "". - Every other role (system, user, tool) still refuses a missing/null content, and a non-string content is still a type error. - ChatMessage keeps `content: String` and its Serialize derive, so every reader and the response bytes are unchanged (pinned by a byte-identity test). - Tests over the exact wire-log request that returned 422. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
OpenAI SDK >= 1.40 clients send `max_completion_tokens`, the newer name for the generation cap. The unknown key was silently ignored, so the cap was lost and generation ran to the server default. - `max_tokens` gains `alias = "max_completion_tokens"` (one serde attribute). - A body carrying BOTH names is refused by serde with `duplicate field` (deterministic 422, no value silently wins); pinned by a test. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…UTH-001) - add build_gguf_router(state, config, auth_gate): realizar's router behind auth::layer, /health included - serve_router now builds through it with AuthGate::from_env(); APR_API_KEY_HASH was ignored on the CPU GGUF / Qwen3.5 path - four oneshot tests on a model-free AppState: 401 without/with wrong key, admitted with the right key, disabled gate open Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t does not exist and called the CPU path the only ungated router Quorum #5451 (haiku) finding; the GPU/CUDA/APR GPU routers are still ungated and the comment now says so. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…b Adapter; name the ungated GPU starters exactly Quorum #5451 round 2 findings (comment truth only). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…fallback overclaim Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ed-gate test really exercises Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…odel-free handler gives, not just 'not 401' Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
§13.11 rung 1 — quorum shadow verdict Shadow mode: this records a verdict and merges nothing. A refusal |
noahgift
left a comment
There was a problem hiding this comment.
NO SEAT, defect at c6d9da8 (aprender-pace, non-author).
Code diff reads fine: ChatMessageWire/TryFrom (assistant-only optional content, other roles still refused), the max_completion_tokens alias, the GGUF router behind the Bearer gate, and the tests. I did not run them.
Defect: contracts/contracts.nt drops 306 triples for 46 contracts whose YAML still exists at this head (e.g. backward-cpu-staging-v1, vram-guard-v1, wgpu-burn-canary-v1, every wgpu-* and vecmat-blis-gemv-v1). The PR's only intended .nt change is the new falsification line. Base e000347 is current main. git diff origin/main <head> -- contracts/contracts.nt shows 306 deletions and 0 for the intended addition. The file shrinks from 35478 to 35172 lines, and backward-cpu-staging-v1 goes from 6 mentions to 0.
Likely a regen run on a tree missing contracts/entrenar/kaizen and other subdirs. Fix: re-run the regen from a full checkout (make readme-sync / the .nt generator), and confirm the diff to the .nt is only the additions for apr-serve-openai-compat-v1 FALSIFY-CHAT-CONTENT-OPTIONAL-5411. I will seat once the .nt is clean.
|
seat 1, aprender-w5018 @c6d9da897874759d9d7da69c930bb67d6b90c0d4 APPROVE. Read the full diff at this head and ran the new tests on a detached checkout of it: aprender-serve
🤖 Generated with Claude Code |
The previous extraction dropped 306 triples from 46 kaizen contracts. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
noahgift
left a comment
There was a problem hiding this comment.
seat 1, aprender-w5018 @99c084ae576a8f2ad5679be3877b75550271d44d
Re-seat. Delta c6d9da8..99c084a touches only contracts/contracts.nt: 306 added triple lines, 0 removed, no source or contract-YAML change, so my earlier read and test runs (aprender-serve 8/8, apr-cli gguf_router_auth 4/4 at c6d9da8) still apply. No defect.
🤖 Generated with Claude Code
noahgift
left a comment
There was a problem hiding this comment.
seat 1, aprender-pace @99c084ae576a8f2ad5679be3877b75550271d44d
Non-author (0 aprender-pace commits). My earlier defect is fixed: against its merge-base, the head has no contracts/contracts.nt change, so the 306 dropped triples are gone. The head changes only the contract YAML (+2 falsification entries) and four crates files (+402/-11).
Read in full:
- ChatMessageWire plus TryFrom: only an assistant message may omit or null
content; system, user and tool are refused withmissing field content. A non-string is still a type error, tested for four roles. Serialization is still the plain derive, with a byte-identical test. alias = max_completion_tokens: with both names present, serde refuses withduplicate field. This is deterministic and tested.- build_gguf_router puts the Bearer gate on the real router,
/healthincluded. Tests cover no bearer, a wrong bearer, the right bearer reaching the handler (503 No model available, which only a handler returns) and a disabled gate. The doc comment lists the GPU/CUDA/APR starters as still ungated follow-ups, which I accept as stated scope.
APPROVE. I did not run the tests, and the checks showed no FAILURE. Not armed.
Supersedes #5411 (fork PR by @guyernest). His commits are kept in this branch's history; this adds the standard on top: fmt, clippy -D warnings on aprender-serve and apr-cli (--lib), and two falsification rows in contracts/apr-serve-openai-compat-v1.yaml (v1.24.0).
contentabsent ornullparse as "" (OpenAI allows it on the tool-call turn); system/user/tool still require it; serialization bytes unchanged.max_completion_tokensaliasesmax_tokens; both together is a serdeduplicate fieldrefusal.AuthGate::from_env()(FALSIFY-AUTH-001).Verified at this head:
cargo test -p aprender-serve --lib chat_message_content5 passed;... max_completion_tokens3 passed;cargo test -p apr-cli --lib gguf_router_auth4 passed; the whole fork diff (4 Rust files) was read first: no build.rs, scripts, workflows, network or secrets.Refs #5411. #5411 is not closed by this PR; it is closed by hand after this one shows MERGED.
🤖 Generated with Claude Code