Skip to main content

memra_server/
lib.rs

1//! memra-server (BASE-4): a minimal OpenAI-ish HTTP server that serves 2-4 concurrent agents across
2//! DIFFERENT models on one endpoint via a single GPU worker thread + step-interleave scheduler.
3//!
4//! Architecture (see worker.rs): axum runs on a tokio runtime; ONE dedicated std::thread owns the
5//! Engine + every loaded HybridModel (CUDA context is thread-affine). Handlers submit `Cmd`s over a
6//! std mpsc channel and receive tokens back over a per-request tokio mpsc channel.
7//!
8//! Endpoints (the full set — `router()` below is the authority):
9//!   GET  /health, GET /livez     -> the SAME handler (`health_live`): INFERENCE liveness, not
10//!                                     process liveness. {"status":"ok"|"draining"|"unhealthy",
11//!                                     "models":[...], "worker":{phase, beat_age_ms, tick_max_ms,
12//!                                     stall_threshold_ms, generation, xid_warnings}} + a
13//!                                     top-level "detail" on a red. Draining stays 200; dead /
14//!                                     GPU-faulted / stalled / loading is 503 (serve-hardening
15//!                                     2026-08-06).
16//!   GET  /readyz                 -> routability, same payload shape with
17//!                                     "status":"ready"|"not_ready". Unready is NOT a restart
18//!                                     request — draining and loading are healthy-but-unroutable.
19//!   GET  /models                 -> {"data":[{"id":name},...]}  (OpenAI-ish);
20//!                                     ?schema=openrouter -> Provider Monitor schema 2.4,
21//!                                     ?schema=openmodels -> OpenModels provider feed.
22//!   GET  /v1/models              -> existing catalog-style model list (context_length,
23//!                                     architecture, pricing stub, top_provider; serve-tail).
24//!   GET  /metrics                -> flat serving counters + step latency percentiles.
25//!   GET  /yield/metrics          -> per-lane x-lane QoS counters + engine-truth step p50/p99
26//!                                     (lane/qos-p95 2026-08-02).
27//!   POST /v1/completions         -> {model,prompt|prompt_ids,max_tokens,temperature?,top_p?,top_k?,
28//!                                     seed?,stop?,chat?,stream?,cache_salt?}. stream=true => SSE
29//!                                     token-by-token; else a single JSON {text,tokens,stop_reason}.
30//!   POST /v1/chat/completions    -> OpenAI chat messages rendered by the GGUF chat template;
31//!                                     OpenAI message/chunk response shapes. `tools`/`tool_choice`
32//!                                     (auto|none) + role:"tool" turns render through the
33//!                                     template's own <tools> branch; emitted <tool_call> blocks
34//!                                     parse into OpenAI `tool_calls` (+"tool_calls" finish);
35//!                                     `reasoning_effort`/`reasoning` map onto the template's
36//!                                     think switch (serve-tools lane, 2026-08-02).
37//!
38//! CONFIG: MEMRA_MODELS="name=/path.gguf[+/draft.gguf],name2=hf:owner/repo,name3=/hf_ckpt_dir"
39//! (comma-separated; `+draft.gguf` attaches that model's regime draft — docs/DRAFT-REGIME.md).
40//! A model path may be a GGUF file OR an HF safetensors checkpoint directory
41//! (config.json + model.safetensors[.index.json] — the run-safetensors load path; serve-st
42//! lane 2026-08-04). Defaults to the BASE-4 test pair (main=27B, judge=9B) if unset.
43//! MEMRA_ADDR sets the bind addr.
44//!
45//! LIFECYCLE: SIGTERM = graceful drain (gap-scan F11) — new completion requests 503 with
46//! Retry-After, /health reports "draining", in-flight requests (streams included) finish
47//! up to MEMRA_DRAIN_S (default 30s), then the process exits 0. Completion responses carry
48//! X-RateLimit-Limit/-Remaining/-Reset (concurrency-slot semantics; gap-scan F12).
49
50/// x-lane QoS (lane/dl-metering gate, QoS-only extraction 2026-08-02): lane types, SLO
51/// admission policy, engine-truth step stats live in the memra-lanes crate so out-of-process
52/// controllers (the sidecar shape) can share them.
53///
54/// `pub`: the key file format, lifecycle helpers, and single-key path are the API a
55/// deployment-owned binary provisions against (engine-billing-extraction-20260829).
56pub mod auth;
57pub(crate) mod constrained;
58/// Dead-darklane background jobs (lane/darklane-training, 2026-08-07): valley detection over
59/// worker truth (phase + beat age + pending admits) and a yield-first background job runner —
60/// a lane class BELOW every serving lane. Engine mechanics only; policy lives product-side.
61pub(crate) mod darklane;
62/// Inference-liveness state (lane/serve-hardening, gaps G5 + G24): the worker heartbeat every
63/// health answer is derived from, the Xid/GPU-fault watcher, and the sd_notify half of the
64/// systemd contract. Process liveness is NOT inference liveness — this module is the difference.
65pub(crate) mod health;
66pub(crate) mod lanes {
67    pub use memra_lanes::*;
68}
69/// Predictive-admission SHADOW instrumentation (darklanes Arc D2 engine gaps,
70/// lane/d2-engine-gaps-20260831): the per-model in-flight book, the rolling
71/// completion-length history, and the `[admit-predict]` receipt line behind
72/// `MEMRA_ADMIT_PREDICT_SHADOW` (default 0). Logs verdicts, never enforces.
73mod admit_predict;
74/// CPU affinity for the GPU worker thread (`MEMRA_WORKER_CPUSET`, alias
75/// `MEMRA_WORKER_AFFINITY` honored, default OFF —
76/// lane/glm5-host-audit 2026-09-01). Engine-wide, not one family's: every served family's
77/// decode tick runs on the single `memra-gpu-worker` thread this module can pin, and that
78/// thread was measured migrating across L3 domains on a 12-CCD EPYC while 192 unpinned tokio
79/// workers shared the same CPUs. Machine config, so it defaults OFF and stays a seam.
80mod affinity;
81/// Translation surfaces (lane/api-surfaces, 2026-08-17): the Anthropic Messages API and
82/// the OpenAI Responses API served over the SAME chat-completions core — same tenant
83/// auth, budget admission, ledger receipts, metering and capture posture; only the wire
84/// rendering differs. `surfaces` is the shared admission driver; the other two are the
85/// per-dialect request translations and response renderers.
86mod anthropic;
87/// The `system_fingerprint` identity, shared with `build.rs` (which `include!`s this same
88/// file to bake the value). Compiled into the crate so the fingerprint tests can re-derive
89/// the id from the working tree instead of pinning a second copy of the algorithm.
90#[allow(dead_code)] // one implementation, two callers: each uses a subset.
91mod build_id;
92mod dsv4_serve;
93mod embed_api;
94/// The admission/accounting seam: the server admits, denies, and reports counts;
95/// what admission MEANS — budgets, prices, tenancy policy — is a deployment concern,
96/// supplied behind `metering::Metering` through `ServerWiring`. The stock binary
97/// ships NO accounting (only the engine is open; the business tier lives in the
98/// deployment's own binary — engine-billing-extraction-20260829, owner razor
99/// 2026-08-29: "only engine is open, business is private").
100pub mod metering;
101mod responses_api;
102mod surfaces;
103mod toolcall;
104mod ttft;
105mod worker;
106
107use std::collections::HashMap;
108use std::net::{SocketAddr, ToSocketAddrs};
109use std::sync::mpsc::Sender;
110use std::sync::{Arc, Mutex};
111
112use axum::{
113    Extension, Json, Router,
114    body::Body,
115    extract::{DefaultBodyLimit, FromRequest, Query, Request as AxumRequest, State},
116    http::{
117        HeaderMap, StatusCode,
118        header::{CONTENT_LENGTH, CONTENT_TYPE, TRANSFER_ENCODING},
119    },
120    middleware::{self, Next},
121    response::{
122        IntoResponse, Response,
123        sse::{Event as SseEvent, Sse},
124    },
125    routing::{get, post},
126};
127use futures_core::Stream as _;
128use serde::de::DeserializeOwned;
129use serde::{Deserialize, Serialize};
130use serde_json::json;
131use tower::ServiceExt as _;
132
133use memra_engine::decode::GenParams;
134use memra_engine::sampler::SamplerConfig;
135use memra_tokenizer::{
136    Tokenizer,
137    chat::{self, ThinkMode, ToolCall as TmplToolCall, Turn as TmplTurn},
138};
139use toolcall::{ParsedToolCall, Piece, ToolStreamParser};
140use worker::{Cmd, Event, ModelCaps, Request, SharedMetrics};
141
142/// Explicit HTTP body ceiling for every inference route (hermes finding, 2026-08-19).
143/// axum's DefaultBodyLimit is 2 MiB, which silently capped the ADVERTISED surface: a
144/// 262,144-token prompt sent as `prompt_ids` is ~2.8 MiB of JSON on its own, and the
145/// vision envelope (base64 data URIs) is far past that — sold features died at the
146/// extractor with a shapeless 413. Budget, itemized from the advertised maxima:
147///
148///   prompt   262,144 tokens x 16 B/token JSON-escaped upper bound     =   4 MiB
149///   images   VISION_MAX_IMAGES (8) x 12 MiB raw x 4/3 base64          = 128 MiB
150///   videos   2 x 12 MiB raw GIF x 4/3 base64                          =  32 MiB
151///   message/tools envelope headroom                                    =   4 MiB
152///                                                            requirement 168 MiB
153///
154/// Ceiling: 192 MiB — covers the requirement with headroom while staying finite (the
155/// per-lane concurrency slots bound how many of these can buffer at once). Applies to
156/// EVERY route on the app router, including `/v1/messages`' raw `Bytes` path (the
157/// `DefaultBodyLimit` extension reaches `Bytes` and `Json` extractors alike).
158///
159/// The "12 MiB raw" per-image line item is ENFORCED, not just budgeted: both data-URI
160/// decoders (`vision_pre::decode_data_uri`, `vision_gemma::gemma_decode_data_uri`)
161/// refuse a payload past `vision_pre::IMG_MAX_RAW_BYTES` by encoded LENGTH, before any
162/// decode allocation, with a named 400 (hermes review finding 48f96cb4cd37e436: until
163/// then only this body ceiling bounded the decode, which runs in the content walkers
164/// BEFORE slot admission, so one image could expand ~144 MiB of host bytes pre-check).
165const MAX_BODY_BYTES: usize = 192 * 1024 * 1024;
166const MAX_BODY_ADMISSIONS: usize = 4;
167const MAX_SMALL_BODY_ADMISSIONS: usize = 32;
168// Small JSON requests are already bounded by the extractor and should not wait behind a
169// deliberately slow large upload. They use their own finite pool; unknown-length/chunked bodies
170// still take the large-body path.
171#[allow(clippy::identity_op)] // allow: the explicit +0/*1/>>0 terms document the lane/byte symmetry of the reference layout
172const BODY_ADMISSION_BYPASS_BYTES: usize = 1 * 1024 * 1024;
173const BODY_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(90);
174const BODY_READ_RATE_BYTES_PER_SEC: u64 = 2 * 1024 * 1024;
175const BODY_READ_TIMEOUT_MAX: std::time::Duration = std::time::Duration::from_secs(180);
176const BODY_IDLE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
177const BODY_ADMISSION_RETRY_AFTER_S: u64 = 1;
178const MAX_STOP_SEQUENCES: usize = 16;
179const MAX_STOP_SEQUENCE_BYTES: usize = 1_024;
180const MAX_STOP_SEQUENCES_BYTES: usize = 4 * 1_024;
181const MAX_CLIENT_IDENTIFIER_BYTES: usize = 256;
182const MAX_HTTP_CONNECTIONS: usize = 1_024;
183const MAX_HTTP2_STREAMS_PER_CONNECTION: u32 = 128;
184const HTTP1_HEADER_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
185const HTTP_CONNECTION_MAX_LIFETIME: std::time::Duration = std::time::Duration::from_secs(300);
186
187fn body_admission_semaphore() -> Arc<tokio::sync::Semaphore> {
188    static SEMAPHORE: std::sync::OnceLock<Arc<tokio::sync::Semaphore>> = std::sync::OnceLock::new();
189    SEMAPHORE
190        .get_or_init(|| Arc::new(tokio::sync::Semaphore::new(MAX_BODY_ADMISSIONS)))
191        .clone()
192}
193
194fn small_body_admission_semaphore() -> Arc<tokio::sync::Semaphore> {
195    static SEMAPHORE: std::sync::OnceLock<Arc<tokio::sync::Semaphore>> = std::sync::OnceLock::new();
196    SEMAPHORE
197        .get_or_init(|| Arc::new(tokio::sync::Semaphore::new(MAX_SMALL_BODY_ADMISSIONS)))
198        .clone()
199}
200
201#[derive(Clone)]
202pub(crate) struct BodyAdmissionGuard {
203    permit: Arc<Mutex<Option<tokio::sync::OwnedSemaphorePermit>>>,
204}
205
206impl BodyAdmissionGuard {
207    fn new(permit: tokio::sync::OwnedSemaphorePermit) -> Self {
208        Self {
209            permit: Arc::new(Mutex::new(Some(permit))),
210        }
211    }
212
213    pub(crate) fn release(&self) {
214        if let Ok(mut permit) = self.permit.lock() {
215            permit.take();
216        }
217    }
218}
219
220pub(crate) struct BodyAdmissionLease(Option<BodyAdmissionGuard>);
221
222impl BodyAdmissionLease {
223    fn release(&mut self) {
224        if let Some(admission) = self.0.take() {
225            admission.release();
226        }
227    }
228
229    pub(crate) fn guard(&self) -> Option<&BodyAdmissionGuard> {
230        self.0.as_ref()
231    }
232}
233
234impl Drop for BodyAdmissionLease {
235    fn drop(&mut self) {
236        self.release();
237    }
238}
239
240pub(crate) struct AdmittedJson<T>(pub(crate) T, pub(crate) BodyAdmissionLease);
241
242#[axum::async_trait]
243impl<S, T> FromRequest<S> for AdmittedJson<T>
244where
245    S: Send + Sync,
246    T: DeserializeOwned,
247{
248    type Rejection = axum::extract::rejection::JsonRejection;
249
250    async fn from_request(req: AxumRequest, state: &S) -> Result<Self, Self::Rejection> {
251        let admission = req.extensions().get::<BodyAdmissionGuard>().cloned();
252        let parsed = Json::<T>::from_request(req, state).await;
253        parsed.map(|Json(value)| Self(value, BodyAdmissionLease(admission)))
254    }
255}
256
257fn declared_body_length(req: &AxumRequest) -> Option<usize> {
258    req.headers()
259        .get(CONTENT_LENGTH)
260        .and_then(|value| value.to_str().ok())
261        .and_then(|value| value.parse().ok())
262}
263
264fn body_requires_admission(req: &AxumRequest) -> bool {
265    // A transfer-encoding header means the wire length is not bounded by Content-Length (and a
266    // conflicting pair must take the conservative path), so chunked/unknown bodies never bypass
267    // the large-upload gate.
268    if req.headers().contains_key(TRANSFER_ENCODING) {
269        return true;
270    }
271    declared_body_length(req).is_none_or(|length| length > BODY_ADMISSION_BYPASS_BYTES)
272}
273
274/// Keep the body parser bounded without making the documented 192 MiB envelope require an
275/// implausibly fast uplink. The base is still a strict deadline for unknown-length bodies; a
276/// declared length earns a pessimistic 2 MiB/s transfer budget, capped at three minutes.
277fn body_read_timeout(req: &AxumRequest) -> std::time::Duration {
278    let Some(length) = declared_body_length(req) else {
279        return BODY_READ_TIMEOUT;
280    };
281    let bytes = length as u64;
282    let extra_seconds =
283        bytes.saturating_add(BODY_READ_RATE_BYTES_PER_SEC - 1) / BODY_READ_RATE_BYTES_PER_SEC;
284    let seconds = BODY_READ_TIMEOUT
285        .as_secs()
286        .saturating_add(extra_seconds)
287        .min(BODY_READ_TIMEOUT_MAX.as_secs());
288    std::time::Duration::from_secs(seconds)
289}
290
291/// Reshape the extractor-produced 413 (a plain-text axum rejection) into the standard
292/// OpenAI error object every SDK parses. Runs OUTSIDE the routes so both the
293/// content-length refusal and the mid-read stream cutoff surface identically: a clean
294/// HTTP 413 with our JSON shape — never a hang, never a bare connection reset.
295async fn shape_payload_too_large(req: AxumRequest, next: Next) -> Response {
296    let resp = next.run(req).await;
297    if resp.status() != StatusCode::PAYLOAD_TOO_LARGE {
298        return resp;
299    }
300    error_response_coded(
301        StatusCode::PAYLOAD_TOO_LARGE,
302        &format!(
303            "request body exceeds the {} MiB limit",
304            MAX_BODY_BYTES / (1024 * 1024)
305        ),
306        "invalid_request_error",
307        None,
308        Some("request_too_large"),
309    )
310}
311
312/// The one place the body-size policy is applied (tested directly in `body_limit_tests`;
313/// `main` wires the app router through here).
314fn apply_body_limit(app: Router) -> Router {
315    app.layer(DefaultBodyLimit::max(MAX_BODY_BYTES))
316        .layer(middleware::from_fn(shape_payload_too_large))
317}
318
319fn protected_inference_path(path: &str) -> bool {
320    matches!(
321        path,
322        "/v1/auth/check"
323            | "/v1/completions"
324            | "/v1/chat/completions"
325            | "/v1/messages"
326            | "/v1/responses"
327            | "/v1/embeddings"
328            | "/v1/rerank"
329    )
330}
331
332/// Give middleware refusals the same request-id and body contract as the handler they
333/// replace. In particular, `/v1/messages` must carry the Anthropic body plus both request-id
334/// header spellings even when the body has not been read yet.
335async fn shape_inference_early_response(path: &str, response: Response) -> Response {
336    let request_id = Envelope::new(path != "/v1/completions");
337    if path == "/v1/messages" {
338        anthropic::with_anthropic_request_id(
339            &request_id.id,
340            anthropic::reshape_error(response, &request_id.id).await,
341        )
342    } else {
343        with_request_id(&request_id.id, response)
344    }
345}
346
347/// Authenticate inference requests from headers before any route extractor is allowed to poll
348/// the body. This covers every tenant-authenticated inference surface; catalog, health, metrics,
349/// and admin policies have distinct public/auth contracts. The route handlers retain their own
350/// authentication checks for defense in depth and for dialect-specific error shaping.
351async fn authenticate_inference_before_body(
352    State(st): State<AppState>,
353    mut req: AxumRequest,
354    next: Next,
355) -> Response {
356    if !protected_inference_path(req.uri().path()) {
357        return next.run(req).await;
358    }
359    let path = req.uri().path().to_string();
360    // Reject an advertised oversize before touching either admission pool. Otherwise a caller
361    // could fill the pool's active slots and waiter queue with requests that the inner extractor
362    // would reject as 413 anyway.
363    if declared_body_length(&req).is_some_and(|length| length > MAX_BODY_BYTES) {
364        return shape_inference_early_response(
365            &path,
366            error_response_coded(
367                StatusCode::PAYLOAD_TOO_LARGE,
368                &format!(
369                    "request body exceeds the {} MiB limit",
370                    MAX_BODY_BYTES / (1024 * 1024)
371                ),
372                "invalid_request_error",
373                None,
374                Some("request_too_large"),
375            ),
376        )
377        .await;
378    }
379    let headers = req.headers();
380    let bearer = bearer_token(headers);
381    let auth = if matches!(path.as_str(), "/v1/messages" | "/v1/auth/check") {
382        let api_key = headers
383            .get("x-api-key")
384            .and_then(|value| value.to_str().ok());
385        surfaces::authenticate_candidates(&st.api_auth, &[bearer, api_key])
386    } else {
387        surfaces::authenticate_candidates(&st.api_auth, &[bearer])
388    };
389    if let Err(why) = auth {
390        return shape_inference_early_response(&path, authentication_error(why)).await;
391    }
392    // Keep the large, authenticated body parser itself bounded. The route-level request slot is
393    // intentionally acquired after JSON/vision validation so ordinary 400s do not consume it;
394    // this separate permit prevents a low-cap key from queueing unbounded 192 MiB parses before
395    // that later gate while retaining the advertised body ceiling and 413 contract. Small,
396    // explicitly sized bodies use a separate finite pool so a slow large upload cannot head-of-
397    // line block ordinary requests, while neither class can create unbounded parser tasks.
398    // Acquisition is deliberately fail-fast; Tokio's async waiter queue is not a resource bound.
399    let body_deadline = tokio::time::Instant::now() + body_read_timeout(&req);
400    let body_admission = if body_requires_admission(&req) {
401        body_admission_semaphore()
402    } else {
403        small_body_admission_semaphore()
404    };
405    let body_permit = match body_admission.try_acquire_owned() {
406        Ok(permit) => permit,
407        Err(tokio::sync::TryAcquireError::Closed) => {
408            let response = retry_contract_response(
409                error_response_coded(
410                    StatusCode::SERVICE_UNAVAILABLE,
411                    "request body admission is unavailable",
412                    "server_error",
413                    None,
414                    Some("body_admission_unavailable"),
415                ),
416                Some(BODY_ADMISSION_RETRY_AFTER_S),
417            );
418            return shape_inference_early_response(&path, response).await;
419        }
420        Err(tokio::sync::TryAcquireError::NoPermits) => {
421            let response = retry_contract_response(
422                error_response_coded(
423                    StatusCode::TOO_MANY_REQUESTS,
424                    "request body admission is busy",
425                    "rate_limit_error",
426                    None,
427                    Some("body_admission_busy"),
428                ),
429                Some(BODY_ADMISSION_RETRY_AFTER_S),
430            );
431            return shape_inference_early_response(&path, response).await;
432        }
433    };
434    // Typed handlers retain this shared guard through semantic traversal, prompt construction,
435    // tokenization, and request-slot admission, then release it before any generation wait. Raw
436    // translation surfaces do the same through their shared admission path. The middleware keeps
437    // a fallback clone so extractor rejection and non-body routes cannot leak a permit.
438    let body_admission_guard = BodyAdmissionGuard::new(body_permit);
439    req.extensions_mut().insert(body_admission_guard.clone());
440    let body = std::mem::replace(req.body_mut(), Body::empty());
441    let mut body = Box::pin(body.into_data_stream());
442    let body_timed_out = Arc::new(std::sync::atomic::AtomicBool::new(false));
443    let body_timed_out_flag = body_timed_out.clone();
444    let guarded_body = async_stream::stream! {
445        loop {
446            let remaining = body_deadline.saturating_duration_since(tokio::time::Instant::now());
447            if remaining.is_zero() {
448                body_timed_out_flag.store(true, std::sync::atomic::Ordering::Release);
449                yield Err(std::io::Error::new(
450                    std::io::ErrorKind::TimedOut,
451                    "request body read deadline exceeded",
452                ));
453                break;
454            }
455            let poll = std::future::poll_fn(|cx| body.as_mut().poll_next(cx));
456            let frame = match tokio::time::timeout(BODY_IDLE_TIMEOUT.min(remaining), poll).await {
457                Ok(frame) => frame,
458                Err(_) => {
459                    body_timed_out_flag.store(true, std::sync::atomic::Ordering::Release);
460                    yield Err(std::io::Error::new(
461                        std::io::ErrorKind::TimedOut,
462                        "request body idle timeout exceeded",
463                    ));
464                    break;
465                }
466            };
467            match frame {
468                Some(Ok(bytes)) => yield Ok(bytes),
469                Some(Err(error)) => {
470                    yield Err(std::io::Error::other(error.to_string()));
471                    break;
472                }
473                None => break,
474            }
475        }
476    };
477    *req.body_mut() = Body::from_stream(guarded_body);
478    let response = next.run(req).await;
479    body_admission_guard.release();
480    if body_timed_out.load(std::sync::atomic::Ordering::Acquire) {
481        let request_id = Envelope::new(path != "/v1/completions");
482        let timeout = error_response_coded(
483            StatusCode::REQUEST_TIMEOUT,
484            "request body read timed out",
485            "invalid_request_error",
486            None,
487            Some("request_body_timeout"),
488        );
489        return if path == "/v1/messages" {
490            anthropic::with_anthropic_request_id(
491                &request_id.id,
492                anthropic::reshape_error(timeout, &request_id.id).await,
493            )
494        } else {
495            with_request_id(&request_id.id, timeout)
496        };
497    }
498    if path == "/v1/messages" && response.status() == StatusCode::PAYLOAD_TOO_LARGE {
499        let request_id = Envelope::new(true);
500        return anthropic::with_anthropic_request_id(
501            &request_id.id,
502            anthropic::reshape_error(response, &request_id.id).await,
503        );
504    }
505    response
506}
507
508#[cfg(test)]
509mod body_limit_tests {
510    use super::*;
511
512    static BODY_ADMISSION_TEST_LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());
513
514    /// A router with the REAL body policy (`apply_body_limit`, the exact helper `main`
515    /// wires) over both extractor shapes the inference routes use: `Json` (completions /
516    /// chat) and raw `Bytes` (`/v1/messages`).
517    fn test_app() -> Router {
518        let app = Router::new()
519            .route(
520                "/bytes",
521                post(|b: axum::body::Bytes| async move { b.len().to_string() }),
522            )
523            .route(
524                "/json",
525                post(
526                    |AdmittedJson(v, _admission): AdmittedJson<serde_json::Value>| async move {
527                        v["pad"].as_str().unwrap_or("").len().to_string()
528                    },
529                ),
530            );
531        apply_body_limit(app)
532    }
533
534    fn streamed_body(chunks: usize) -> Body {
535        // one shared 1 MiB chunk, cloned (Bytes clones are refcounted — no O(n) alloc);
536        // streaming means NO Content-Length, exercising the mid-read cutoff path.
537        let chunk = axum::body::Bytes::from(vec![b'x'; 1024 * 1024]);
538        Body::from_stream(async_stream::stream! {
539            for _ in 0..chunks {
540                yield Ok::<_, std::io::Error>(chunk.clone());
541            }
542        })
543    }
544
545    #[tokio::test]
546    async fn bodies_past_the_old_2mib_default_are_accepted() {
547        // 3 MiB — over axum's 2 MiB default that silently capped the advertised
548        // 262k-token + vision surface, comfortably under MAX_BODY_BYTES.
549        for (path, body) in [
550            ("/bytes", Body::from(vec![b'x'; 3 * 1024 * 1024])),
551            (
552                "/json",
553                Body::from(
554                    serde_json::to_vec(&json!({ "pad": "x".repeat(3 * 1024 * 1024) })).unwrap(),
555                ),
556            ),
557        ] {
558            let resp = test_app()
559                .oneshot(
560                    axum::http::Request::post(path)
561                        .header(CONTENT_TYPE, "application/json")
562                        .body(body)
563                        .unwrap(),
564                )
565                .await
566                .unwrap();
567            assert_eq!(resp.status(), StatusCode::OK, "{path}");
568        }
569    }
570
571    #[tokio::test]
572    async fn body_at_exactly_the_limit_is_accepted() {
573        let resp = test_app()
574            .oneshot(
575                axum::http::Request::post("/bytes")
576                    .body(streamed_body(MAX_BODY_BYTES / (1024 * 1024)))
577                    .unwrap(),
578            )
579            .await
580            .unwrap();
581        assert_eq!(resp.status(), StatusCode::OK);
582        let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
583            .await
584            .unwrap();
585        assert_eq!(body.as_ref(), MAX_BODY_BYTES.to_string().as_bytes());
586    }
587
588    #[tokio::test]
589    async fn oversize_body_is_a_clean_413_in_our_error_shape() {
590        // one chunk past the ceiling; both extractor shapes must answer the SAME way —
591        // an HTTP 413 carrying the standard OpenAI error object (never axum's bare-text
592        // rejection, never a hang or reset).
593        for path in ["/bytes", "/json"] {
594            let resp = test_app()
595                .oneshot(
596                    axum::http::Request::post(path)
597                        .header(CONTENT_TYPE, "application/json")
598                        .body(streamed_body(MAX_BODY_BYTES / (1024 * 1024) + 1))
599                        .unwrap(),
600                )
601                .await
602                .unwrap();
603            assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE, "{path}");
604            assert_eq!(
605                resp.headers().get("x-should-retry").map(|v| v.as_bytes()),
606                Some(b"false".as_ref()),
607                "{path}: retrying identical bytes cannot fix a 413"
608            );
609            let body = axum::body::to_bytes(resp.into_body(), usize::MAX)
610                .await
611                .unwrap();
612            let v: serde_json::Value = serde_json::from_slice(&body).expect("JSON error shape");
613            assert_eq!(v["error"]["type"], "invalid_request_error", "{path}");
614            assert_eq!(v["error"]["code"], "request_too_large", "{path}");
615            assert!(
616                v["error"]["message"].as_str().unwrap().contains("192 MiB"),
617                "{path}: message names the limit"
618            );
619        }
620    }
621
622    #[tokio::test]
623    async fn authenticated_body_admission_is_finite() {
624        let _test_lock = BODY_ADMISSION_TEST_LOCK.lock().await;
625        let semaphore = body_admission_semaphore();
626        let mut permits = Vec::new();
627        for _ in 0..MAX_BODY_ADMISSIONS {
628            permits.push(semaphore.clone().acquire_owned().await.unwrap());
629        }
630        assert!(
631            tokio::time::timeout(std::time::Duration::from_millis(20), semaphore.acquire())
632                .await
633                .is_err(),
634            "body parser admission must not be unbounded"
635        );
636        drop(permits);
637        assert!(semaphore.acquire().await.is_ok());
638    }
639
640    #[tokio::test]
641    async fn small_body_admission_is_finite_and_separate() {
642        let _test_lock = BODY_ADMISSION_TEST_LOCK.lock().await;
643        let large = body_admission_semaphore();
644        let small = small_body_admission_semaphore();
645        let mut small_permits = Vec::new();
646        for _ in 0..MAX_SMALL_BODY_ADMISSIONS {
647            small_permits.push(small.clone().acquire_owned().await.unwrap());
648        }
649        assert!(
650            tokio::time::timeout(std::time::Duration::from_millis(20), small.acquire())
651                .await
652                .is_err(),
653            "small body parser admission must be bounded"
654        );
655        assert!(
656            large.clone().try_acquire().is_ok(),
657            "small uploads must not consume large-upload permits"
658        );
659        drop(small_permits);
660        assert!(small.acquire().await.is_ok());
661    }
662
663    #[test]
664    fn small_declared_bodies_bypass_large_upload_admission() {
665        let request = axum::http::Request::post("/v1/chat/completions")
666            .header(CONTENT_LENGTH, "2048")
667            .body(Body::empty())
668            .unwrap();
669        assert!(!body_requires_admission(&request));
670
671        let request = axum::http::Request::post("/v1/chat/completions")
672            .header(
673                CONTENT_LENGTH,
674                (BODY_ADMISSION_BYPASS_BYTES + 1).to_string(),
675            )
676            .body(Body::empty())
677            .unwrap();
678        assert!(body_requires_admission(&request));
679
680        let request = axum::http::Request::post("/v1/chat/completions")
681            .header(CONTENT_LENGTH, "2048")
682            .header(TRANSFER_ENCODING, "chunked")
683            .body(Body::empty())
684            .unwrap();
685        assert!(body_requires_admission(&request));
686    }
687
688    #[test]
689    fn declared_body_timeout_scales_with_upload_size_and_has_a_cap() {
690        let unknown = axum::http::Request::post("/v1/chat/completions")
691            .body(Body::empty())
692            .unwrap();
693        assert_eq!(body_read_timeout(&unknown), BODY_READ_TIMEOUT);
694
695        let large = axum::http::Request::post("/v1/chat/completions")
696            .header(CONTENT_LENGTH, MAX_BODY_BYTES.to_string())
697            .body(Body::empty())
698            .unwrap();
699        assert!(body_read_timeout(&large) > BODY_READ_TIMEOUT);
700        assert_eq!(body_read_timeout(&large), BODY_READ_TIMEOUT_MAX);
701
702        let absurd = axum::http::Request::post("/v1/chat/completions")
703            .header(CONTENT_LENGTH, u64::MAX.to_string())
704            .body(Body::empty())
705            .unwrap();
706        assert_eq!(body_read_timeout(&absurd), BODY_READ_TIMEOUT_MAX);
707    }
708
709    #[tokio::test]
710    async fn early_body_refusals_keep_dialect_ids_and_retry_contracts() {
711        let too_large = shape_inference_early_response(
712            "/v1/messages",
713            error_response_coded(
714                StatusCode::PAYLOAD_TOO_LARGE,
715                "request body exceeds the 192 MiB limit",
716                "invalid_request_error",
717                None,
718                Some("request_too_large"),
719            ),
720        )
721        .await;
722        assert_eq!(too_large.status(), StatusCode::PAYLOAD_TOO_LARGE);
723        let house_id = too_large.headers()["x-request-id"].clone();
724        assert_eq!(too_large.headers()["request-id"], house_id);
725        assert_eq!(too_large.headers()["x-should-retry"], "false");
726        let body = axum::body::to_bytes(too_large.into_body(), usize::MAX)
727            .await
728            .unwrap();
729        let payload: serde_json::Value = serde_json::from_slice(&body).unwrap();
730        assert_eq!(payload["type"], "error");
731        assert_eq!(payload["request_id"], house_id.to_str().unwrap());
732
733        let busy = shape_inference_early_response(
734            "/v1/chat/completions",
735            retry_contract_response(
736                error_response_coded(
737                    StatusCode::TOO_MANY_REQUESTS,
738                    "request body admission is busy",
739                    "rate_limit_error",
740                    None,
741                    Some("body_admission_busy"),
742                ),
743                Some(BODY_ADMISSION_RETRY_AFTER_S),
744            ),
745        )
746        .await;
747        assert_eq!(busy.status(), StatusCode::TOO_MANY_REQUESTS);
748        assert!(!busy.headers()["x-request-id"].is_empty());
749        assert_eq!(busy.headers()["retry-after"], "1");
750        assert_eq!(busy.headers()["retry-after-ms"], "1000");
751        assert!(busy.headers().get("x-should-retry").is_none());
752        let body = axum::body::to_bytes(busy.into_body(), usize::MAX)
753            .await
754            .unwrap();
755        let payload: serde_json::Value = serde_json::from_slice(&body).unwrap();
756        assert_eq!(payload["error"]["code"], "body_admission_busy");
757    }
758
759    #[tokio::test]
760    async fn vision_preprocess_admission_is_fail_fast_and_retryable() {
761        let semaphore = Box::leak(Box::new(tokio::sync::Semaphore::new(1)));
762        let held = semaphore.try_acquire().unwrap();
763        let busy = try_vision_preprocess_with(true, semaphore).unwrap_err();
764        assert_eq!(busy.status(), StatusCode::TOO_MANY_REQUESTS);
765        assert_eq!(busy.headers()["retry-after"], "1");
766        drop(held);
767        assert!(
768            try_vision_preprocess_with(true, semaphore)
769                .unwrap()
770                .is_some()
771        );
772        assert!(
773            try_vision_preprocess_with(false, semaphore)
774                .unwrap()
775                .is_none()
776        );
777    }
778
779    #[tokio::test]
780    async fn typed_json_retains_body_admission_until_handler_validation_releases_it() {
781        #[derive(Clone)]
782        struct Signals {
783            parsed: Arc<tokio::sync::Notify>,
784            finish: Arc<tokio::sync::Notify>,
785            semaphore: Arc<tokio::sync::Semaphore>,
786        }
787
788        let semaphore = Arc::new(tokio::sync::Semaphore::new(1));
789        let guard = BodyAdmissionGuard::new(semaphore.clone().try_acquire_owned().unwrap());
790        let signals = Signals {
791            parsed: Arc::new(tokio::sync::Notify::new()),
792            finish: Arc::new(tokio::sync::Notify::new()),
793            semaphore: semaphore.clone(),
794        };
795        let app = Router::new()
796            .route(
797                "/",
798                post(
799                    |Extension(signals): Extension<Signals>,
800                     AdmittedJson(_, mut admission): AdmittedJson<serde_json::Value>| async move {
801                        assert_eq!(
802                            signals.semaphore.available_permits(),
803                            0,
804                            "typed deserialization alone must not release post-parse admission"
805                        );
806                        admission.release();
807                        assert_eq!(signals.semaphore.available_permits(), 1);
808                        signals.parsed.notify_one();
809                        signals.finish.notified().await;
810                        "ok"
811                    },
812                ),
813            )
814            .layer(Extension(signals.clone()))
815            .layer(Extension(guard));
816        let response = tokio::spawn(
817            app.oneshot(
818                axum::http::Request::post("/")
819                    .header(CONTENT_TYPE, "application/json")
820                    .body(Body::from(r#"{"value":1}"#))
821                    .unwrap(),
822            ),
823        );
824        signals.parsed.notified().await;
825        assert_eq!(
826            semaphore.available_permits(),
827            1,
828            "validated work must release admission before generation waits"
829        );
830        signals.finish.notify_one();
831        assert_eq!(response.await.unwrap().unwrap().status(), StatusCode::OK);
832    }
833
834    #[tokio::test]
835    async fn transport_closes_stalled_headers_and_caps_connections() {
836        use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
837
838        let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
839        let address = listener.local_addr().unwrap();
840        let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
841        let server = tokio::spawn(serve_bounded_http_with_limits(
842            listener,
843            Router::new().route("/", get(|| async { "ok" })).route(
844                "/slow",
845                get(|| async {
846                    tokio::time::sleep(std::time::Duration::from_millis(140)).await;
847                    "slow-ok"
848                }),
849            ),
850            async move {
851                let _ = shutdown_rx.await;
852            },
853            std::time::Duration::from_millis(30),
854            1,
855            std::time::Duration::from_millis(80),
856        ));
857
858        let mut stalled = tokio::net::TcpStream::connect(address).await.unwrap();
859        stalled.write_all(b"GET / HT").await.unwrap();
860        tokio::time::sleep(std::time::Duration::from_millis(10)).await;
861        let mut excess = tokio::net::TcpStream::connect(address).await.unwrap();
862        let mut bytes = Vec::new();
863        tokio::time::timeout(
864            std::time::Duration::from_millis(250),
865            excess.read_to_end(&mut bytes),
866        )
867        .await
868        .expect("connection beyond the cap must be closed promptly")
869        .unwrap();
870
871        bytes.clear();
872        tokio::time::timeout(
873            std::time::Duration::from_millis(500),
874            stalled.read_to_end(&mut bytes),
875        )
876        .await
877        .expect("stalled request headers must hit the configured deadline")
878        .unwrap();
879
880        let mut idle = tokio::net::TcpStream::connect(address).await.unwrap();
881        idle.write_all(b"GET / HTTP/1.1\r\nHost: local\r\n\r\n")
882            .await
883            .unwrap();
884        bytes.clear();
885        tokio::time::timeout(
886            std::time::Duration::from_millis(500),
887            idle.read_to_end(&mut bytes),
888        )
889        .await
890        .expect("an idle keep-alive connection must hit the maximum lifetime")
891        .unwrap();
892        assert!(String::from_utf8_lossy(&bytes).contains("200 OK"));
893
894        let mut active = tokio::net::TcpStream::connect(address).await.unwrap();
895        active
896            .write_all(b"GET /slow HTTP/1.1\r\nHost: local\r\n\r\n")
897            .await
898            .unwrap();
899        bytes.clear();
900        tokio::time::timeout(
901            std::time::Duration::from_millis(500),
902            active.read_to_end(&mut bytes),
903        )
904        .await
905        .expect("an active response must finish across the connection age boundary")
906        .unwrap();
907        let active_response = String::from_utf8_lossy(&bytes);
908        assert!(active_response.contains("200 OK"), "{active_response}");
909        assert!(active_response.contains("slow-ok"), "{active_response}");
910
911        // HTTP/2 keepalive constructs its timer during the handshake. If the H2 builder
912        // does not receive a TokioTimer, hyper panics in the connection task and the
913        // response future sees a dropped connection instead of this 200.
914        let h2_stream = tokio::net::TcpStream::connect(address).await.unwrap();
915        let (mut h2_client, h2_connection) = h2::client::handshake(h2_stream).await.unwrap();
916        let h2_driver = tokio::spawn(h2_connection);
917        let request = axum::http::Request::builder()
918            .uri(format!("http://{address}/"))
919            .body(())
920            .unwrap();
921        let (response, _) = h2_client.send_request(request, true).unwrap();
922        let response = tokio::time::timeout(std::time::Duration::from_millis(500), response)
923            .await
924            .expect("HTTP/2 handshake and response must complete")
925            .expect("HTTP/2 connection must stay alive through the response");
926        assert_eq!(response.status(), StatusCode::OK);
927        drop(h2_client);
928        h2_driver.abort();
929        let _ = h2_driver.await;
930
931        let _ = shutdown_tx.send(());
932        server.await.unwrap().unwrap();
933    }
934}
935
936#[derive(Clone, Default)]
937struct TtftRequestTrace(Option<Arc<ttft::Trace>>);
938
939fn is_sse_data_frame(bytes: &[u8]) -> bool {
940    bytes
941        .windows(b"data:".len())
942        .any(|window| window == b"data:")
943}
944
945async fn ttft_request_start(mut req: AxumRequest, next: Next) -> Response {
946    let trace = ttft::start(req.uri().path());
947    req.extensions_mut().insert(TtftRequestTrace(trace.clone()));
948    let response = next.run(req).await;
949    let Some(trace) = trace else {
950        return response;
951    };
952    let is_sse = response
953        .headers()
954        .get(CONTENT_TYPE)
955        .and_then(|value| value.to_str().ok())
956        .is_some_and(|value| value.starts_with("text/event-stream"));
957    if !is_sse {
958        return response;
959    }
960
961    // Stamp the first serialized application data frame as Hyper polls it. Axum's
962    // keepalive comments can precede a long prefill, so non-data frames do not count.
963    let (parts, body) = response.into_parts();
964    let mut body = Box::pin(body.into_data_stream());
965    let stream = async_stream::stream! {
966        while let Some(frame) =
967            std::future::poll_fn(|cx| body.as_mut().poll_next(cx)).await
968        {
969            if frame
970                .as_ref()
971                .is_ok_and(|bytes| is_sse_data_frame(bytes))
972            {
973                trace.mark_first_sse_byte();
974            }
975            yield frame;
976        }
977    };
978    Response::from_parts(parts, Body::from_stream(stream))
979}
980
981const OPENROUTER_SCHEMA_VERSION: &str = "2.4";
982const JSON_SAFE_INTEGER_MAX: u64 = 9_007_199_254_740_991;
983
984#[derive(Debug, Clone, Default, Deserialize)]
985#[serde(deny_unknown_fields)]
986struct OpenRouterMetadataFile {
987    #[serde(default)]
988    models: HashMap<String, OpenRouterModelMetadata>,
989    /// Machine-validated future offers. These never enter a model feed or request path until the
990    /// operator moves the entry into `models` and loads the same alias through `MEMRA_MODELS`.
991    #[serde(default)]
992    planned_models: HashMap<String, OpenRouterModelMetadata>,
993    /// Router-marketplace provider identity (TrustedRouter contract v2). Rendered at the top
994    /// of /v1/models next to the server-truth error contract; absent = no provider block.
995    #[serde(default)]
996    provider: Option<ProviderMetadata>,
997}
998
999/// Operator-declared provider identity for the /v1/models contract-v2 header. Everything a
1000/// router needs to route AROUND us (status page, contacts, regions) is declared here; the
1001/// error contract itself (429/503/Retry-After/quota code) is server truth and not configurable.
1002#[derive(Debug, Clone, Deserialize)]
1003#[serde(deny_unknown_fields)]
1004struct ProviderMetadata {
1005    id: String,
1006    #[serde(default)]
1007    status_url: Option<String>,
1008    #[serde(default)]
1009    support_contact: Option<String>,
1010    #[serde(default)]
1011    incident_contact: Option<String>,
1012    #[serde(default)]
1013    regions: Vec<String>,
1014}
1015
1016/// Contract-v2 lifecycle block (RFC 3339 timestamps). A model without one is "active".
1017#[derive(Debug, Clone, Default, Deserialize)]
1018#[serde(deny_unknown_fields)]
1019struct LifecycleMetadata {
1020    #[serde(default)]
1021    status: Option<String>,
1022    #[serde(default)]
1023    deprecation_at: Option<String>,
1024    #[serde(default)]
1025    retirement_at: Option<String>,
1026    #[serde(default)]
1027    replacement_model_id: Option<String>,
1028}
1029
1030/// Contract-v2 reliability block: how long a router should wait before failing over.
1031#[derive(Debug, Clone, Default, Deserialize)]
1032#[serde(deny_unknown_fields)]
1033struct ReliabilityMetadata {
1034    #[serde(default)]
1035    first_token_timeout_seconds: Option<u64>,
1036    #[serde(default)]
1037    completion_timeout_seconds: Option<u64>,
1038    #[serde(default)]
1039    stream_idle_timeout_seconds: Option<u64>,
1040    #[serde(default)]
1041    capacity_scope: Option<String>,
1042}
1043
1044#[derive(Debug, Clone, Default, Deserialize)]
1045#[serde(deny_unknown_fields)]
1046struct OpenRouterModelMetadata {
1047    /// Contract-v2 per-model blocks (see the ProviderMetadata docs above).
1048    #[serde(default)]
1049    owned_by: Option<String>,
1050    #[serde(default)]
1051    lifecycle: Option<LifecycleMetadata>,
1052    #[serde(default)]
1053    reliability: Option<ReliabilityMetadata>,
1054    #[serde(default)]
1055    hugging_face_id: Option<String>,
1056    #[serde(default)]
1057    created: Option<u64>,
1058    #[serde(default)]
1059    quantization: Option<String>,
1060    #[serde(default)]
1061    description: Option<String>,
1062    #[serde(default)]
1063    max_prompt_length: Option<u64>,
1064    #[serde(default)]
1065    max_output_length: Option<u64>,
1066    /// Request default when max_tokens is omitted. Keeping this separate from the provider maximum
1067    /// prevents an advertised 262k ceiling from reserving a 262k KV cache for every ordinary call.
1068    #[serde(default)]
1069    default_output_length: Option<u64>,
1070    #[serde(default)]
1071    pricing: OpenRouterPricing,
1072    #[serde(default)]
1073    capacity: OpenRouterCapacity,
1074    #[serde(default)]
1075    is_ready: Option<bool>,
1076    #[serde(default)]
1077    is_free: Option<bool>,
1078    #[serde(default)]
1079    discount_to_user: Option<f64>,
1080    #[serde(default)]
1081    openrouter_slug: Option<String>,
1082    #[serde(default)]
1083    datacenters: Vec<OpenRouterDatacenter>,
1084    /// Extra INPUT modalities beyond the implicit "text" (lane/vision: ["image"]).
1085    /// Each renders as its own input-modality object in the feed; image tokens bill
1086    /// at the prompt token price (pads are ordinary prompt tokens).
1087    #[serde(default)]
1088    input_modalities: Vec<String>,
1089    /// Which API surface this model actually serves: "chat" (default), "embedding",
1090    /// or "rerank". This is a PUBLISHED CONTRACT, not a hint — the catalog row a
1091    /// client SDK reads is built from it, so it is declared rather than inferred.
1092    ///
1093    /// It exists because the row used to be a hardcoded `"type": "chat"` with
1094    /// `endpoints: ["chat/completions"]` for every registered model. On 2026-08-28
1095    /// that advertised qwen3-embedding-8b and qwen3-reranker-8b as chat models with
1096    /// `tools: true`, `streaming: true` and no mention of /v1/embeddings or
1097    /// /v1/rerank — the two surfaces they actually serve. A client that believed
1098    /// the catalog would call the wrong endpoint with the wrong body shape.
1099    ///
1100    /// Embedding/rerank capability is decided at RUNTIME (does the prime path yield
1101    /// hidden state), which cannot be read at catalog-build time; the contract we
1102    /// publish must therefore be stated by the deployment, not guessed.
1103    #[serde(default)]
1104    surface: Option<String>,
1105    #[serde(default)]
1106    zdr: Option<bool>,
1107    #[serde(default)]
1108    hipaa: Option<bool>,
1109    /// SERVING-DEPLOYMENT default for the OpenAI `reasoning_effort` field when a chat
1110    /// request leaves reasoning UNSET (owner ruling 2026-08-19: gemma-4 serves think-ON
1111    /// by default — think-on scored 80.81 GPQA vs 76.26 think-off on the served mint;
1112    /// qwen's template already defaults ON without any knob). Applied by `parse_think`
1113    /// exactly as if the client had sent this value, so the rendered prompt is
1114    /// byte-identical to the explicit request. Explicit client reasoning
1115    /// (`reasoning_effort`, `reasoning.effort`, `reasoning.enabled`) always wins; the
1116    /// template's own vendor-law rendering semantics are untouched — this only moves
1117    /// which ThinkMode an unset request resolves to for THIS deployment.
1118    #[serde(default)]
1119    default_reasoning_effort: Option<String>,
1120    /// VENDOR-RECOMMENDED SAMPLING for requests that expressed NOTHING (owner ruling
1121    /// 2026-08-19: "we don't have to serve greedy, we measure greedy but we serve what the
1122    /// user chooses" / "we default to what are the recommendations" / "greedy can create
1123    /// issues"). Each key substitutes for exactly one omitted sampling field, on EVERY
1124    /// surface (`/v1/completions`, `/v1/chat/completions`, `/v1/messages`, `/v1/responses`)
1125    /// through the single `resolve_sampler_config` law. An explicit client value always
1126    /// wins — including an explicit `temperature: 0`, which still produces true greedy.
1127    ///
1128    /// The value belongs to the MODEL VENDOR, not to us: put the citation in the TOML
1129    /// comment next to it so nobody later "cleans up" a deliberate number. Boot-validated
1130    /// (see `validate_openrouter_metadata`): a typo'd default must fail before GPU load,
1131    /// never become a per-request 400 storm under the watchdog.
1132    ///
1133    /// `default_temperature` REFUSES 0.0 on purpose. A zero here would reinstate exactly the
1134    /// greedy-by-default hazard this key exists to remove — silently, deployment-wide, for
1135    /// every omitting client. Greedy stays reachable the honest way: the client sends
1136    /// `temperature: 0`.
1137    #[serde(default)]
1138    default_temperature: Option<f32>,
1139    #[serde(default)]
1140    default_top_p: Option<f32>,
1141    /// 0 = disabled (keep all) — the same convention the request field uses.
1142    #[serde(default)]
1143    default_top_k: Option<usize>,
1144    #[serde(default)]
1145    default_min_p: Option<f32>,
1146    #[serde(default)]
1147    default_presence_penalty: Option<f32>,
1148    #[serde(default)]
1149    default_frequency_penalty: Option<f32>,
1150    /// OpenRouter/HF-convention multiplicative penalty; 1.0 = off.
1151    #[serde(default)]
1152    default_repetition_penalty: Option<f32>,
1153    /// SECOND VENDOR SAMPLING ARM for the model's NON-THINKING mode (owner ruling
1154    /// 2026-08-24: "do what is correct" — served models default to the VENDOR's
1155    /// recommendation, and some vendors publish TWO recommendations, one per thinking
1156    /// mode; qwen3.8's card gives thinking 1.0/0.95/20 and non-thinking 0.7/0.80/20 +
1157    /// presence_penalty 1.5). The flat `default_*` keys above stay the PRIMARY arm —
1158    /// what every request got before this table existed — and this table, when
1159    /// declared, is what a request whose RESOLVED thinking mode is OFF gets for the
1160    /// sampling fields it left unset (`ModelSamplingDefaults::for_mode`). Off is the
1161    /// resolved `ThinkMode::NoThink`, whichever spelling produced it: `reasoning_effort:
1162    /// "none"|"minimal"`, `enable_thinking:false`, `chat_template_kwargs.
1163    /// enable_thinking:false`, `reasoning:{enabled:false}`, `include_reasoning:false`,
1164    /// Anthropic `thinking.type:"disabled"`, or an operator `default_reasoning_effort =
1165    /// "none"` resolving an unset request. An explicit client value is NEVER overridden
1166    /// by either arm, and an explicit `temperature: 0` still produces true greedy.
1167    ///
1168    /// A model WITHOUT this table is byte-identical to before it existed: one arm,
1169    /// every mode. Same boot-validation posture and ranges as the flat keys (a typo'd
1170    /// arm fails before GPU load), and an EMPTY declared table is refused — declaring
1171    /// the arm and recommending nothing would silently hand thinking-off traffic the
1172    /// bare API-standard defaults while looking configured.
1173    #[serde(default)]
1174    non_thinking_sampling: Option<SamplingArmMetadata>,
1175}
1176
1177/// One declared sampling arm (`non_thinking_sampling`): the same seven vendor keys as the
1178/// flat `default_*` set, unprefixed because the table name already says which arm they
1179/// belong to. `None` = the vendor recommends nothing for that field in this mode — it
1180/// falls through to the API-standard default, never to the other arm (arms are separate
1181/// vendor programs; blending them would serve numbers no vendor published).
1182#[derive(Debug, Clone, Default, Deserialize)]
1183#[serde(deny_unknown_fields)]
1184struct SamplingArmMetadata {
1185    #[serde(default)]
1186    temperature: Option<f32>,
1187    #[serde(default)]
1188    top_p: Option<f32>,
1189    #[serde(default)]
1190    top_k: Option<usize>,
1191    #[serde(default)]
1192    min_p: Option<f32>,
1193    #[serde(default)]
1194    presence_penalty: Option<f32>,
1195    #[serde(default)]
1196    frequency_penalty: Option<f32>,
1197    #[serde(default)]
1198    repetition_penalty: Option<f32>,
1199}
1200
1201impl SamplingArmMetadata {
1202    fn is_empty(&self) -> bool {
1203        self.temperature.is_none()
1204            && self.top_p.is_none()
1205            && self.top_k.is_none()
1206            && self.min_p.is_none()
1207            && self.presence_penalty.is_none()
1208            && self.frequency_penalty.is_none()
1209            && self.repetition_penalty.is_none()
1210    }
1211}
1212
1213#[derive(Debug, Clone, Default, Deserialize)]
1214#[serde(deny_unknown_fields)]
1215struct OpenRouterPricing {
1216    #[serde(default)]
1217    prompt: Option<String>,
1218    #[serde(default)]
1219    cached_prompt: Option<String>,
1220    #[serde(default)]
1221    cache_write: Option<String>,
1222    #[serde(default)]
1223    completion: Option<String>,
1224    #[serde(default)]
1225    internal_reasoning: Option<String>,
1226    #[serde(default)]
1227    request: Option<String>,
1228}
1229
1230#[derive(Debug, Clone, Default, Deserialize)]
1231#[serde(deny_unknown_fields)]
1232struct OpenRouterCapacity {
1233    #[serde(default)]
1234    prompt_tpm: Option<u64>,
1235    #[serde(default)]
1236    cached_prompt_tpm: Option<u64>,
1237    #[serde(default)]
1238    completion_tpm: Option<u64>,
1239    #[serde(default)]
1240    request_rpm: Option<u64>,
1241    #[serde(default)]
1242    concurrency: Option<u64>,
1243}
1244
1245#[derive(Debug, Clone, Deserialize, Serialize)]
1246#[serde(deny_unknown_fields)]
1247struct OpenRouterDatacenter {
1248    country_code: String,
1249    #[serde(default, skip_serializing_if = "Option::is_none")]
1250    region: Option<String>,
1251}
1252
1253impl OpenRouterMetadataFile {
1254    fn parse(
1255        text: &str,
1256    ) -> Result<
1257        (
1258            HashMap<String, OpenRouterModelMetadata>,
1259            Option<ProviderMetadata>,
1260        ),
1261        String,
1262    > {
1263        let file: Self =
1264            toml::from_str(text).map_err(|e| format!("models metadata TOML parse: {e}"))?;
1265        for (alias, metadata) in &file.models {
1266            validate_openrouter_metadata(alias, metadata)?;
1267        }
1268        for (alias, metadata) in &file.planned_models {
1269            validate_openrouter_metadata(alias, metadata)?;
1270            if file.models.contains_key(alias) {
1271                return Err(format!(
1272                    "model alias {alias:?} appears in both models and planned_models"
1273                ));
1274            }
1275        }
1276        if let Some(provider) = &file.provider {
1277            if provider.id.is_empty() {
1278                return Err("provider.id must be a non-empty slug".into());
1279            }
1280            // The contract wants URIs, not bare addresses: mailto:ops@example.com or https://…
1281            for (field, value) in [
1282                ("provider.support_contact", &provider.support_contact),
1283                ("provider.incident_contact", &provider.incident_contact),
1284            ] {
1285                if let Some(value) = value
1286                    && !value.contains(':')
1287                {
1288                    return Err(format!(
1289                        "{field} must be a URI (mailto:… or https://…), got {value:?}"
1290                    ));
1291                }
1292            }
1293        }
1294        Ok((file.models, file.provider))
1295    }
1296
1297    #[cfg(test)]
1298    fn from_toml(text: &str) -> Result<HashMap<String, OpenRouterModelMetadata>, String> {
1299        Self::parse(text).map(|(models, _)| models)
1300    }
1301}
1302
1303/// Decimal-shift a per-token USD price string six places left (the per-1M-token price)
1304/// without floating point: "0.00000038" -> "0.38", "0.0000026" -> "2.60". Keeps at least
1305/// two fraction digits — the router contract's examples are "0.50"-style strings.
1306fn per_million_price(per_token: &str) -> Option<String> {
1307    if !valid_price_string(per_token) {
1308        return None;
1309    }
1310    let (whole, frac) = match per_token.split_once('.') {
1311        Some((whole, frac)) => (whole, frac),
1312        None => (per_token, ""),
1313    };
1314    let mut digits = format!("{whole}{frac}");
1315    let point = whole.len() + 6;
1316    while digits.len() < point {
1317        digits.push('0');
1318    }
1319    let (int_part, frac_part) = digits.split_at(point);
1320    let int_part = int_part.trim_start_matches('0');
1321    let int_part = if int_part.is_empty() { "0" } else { int_part };
1322    let mut frac_out = frac_part.trim_end_matches('0').to_string();
1323    while frac_out.len() < 2 {
1324        frac_out.push('0');
1325    }
1326    Some(format!("{int_part}.{frac_out}"))
1327}
1328
1329fn valid_price_string(value: &str) -> bool {
1330    let mut parts = value.split('.');
1331    let whole = parts.next().unwrap_or_default();
1332    let fraction = parts.next();
1333    !whole.is_empty()
1334        && whole.bytes().all(|b| b.is_ascii_digit())
1335        && fraction.is_none_or(|v| !v.is_empty() && v.bytes().all(|b| b.is_ascii_digit()))
1336        && parts.next().is_none()
1337}
1338
1339fn validate_openrouter_metadata(
1340    alias: &str,
1341    metadata: &OpenRouterModelMetadata,
1342) -> Result<(), String> {
1343    if alias.is_empty() {
1344        return Err("models metadata contains an empty model alias".into());
1345    }
1346    // Fail at BOOT, not per-request: a typo'd default must never turn into a 400 storm
1347    // (or a silent no-op) after the box restarts under the watchdog.
1348    if let Some(effort) = metadata.default_reasoning_effort.as_deref()
1349        && !matches!(effort, "none" | "minimal" | "low" | "medium" | "high")
1350    {
1351        return Err(format!(
1352            "model {alias:?}: default_reasoning_effort {effort:?} is not a \
1353             reasoning_effort level (none|minimal|low|medium|high)"
1354        ));
1355    }
1356    validate_sampling_defaults(alias, metadata)?;
1357    for m in &metadata.input_modalities {
1358        if m != "image" && m != "video" {
1359            return Err(format!(
1360                "model {alias:?}: input_modalities entry {m:?} not served (image/video)"
1361            ));
1362        }
1363    }
1364    if let Some(sfc) = metadata.surface.as_deref()
1365        && !matches!(sfc, "chat" | "embedding" | "rerank")
1366    {
1367        return Err(format!(
1368            "model {alias:?}: surface {sfc:?} is not a served surface (chat|embedding|rerank)"
1369        ));
1370    }
1371    if let Some(q) = metadata.quantization.as_deref()
1372        && !matches!(
1373            q,
1374            "int4"
1375                | "int8"
1376                | "fp4"
1377                | "mxfp4"
1378                | "nvfp4"
1379                | "fp6"
1380                | "fp8"
1381                | "mxfp8"
1382                | "fp16"
1383                | "bf16"
1384                | "fp32"
1385        )
1386    {
1387        return Err(format!(
1388            "model {alias:?}: quantization {q:?} is not in the OpenRouter schema 2.4 enum"
1389        ));
1390    }
1391    for (field, value) in [
1392        ("pricing.prompt", metadata.pricing.prompt.as_deref()),
1393        (
1394            "pricing.cached_prompt",
1395            metadata.pricing.cached_prompt.as_deref(),
1396        ),
1397        (
1398            "pricing.cache_write",
1399            metadata.pricing.cache_write.as_deref(),
1400        ),
1401        ("pricing.completion", metadata.pricing.completion.as_deref()),
1402        (
1403            "pricing.internal_reasoning",
1404            metadata.pricing.internal_reasoning.as_deref(),
1405        ),
1406        ("pricing.request", metadata.pricing.request.as_deref()),
1407    ] {
1408        if let Some(value) = value
1409            && !valid_price_string(value)
1410        {
1411            return Err(format!(
1412                "model {alias:?}: {field} must be a non-negative per-unit USD decimal string"
1413            ));
1414        }
1415    }
1416    for (field, value) in [
1417        ("created", metadata.created),
1418        ("max_prompt_length", metadata.max_prompt_length),
1419        ("max_output_length", metadata.max_output_length),
1420        ("default_output_length", metadata.default_output_length),
1421        ("capacity.prompt_tpm", metadata.capacity.prompt_tpm),
1422        (
1423            "capacity.cached_prompt_tpm",
1424            metadata.capacity.cached_prompt_tpm,
1425        ),
1426        ("capacity.completion_tpm", metadata.capacity.completion_tpm),
1427        ("capacity.request_rpm", metadata.capacity.request_rpm),
1428        ("capacity.concurrency", metadata.capacity.concurrency),
1429    ] {
1430        if let Some(value) = value
1431            && value > JSON_SAFE_INTEGER_MAX
1432        {
1433            return Err(format!(
1434                "model {alias:?}: {field} exceeds OpenRouter's JSON safe-integer maximum"
1435            ));
1436        }
1437    }
1438    for (field, value) in [
1439        ("max_prompt_length", metadata.max_prompt_length),
1440        ("max_output_length", metadata.max_output_length),
1441        ("default_output_length", metadata.default_output_length),
1442        ("capacity.prompt_tpm", metadata.capacity.prompt_tpm),
1443        (
1444            "capacity.cached_prompt_tpm",
1445            metadata.capacity.cached_prompt_tpm,
1446        ),
1447        ("capacity.completion_tpm", metadata.capacity.completion_tpm),
1448        ("capacity.request_rpm", metadata.capacity.request_rpm),
1449        ("capacity.concurrency", metadata.capacity.concurrency),
1450    ] {
1451        if value == Some(0) {
1452            return Err(format!(
1453                "model {alias:?}: {field} must be greater than zero when declared"
1454            ));
1455        }
1456    }
1457    if let (Some(default), Some(maximum)) =
1458        (metadata.default_output_length, metadata.max_output_length)
1459        && default > maximum
1460    {
1461        return Err(format!(
1462            "model {alias:?}: default_output_length {default} exceeds max_output_length {maximum}"
1463        ));
1464    }
1465    if metadata.default_output_length.is_some() && metadata.max_output_length.is_none() {
1466        return Err(format!(
1467            "model {alias:?}: default_output_length requires max_output_length"
1468        ));
1469    }
1470    if let Some(discount) = metadata.discount_to_user
1471        && (!discount.is_finite() || discount >= 1.0)
1472    {
1473        return Err(format!(
1474            "model {alias:?}: discount_to_user must be finite and less than 1"
1475        ));
1476    }
1477    if metadata
1478        .openrouter_slug
1479        .as_deref()
1480        .is_some_and(str::is_empty)
1481    {
1482        return Err(format!(
1483            "model {alias:?}: openrouter_slug must not be empty when declared"
1484        ));
1485    }
1486    for dc in &metadata.datacenters {
1487        if dc.country_code.len() != 2 || !dc.country_code.bytes().all(|b| b.is_ascii_uppercase()) {
1488            return Err(format!(
1489                "model {alias:?}: datacenter country_code {:?} must be two uppercase ASCII letters",
1490                dc.country_code
1491            ));
1492        }
1493    }
1494    Ok(())
1495}
1496
1497/// Boot validation for the vendor-recommended sampling defaults (lane/vendor-default-sampling,
1498/// 2026-08-19). Same posture as `default_reasoning_effort`: FAIL BEFORE GPU LOAD. A bad number
1499/// here would otherwise apply to every omitting client on a box that came back under the
1500/// watchdog, which is the worst possible place to discover a typo.
1501///
1502/// Ranges are the real API ranges, not taste:
1503/// - `default_temperature` must be FINITE, > 0.0, <= 2.0. Zero is refused on purpose — see the
1504///   field docs: a zero default is greedy-by-default wearing a config hat, and it is exactly
1505///   the hazard the owner ruled out. Greedy is reached by an explicit client `temperature: 0`.
1506/// - `default_top_p` in (0.0, 1.0]; 1.0 = disabled, 0.0 would mask every token.
1507/// - `default_top_k` 0 = disabled (keep all); any positive k is a real truncation.
1508/// - `default_min_p` in [0.0, 1.0); 0.0 = disabled, 1.0 would keep only the argmax.
1509/// - `default_presence_penalty` / `default_frequency_penalty` in [-2.0, 2.0] (OpenAI's range).
1510/// - `default_repetition_penalty` finite and > 0.0; 1.0 = off. Zero would zero every logit.
1511fn validate_sampling_defaults(
1512    alias: &str,
1513    metadata: &OpenRouterModelMetadata,
1514) -> Result<(), String> {
1515    validate_sampling_arm(
1516        alias,
1517        &[
1518            "default_temperature",
1519            "default_top_p",
1520            "default_min_p",
1521            "default_presence_penalty",
1522            "default_frequency_penalty",
1523            "default_repetition_penalty",
1524        ],
1525        metadata.default_temperature,
1526        metadata.default_top_p,
1527        metadata.default_min_p,
1528        metadata.default_presence_penalty,
1529        metadata.default_frequency_penalty,
1530        metadata.default_repetition_penalty,
1531    )?;
1532    if let Some(arm) = &metadata.non_thinking_sampling {
1533        // A DECLARED-but-empty arm is refused: it would silently hand every
1534        // thinking-off request the bare API-standard defaults while the file looks
1535        // configured. Either recommend something or delete the table.
1536        if arm.is_empty() {
1537            return Err(format!(
1538                "model {alias:?}: non_thinking_sampling declares no fields — declare at \
1539                 least one vendor recommendation or delete the table"
1540            ));
1541        }
1542        validate_sampling_arm(
1543            alias,
1544            &[
1545                "non_thinking_sampling.temperature",
1546                "non_thinking_sampling.top_p",
1547                "non_thinking_sampling.min_p",
1548                "non_thinking_sampling.presence_penalty",
1549                "non_thinking_sampling.frequency_penalty",
1550                "non_thinking_sampling.repetition_penalty",
1551            ],
1552            arm.temperature,
1553            arm.top_p,
1554            arm.min_p,
1555            arm.presence_penalty,
1556            arm.frequency_penalty,
1557            arm.repetition_penalty,
1558        )?;
1559    }
1560    Ok(())
1561}
1562
1563/// The range law for ONE sampling arm — the flat `default_*` keys and the
1564/// `non_thinking_sampling` table go through this same body so the two arms cannot
1565/// drift apart in what they accept (a zero temperature is refused on BOTH, for the
1566/// same greedy-by-default reason). `keys` carries the six TOML key names in field
1567/// order purely so the refusal names the exact key the operator wrote.
1568#[allow(clippy::too_many_arguments)]
1569fn validate_sampling_arm(
1570    alias: &str,
1571    keys: &[&str; 6],
1572    temperature: Option<f32>,
1573    top_p: Option<f32>,
1574    min_p: Option<f32>,
1575    presence_penalty: Option<f32>,
1576    frequency_penalty: Option<f32>,
1577    repetition_penalty: Option<f32>,
1578) -> Result<(), String> {
1579    if let Some(t) = temperature
1580        && (!t.is_finite() || t <= 0.0 || t > 2.0)
1581    {
1582        return Err(format!(
1583            "model {alias:?}: {} {t} must be finite and in (0, 2]. \
1584                 A zero DEFAULT would make greedy decoding the deployment-wide behavior for \
1585                 every request that omits temperature (owner ruling 2026-08-19: we serve the \
1586                 vendor recommendation, not greedy); clients reach greedy by sending an \
1587                 explicit temperature 0.",
1588            keys[0]
1589        ));
1590    }
1591    if let Some(p) = top_p
1592        && (!p.is_finite() || p <= 0.0 || p > 1.0)
1593    {
1594        return Err(format!(
1595            "model {alias:?}: {} {p} must be finite and in (0, 1] (1.0 = disabled)",
1596            keys[1]
1597        ));
1598    }
1599    if let Some(m) = min_p
1600        && (!m.is_finite() || !(0.0..1.0).contains(&m))
1601    {
1602        return Err(format!(
1603            "model {alias:?}: {} {m} must be finite and in [0, 1) (0.0 = disabled)",
1604            keys[2]
1605        ));
1606    }
1607    for (field, value) in [(keys[3], presence_penalty), (keys[4], frequency_penalty)] {
1608        if let Some(v) = value
1609            && (!v.is_finite() || !(-2.0..=2.0).contains(&v))
1610        {
1611            return Err(format!(
1612                "model {alias:?}: {field} {v} must be finite and in [-2, 2]"
1613            ));
1614        }
1615    }
1616    if let Some(r) = repetition_penalty
1617        && (!r.is_finite() || r <= 0.0)
1618    {
1619        return Err(format!(
1620            "model {alias:?}: {} {r} must be finite and \
1621             greater than zero (1.0 = off)",
1622            keys[5]
1623        ));
1624    }
1625    Ok(())
1626}
1627
1628fn load_openrouter_metadata(
1629    models: &[(String, String, Option<String>)],
1630) -> Result<
1631    (
1632        HashMap<String, OpenRouterModelMetadata>,
1633        Option<ProviderMetadata>,
1634    ),
1635    String,
1636> {
1637    let path = match std::env::var("MEMRA_MODEL_METADATA") {
1638        Ok(path) => path,
1639        Err(_) => return Ok((HashMap::new(), None)),
1640    };
1641    let p = std::path::Path::new(&path);
1642    if !p.is_file() {
1643        return Err(format!(
1644            "MEMRA_MODEL_METADATA={path:?} is not an existing TOML file"
1645        ));
1646    }
1647    let text =
1648        std::fs::read_to_string(p).map_err(|e| format!("MEMRA_MODEL_METADATA {path:?}: {e}"))?;
1649    let (metadata, provider) = OpenRouterMetadataFile::parse(&text)
1650        .map_err(|e| format!("MEMRA_MODEL_METADATA {path:?}: {e}"))?;
1651    for alias in metadata.keys() {
1652        if !models.iter().any(|(name, _, _)| name == alias) {
1653            return Err(format!(
1654                "MEMRA_MODEL_METADATA {path:?}: model alias {alias:?} is not present in MEMRA_MODELS"
1655            ));
1656        }
1657    }
1658    eprintln!(
1659        "[server] OpenRouter metadata loaded: {} model(s) from {path}",
1660        metadata.len()
1661    );
1662    Ok((metadata, provider))
1663}
1664
1665#[derive(Clone)]
1666struct AppState {
1667    cmd_tx: Sender<Cmd>,
1668    models: Arc<Vec<String>>,
1669    caps: Arc<HashMap<String, ModelCaps>>,
1670    openrouter_metadata: Arc<HashMap<String, OpenRouterModelMetadata>>,
1671    /// Contract-v2 provider identity from the metadata file (None = no provider block).
1672    provider_metadata: Arc<Option<ProviderMetadata>>,
1673    /// Optional admission + usage accounting behind the metering seam. Terminal usage is
1674    /// synced before the HTTP completion is published; the CUDA-owner worker never performs
1675    /// accounting I/O. None ⇔ no accounting configured (the old `request_ledger: None`).
1676    /// The stock binary wires `ledger::Ledger`; limits enforcement (the old
1677    /// `tenant_budgets`) is the same object answering `enforces_limits()`.
1678    metering: Option<Arc<dyn metering::Metering>>,
1679    /// HTTP-side tokenizer copies used only when prepaid enforcement is enabled. Reservations
1680    /// price the same rendered prompt before worker admission, without moving auth into worker.rs.
1681    budget_tokenizers: Option<Arc<HashMap<String, Arc<Tokenizer>>>>,
1682    /// Immutable request-auth sources resolved before model load. The keyring itself
1683    /// hot-reloads internally; the source selection must not drift after bind validation.
1684    api_auth: ApiAuth,
1685    /// Metrics are open only for the no-key loopback development shape.
1686    metrics_auth: MetricsAuth,
1687    metrics: SharedMetrics,
1688    /// live per-lane in-flight request gauge (HTTP-layer view: submitted and not yet
1689    /// finished, queued-at-worker included) — drives the X-RateLimit-* headers and the
1690    /// graceful-drain completion barrier (serve-tail lane, gap-scan F11/F12).
1691    inflight: InflightCounts,
1692    /// per-tenant in-flight gauge (lane/api-keys): keyed by tenant id, same RAII life as
1693    /// the lane gauge — drives per-key rate-limit overrides + their headers.
1694    tenant_inflight: TenantGauge,
1695    /// inference liveness (lane/serve-hardening, G5): the GPU worker's heartbeat + phase +
1696    /// fault latches, shared with the worker thread and the Xid watcher. /health, /livez and
1697    /// /readyz read ONLY this — never "the process is up".
1698    health: health::SharedHealth,
1699    /// dead-darklane background job observability (lane/darklane-training): the runner's
1700    /// shared counters + its yield mode, for the /metrics "bg" block. None when MEMRA_BG_JOB
1701    /// is unset — the block is absent and the payload byte-identical to pre-lane.
1702    bg: Option<(Arc<darklane::BgJobState>, &'static str)>,
1703}
1704
1705impl AppState {
1706    /// THE per-request vendor-defaults lookup: every surface handler resolves this model's
1707    /// omitted-field sampling defaults through this one body (operator metadata first, arch
1708    /// caps second — `SamplingDefaults::resolve`). Handlers call this instead of composing
1709    /// the two sources at their own call site so a surface CANNOT quietly consult fewer
1710    /// sources than its siblings: that asymmetry is exactly how `/v1/completions` used to
1711    /// ship temperature 1.0 against the Step-3.7 arch caps (0.5/0.9) the chat path applied
1712    /// (hermes `d991b51699218285`; the resolver itself landed with
1713    /// lane/vendor-default-sampling, 8e9f37a1b7). The worker-truth teeth live in
1714    /// `same_omitted_request_resolves_identically_on_all_four_surfaces`.
1715    ///
1716    /// Returns BOTH vendor arms (lane/per-mode-sampling, 2026-08-24); which one a request
1717    /// gets is decided by its resolved thinking mode inside the one builder
1718    /// (`ModelSamplingDefaults::for_mode`), never at a surface's own call site.
1719    fn sampling_defaults(&self, model: &str) -> ModelSamplingDefaults {
1720        ModelSamplingDefaults::resolve(self.openrouter_metadata.get(model), self.caps.get(model))
1721    }
1722}
1723
1724#[derive(Clone, Default)]
1725struct ApiAuth {
1726    keyring: Option<&'static auth::KeyStore>,
1727    single_key: Option<Arc<str>>,
1728}
1729
1730impl ApiAuth {
1731    fn from_env() -> Result<ApiAuth, String> {
1732        let single_key = match std::env::var("MEMRA_API_KEY") {
1733            Ok(key) if key.is_empty() => return Err("MEMRA_API_KEY must not be empty".into()),
1734            Ok(key) => Some(Arc::from(key)),
1735            Err(std::env::VarError::NotPresent) => None,
1736            Err(std::env::VarError::NotUnicode(_)) => {
1737                return Err("MEMRA_API_KEY must be valid UTF-8".into());
1738            }
1739        };
1740        Ok(ApiAuth {
1741            keyring: auth::global(),
1742            single_key,
1743        })
1744    }
1745
1746    fn configured(&self) -> bool {
1747        self.keyring.is_some() || self.single_key.is_some()
1748    }
1749}
1750
1751#[derive(Clone, Default)]
1752struct MetricsAuth {
1753    required: bool,
1754    token: Option<Arc<str>>,
1755}
1756
1757impl MetricsAuth {
1758    fn new(bind_loopback: bool, api_auth_configured: bool, token: Option<String>) -> MetricsAuth {
1759        let token = token.map(Arc::from);
1760        MetricsAuth {
1761            required: !bind_loopback || api_auth_configured || token.is_some(),
1762            token,
1763        }
1764    }
1765}
1766
1767fn resolve_bind_addr(addr: &str) -> Result<(SocketAddr, bool), String> {
1768    let mut resolved = addr
1769        .to_socket_addrs()
1770        .map_err(|e| format!("MEMRA_ADDR={addr:?} cannot be resolved: {e}"))?;
1771    let first = resolved
1772        .next()
1773        .ok_or_else(|| format!("MEMRA_ADDR={addr:?} resolved to no socket addresses"))?;
1774    let mut loopback = first.ip().to_canonical().is_loopback();
1775    for socket in resolved {
1776        loopback &= socket.ip().to_canonical().is_loopback();
1777    }
1778    Ok((first, loopback))
1779}
1780
1781fn bind_is_loopback(addr: &str) -> Result<bool, String> {
1782    resolve_bind_addr(addr).map(|(_, loopback)| loopback)
1783}
1784
1785fn validate_bind_security(
1786    addr: &str,
1787    api_auth_configured: bool,
1788    allow_open_bind: bool,
1789) -> Result<bool, String> {
1790    let loopback = bind_is_loopback(addr)?;
1791    if !loopback && !api_auth_configured && !allow_open_bind {
1792        return Err(format!(
1793            "refusing unauthenticated non-loopback bind {addr:?}; configure MEMRA_API_KEY or \
1794             MEMRA_API_KEYS, or set MEMRA_ALLOW_OPEN_BIND=1 for an explicit development override"
1795        ));
1796    }
1797    Ok(loopback)
1798}
1799
1800// ---- rate-limit headers (serve-tail lane, 2026-08-04; gap-scan F12) ----
1801//
1802// X-RateLimit-Limit / -Remaining / -Reset on /v1/completions and /v1/chat/completions,
1803// with CONCURRENCY-SLOT semantics (this server admission-caps concurrent sessions; it has
1804// no request/min or token/min budget to report — inventing one would be dishonest):
1805//   Limit     = the lane's configured admission cap — the same values the worker's own
1806//               admission gate enforces (interactive: MEMRA_MAX_SESSIONS batched /
1807//               MAX_ACTIVE legacy; judge/harvest: LanePolicy max_sessions).
1808//   Remaining = free slots at submission time (cap minus in-flight, this request
1809//               included). Interactive beyond the cap QUEUES (never shed), so Remaining 0
1810//               means "you will wait", not "you will be rejected".
1811//   Reset     = seconds until a slot is ESTIMATED free: 0 while slots are free; else the
1812//               live meter's mean service time (tokens/request x p50 step latency) when
1813//               it has signal, else MEMRA_RL_RESET_S (default 2). Honestly coarse — a
1814//               hint, not a promise.
1815// Dark-lane 429 sheds carry the same trio (Retry-After was already there).
1816
1817type InflightCounts = Arc<[std::sync::atomic::AtomicUsize; 3]>;
1818
1819/// Per-tenant in-flight gauge (lane/api-keys): tenant id -> live request count. Entries
1820/// are removed at zero so the map stays bounded by concurrent tenants, not tenant history.
1821type TenantGauge = Arc<std::sync::Mutex<HashMap<String, usize>>>;
1822
1823/// RAII in-flight slot: increments the lane + tenant gauges at submission, decrements
1824/// both when the response is complete — dropped at handler exit (blocking) or when the
1825/// SSE stream finishes/disconnects (moved into the stream).
1826struct InflightGuard {
1827    counts: InflightCounts,
1828    idx: usize,
1829    tenants: TenantGauge,
1830    tenant: String,
1831}
1832
1833impl InflightGuard {
1834    /// Atomically enforce a binding tenant cap, then return the guard + the (lane, tenant)
1835    /// in-flight counts INCLUDING this request. The tenant mutex closes the two-arrivals-at-
1836    /// once race: at cap, exactly one request wins and the other returns the existing count.
1837    fn try_acquire(
1838        counts: InflightCounts,
1839        lane: lanes::Lane,
1840        tenants: TenantGauge,
1841        tenant: &str,
1842        tenant_cap: Option<usize>,
1843    ) -> Result<(Self, usize, usize), usize> {
1844        let idx = lane.idx();
1845        let nt = {
1846            let mut m = tenants.lock().unwrap();
1847            let e = m.entry(tenant.to_string()).or_insert(0);
1848            if tenant_cap.is_some_and(|cap| *e >= cap) {
1849                return Err(*e);
1850            }
1851            *e += 1;
1852            *e
1853        };
1854        let n = counts[idx].fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1;
1855        Ok((
1856            InflightGuard {
1857                counts,
1858                idx,
1859                tenants,
1860                tenant: tenant.to_string(),
1861            },
1862            n,
1863            nt,
1864        ))
1865    }
1866}
1867
1868impl Drop for InflightGuard {
1869    fn drop(&mut self) {
1870        self.counts[self.idx].fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
1871        let mut m = self.tenants.lock().unwrap();
1872        if let Some(e) = m.get_mut(&self.tenant) {
1873            *e -= 1;
1874            if *e == 0 {
1875                m.remove(&self.tenant);
1876            }
1877        }
1878    }
1879}
1880
1881/// The lane's configured admission cap — mirrors the worker's admission gate exactly
1882/// (worker.rs step 2): interactive = MEMRA_MAX_SESSIONS (64) batched / MAX_ACTIVE legacy;
1883/// judge/harvest = LanePolicy::from_env().max_sessions. Read once.
1884fn lane_cap(lane: lanes::Lane) -> usize {
1885    static CAPS: std::sync::OnceLock<[usize; 3]> = std::sync::OnceLock::new();
1886    CAPS.get_or_init(|| {
1887        let batching = std::env::var("MEMRA_SERVE_BATCH")
1888            .map(|v| v != "0")
1889            .unwrap_or(true);
1890        let interactive = if batching {
1891            std::env::var("MEMRA_MAX_SESSIONS")
1892                .ok()
1893                .and_then(|v| v.parse().ok())
1894                .unwrap_or(64)
1895        } else {
1896            worker::MAX_ACTIVE
1897        };
1898        let p = lanes::LanePolicy::from_env();
1899        [interactive, p.max_sessions[1], p.max_sessions[2]]
1900    })[lane.idx()]
1901}
1902
1903/// Coarse next-slot estimate (seconds): mean tokens/request x p50 step latency from the
1904/// live meter when it has signal, else the MEMRA_RL_RESET_S static (default 2).
1905fn reset_estimate_s(m: &worker::Metrics) -> u64 {
1906    if m.completed > 0 && m.step_p50_ms > 0.0 {
1907        let mean_toks = m.tokens_out as f64 / m.completed as f64;
1908        return ((mean_toks * m.step_p50_ms as f64 / 1000.0).ceil() as u64).clamp(1, 600);
1909    }
1910    static D: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
1911    *D.get_or_init(|| {
1912        std::env::var("MEMRA_RL_RESET_S")
1913            .ok()
1914            .and_then(|v| v.parse().ok())
1915            .unwrap_or(2)
1916    })
1917}
1918
1919// ---- request deadline + deadline-aware admission (lane/deadline-billing-20260823) --------
1920//
1921// Owner ruling (2026-08-23): "we can add a timeout param to the api with default timeout
1922// documented correctly, and if the time pass and we didnt responed in time we fail and we
1923// dont bill. if the non response is our fault we should not bill. we need to have
1924// backpressure and circut breaker."
1925//
1926// The circuit breaker itself lives at the router (per-isolate breaker + load spill on the
1927// X-RateLimit readings); THIS side's whole contribution to it is honest, prompt 429s with
1928// Retry-After. Do not build a second breaker here.
1929
1930/// `timeout_ms` bounds. The 90 s maximum is a PLATFORM fact, not a preference: Cloudflare's
1931/// proxy returns 524 at ~100 s of time-to-headers for a non-streaming response, so any
1932/// promise past 90 s would be broken upstream of this server no matter what it does. The
1933/// default equals the maximum — "we answer inside 90 s or you don't pay" is the documented
1934/// contract for every request, including ones that never heard of the parameter.
1935pub(crate) const TIMEOUT_MS_MIN: u64 = 1_000;
1936pub(crate) const TIMEOUT_MS_MAX: u64 = 90_000;
1937pub(crate) const TIMEOUT_MS_DEFAULT: u64 = 90_000;
1938
1939/// `MEMRA_TIMEOUT_MS_MAX` — measurement-cell override of the deadline ceiling (docs/FLAGS.md
1940/// row of the same name). The 90 s ceiling is a PLATFORM fact of the fronted product route
1941/// (Cloudflare 524 at ~100 s of time-to-headers), so raising it is only honest on a
1942/// direct-to-server connection, which is exactly the offline capacity/prefill measurement
1943/// shape it exists for (lane/glm53-1m-demo: a ~1M-token monolithic prime runs for hours, and
1944/// that cell's question is capacity and correctness, not latency). Unset, unparseable, or
1945/// below `TIMEOUT_MS_MIN` => the shipped ceiling, behavior byte-identical to before this
1946/// function existed. When set, the default follows it, preserving the documented
1947/// "default equals the maximum" contract for requests that never pass the parameter.
1948pub(crate) fn timeout_ms_max() -> u64 {
1949    static V: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
1950    *V.get_or_init(|| {
1951        std::env::var("MEMRA_TIMEOUT_MS_MAX")
1952            .ok()
1953            .and_then(|s| s.parse::<u64>().ok())
1954            .filter(|&ms| ms >= TIMEOUT_MS_MIN)
1955            .unwrap_or(TIMEOUT_MS_MAX)
1956    })
1957}
1958
1959/// Validate `timeout_ms` (all four surfaces call this ONE body — standard-surface law).
1960/// Absent/null => the documented default. Wrong type or out of range => the named-400
1961/// message, which always states the range and the streaming escape hatch.
1962pub(crate) fn parse_timeout_ms(v: Option<&serde_json::Value>) -> Result<u64, String> {
1963    let max = timeout_ms_max();
1964    let Some(v) = v.filter(|v| !v.is_null()) else {
1965        // Default equals the maximum, including under the measurement-cell override.
1966        return Ok(max);
1967    };
1968    let Some(ms) = v.as_u64() else {
1969        return Err(format!(
1970            "timeout_ms must be an integer number of milliseconds in \
1971             {TIMEOUT_MS_MIN}..={max}, got {v}; for work longer than \
1972             {max} ms use \"stream\": true — the deadline then bounds only the \
1973             time to first token and the stream may run as long as it needs"
1974        ));
1975    };
1976    if !(TIMEOUT_MS_MIN..=max).contains(&ms) {
1977        return Err(format!(
1978            "timeout_ms {ms} is outside the accepted range \
1979             {TIMEOUT_MS_MIN}..={max} (milliseconds). {max} is a \
1980             platform ceiling, not a preference: the fronting proxy fails a non-streaming \
1981             response whose headers take ~100 s (HTTP 524), so promising more would be a \
1982             lie. For work longer than {max} ms use \"stream\": true — the \
1983             deadline then bounds only the time to first token and the stream may run as \
1984             long as it needs"
1985        ));
1986    }
1987    Ok(ms)
1988}
1989
1990/// One request's effective deadline: the instant it expires plus the declared value (for
1991/// error messages that must name the deadline the caller actually got).
1992#[derive(Clone, Copy)]
1993pub(crate) struct RequestDeadline {
1994    pub(crate) at: tokio::time::Instant,
1995    pub(crate) ms: u64,
1996}
1997
1998impl RequestDeadline {
1999    pub(crate) fn starting_now(ms: u64) -> Self {
2000        Self {
2001            at: tokio::time::Instant::now() + std::time::Duration::from_millis(ms),
2002            ms,
2003        }
2004    }
2005
2006    pub(crate) fn remaining(&self) -> std::time::Duration {
2007        self.at
2008            .saturating_duration_since(tokio::time::Instant::now())
2009    }
2010}
2011
2012/// 408 for a missed deadline: standard error object, `type: "timeout"`,
2013/// `code: "deadline_exceeded"`, message naming the effective deadline and the billing
2014/// promise. 408 is deliberately retryable (exempt from `x-should-retry: false` — SDKs
2015/// retry it by default) and carries no Retry-After: the miss says nothing about when a
2016/// retry would fit, and a made-up window would be a promise this server cannot keep.
2017pub(crate) fn deadline_exceeded_response(ms: u64, stream: bool) -> Response {
2018    let what = if stream {
2019        "the first token was produced"
2020    } else {
2021        "the response completed"
2022    };
2023    let msg = format!(
2024        "deadline of {ms} ms (timeout_ms; default {TIMEOUT_MS_DEFAULT}) elapsed before \
2025         {what}; generation was cancelled and this request is not billed"
2026    );
2027    error_response_coded(
2028        StatusCode::REQUEST_TIMEOUT,
2029        &msg,
2030        "timeout",
2031        Some("timeout_ms"),
2032        Some("deadline_exceeded"),
2033    )
2034}
2035
2036// ---- non-streaming feasibility gate (lane/deadline-partial-20260826) ---------------
2037//
2038// Owner report 2026-08-26: "we have an issue with non streaming and timeouts, if someone
2039// sends 30k token input, he get a timeout ... thats a customer expirience", and the
2040// ruling: "the 90s cap doesnt make sense, it should or return in batches that it can work
2041// under 90s or limit is full context".
2042//
2043// MEASURED SHAPE (darklanes research/nonstream-deadline-20260826): at 30,278 prompt
2044// tokens through the customer path, non-streaming answered 200 at 4096 out (52.0 s),
2045// 5120 (61.9 s) and 6144 (71.5 s), and 408'd at 8192 (90.7 s) and 16384 (91.5 s), while
2046// the SAME 8192-token work streamed 200 in 93.8 s — past the deadline. So the wall clock
2047// never bounded the box, only one response shape, and 90 s of generated tokens were
2048// discarded to produce the error.
2049//
2050// Two gates answer the ruling. This one is the "limit is knowable" half: refuse a
2051// non-streaming request we can SEE will not finish, immediately, naming the max_tokens
2052// that fits — instead of burning the full deadline and discarding the work. The other
2053// half (deliver what was generated when the deadline lands anyway) is in
2054// `blocking_response_with_receipt`.
2055//
2056// WHY A CONSERVATIVE ESTIMATE PLUS A MARGIN, not a promise: throughput is shape-dependent
2057// (the same box does ~100 tok/s on verbose prose and 300+ on digits), so a tight estimate
2058// would refuse requests that would have succeeded — and a false refusal is worse than a
2059// slow success. The floors below are deliberately BELOW anything measured, and the gate
2060// only fires when even the pessimistic estimate exceeds the deadline by MARGIN. On the
2061// measured ladder that boundary lands between 6144 (allowed; really 71.5 s) and 8192
2062// (refused; really a 408), which is the behaviour the receipts ask for.
2063//
2064// INDUSTRY CHECK (owner: "check how other enddoints handle non streaming answers"):
2065// Anthropic enforces the same idea client-side — its SDK raises
2066// "Streaming is required for operations that may take longer than 10 minutes" BEFORE
2067// sending — and OpenAI, Google, Azure and the hosted resellers all decline to publish a server-side duration
2068// ceiling and push long work to streaming or an async/batch surface. Refusing early with
2069// an actionable message is the precedented behaviour; silently truncating is not.
2070
2071/// Pessimistic prefill rate for the feasibility estimate, tokens/second. The api-router
2072/// uses the same 2k floor for its own header-timeout budget; measured prefill on the
2073/// serving cards is ~2.9k tok/s at 30k tokens, so this under-promises on purpose.
2074/// Override: `MEMRA_PREFILL_FLOOR_TOK_S`.
2075pub(crate) const PREFILL_FLOOR_TOK_S: u64 = 2_000;
2076
2077/// Pessimistic decode rate for the feasibility estimate, tokens/second. The slowest arm
2078/// measured through the customer path on the current fleet is ~100 tok/s (verbose prose at
2079/// 30k context); 60 leaves room for a busier box without refusing honest work.
2080/// Override: `MEMRA_DECODE_FLOOR_TOK_S`.
2081pub(crate) const DECODE_FLOOR_TOK_S: u64 = 60;
2082
2083/// How far past the deadline the pessimistic estimate must land before this gate refuses,
2084/// in percent. 150 = "refuse only when even the floor-rate estimate needs 1.5x the
2085/// deadline"; anything closer is attempted and covered by partial delivery.
2086pub(crate) const DEADLINE_INFEASIBLE_MARGIN_PCT: u64 = 150;
2087
2088/// A BOOLEAN flag, which needs its own reader precisely BECAUSE `env_u64` filters to
2089/// POSITIVE values: reading an off-switch through that reader made `=0` fall back to the
2090/// default, so the documented rollback seam did nothing. Caught by the bench gate — arm 7
2091/// ran with `MEMRA_NONSTREAM_DEADLINE_GATE=0` set and was still refused — which is the only
2092/// reason the FLAGS.md row is not a lie. `0`/`off`/`false` = off; anything else = on.
2093fn env_flag_on(name: &'static str, default_on: bool) -> bool {
2094    match std::env::var(name) {
2095        Ok(v) => !matches!(
2096            v.trim().to_ascii_lowercase().as_str(),
2097            "0" | "off" | "false"
2098        ),
2099        Err(_) => default_on,
2100    }
2101}
2102
2103/// A POSITIVE numeric knob (a rate): zero and garbage fall back to the default, because a
2104/// zero rate would divide by zero in the estimate. NEVER read a boolean through this.
2105pub(crate) fn env_u64(name: &'static str, default: u64) -> u64 {
2106    std::env::var(name)
2107        .ok()
2108        .and_then(|v| v.parse::<u64>().ok())
2109        .filter(|v| *v > 0)
2110        .unwrap_or(default)
2111}
2112
2113/// Prompt size in tokens for the feasibility estimate ONLY — never for billing, never for
2114/// admission accounting, both of which count with the real tokenizer at their own sites.
2115///
2116/// Exact when the caller sent `prompt_ids` or a budget tokenizer for this model is loaded
2117/// (production always has one). The character fallback DELIBERATELY UNDER-COUNTS at
2118/// `bytes / CHARS_PER_TOKEN_FLOOR`: an over-count inflates the prefill term and refuses
2119/// requests that would have succeeded, while an under-count merely lets a doomed request
2120/// through to partial delivery. The bench gate caught this — a bytes/4 proxy read a real
2121/// 30,278-token prompt as 51,277 (that text runs ~6.8 chars/token), a 69% over-count in
2122/// the false-refusal direction.
2123const CHARS_PER_TOKEN_FLOOR: usize = 6;
2124
2125pub(crate) fn prompt_tokens_estimate(
2126    request: &worker::Request,
2127    tokenizer: Option<&Tokenizer>,
2128) -> u64 {
2129    if !request.prompt_ids.is_empty() {
2130        return request.prompt_ids.len() as u64;
2131    }
2132    let mut text = String::new();
2133    text.push_str(&request.prompt_text);
2134    for turn in &request.chat_turns {
2135        text.push_str(&turn.content);
2136    }
2137    for tool in &request.tools_json {
2138        text.push_str(tool);
2139    }
2140    if let Some(tokenizer) = tokenizer {
2141        return tokenizer.encode(text.as_str(), false).len() as u64;
2142    }
2143    (text.len() / CHARS_PER_TOKEN_FLOOR) as u64
2144}
2145
2146/// The `max_tokens` that WOULD fit this request's remaining deadline at the floor rates,
2147/// after paying for prefill. `None` when prefill alone cannot fit — that request has no
2148/// feasible completion length at all.
2149pub(crate) fn deadline_fitting_max_tokens(prompt_tokens: u64, remaining_ms: u64) -> Option<u64> {
2150    let prefill_ms = prompt_tokens
2151        .saturating_mul(1_000)
2152        .checked_div(env_u64("MEMRA_PREFILL_FLOOR_TOK_S", PREFILL_FLOOR_TOK_S))
2153        .unwrap_or(u64::MAX);
2154    let decode_ms = remaining_ms.checked_sub(prefill_ms)?;
2155    if decode_ms == 0 {
2156        return None;
2157    }
2158    Some(decode_ms.saturating_mul(env_u64("MEMRA_DECODE_FLOOR_TOK_S", DECODE_FLOOR_TOK_S)) / 1_000)
2159}
2160
2161/// Refuse a non-streaming request whose pessimistic estimate exceeds its deadline by
2162/// `DEADLINE_INFEASIBLE_MARGIN_PCT`. Returns the 400 message; the caller answers with a
2163/// named 400 (`code: "nonstream_deadline_infeasible"`), which costs no slot, opens no
2164/// receipt, and burns no GPU — the point of the gate.
2165///
2166/// Streaming is never gated: its deadline bounds only time-to-first-token and the stream
2167/// may run as long as it needs, which is exactly what this message tells the caller.
2168/// Off switch: `MEMRA_NONSTREAM_DEADLINE_GATE=0` (then an infeasible request runs and is
2169/// covered by partial delivery instead).
2170pub(crate) fn nonstream_deadline_gate(
2171    request: &worker::Request,
2172    stream: bool,
2173    deadline: RequestDeadline,
2174    caller_declared_max_tokens: bool,
2175    tokenizer: Option<&Tokenizer>,
2176) -> Result<(), String> {
2177    if stream || !env_flag_on("MEMRA_NONSTREAM_DEADLINE_GATE", true) {
2178        return Ok(());
2179    }
2180    let max_new = request.params.max_new as u64;
2181    // ONLY a caller-declared max_tokens is judged. An omitted cap is the owner's "limit is
2182    // full context" case: `apply_model_request_limits` has already resolved it to the
2183    // model's max_output (32768 on the q38 registry), so gating it would refuse the single
2184    // MOST COMMON customer shape — a request with no max_tokens at all — over a number the
2185    // caller never chose and cannot act on. The bench gate caught exactly that (arm 5).
2186    // Those requests run and are covered by partial delivery instead.
2187    if !caller_declared_max_tokens || max_new == worker::MAX_NEW_CTX_BOUNDED as u64 || max_new == 0
2188    {
2189        return Ok(());
2190    }
2191    let prompt_tokens = prompt_tokens_estimate(request, tokenizer);
2192    let remaining_ms = deadline.remaining().as_millis() as u64;
2193    let prefill_ms = prompt_tokens.saturating_mul(1_000)
2194        / env_u64("MEMRA_PREFILL_FLOOR_TOK_S", PREFILL_FLOOR_TOK_S).max(1);
2195    let decode_ms = max_new.saturating_mul(1_000)
2196        / env_u64("MEMRA_DECODE_FLOOR_TOK_S", DECODE_FLOOR_TOK_S).max(1);
2197    let est_ms = prefill_ms.saturating_add(decode_ms);
2198    let bound_ms = remaining_ms.saturating_mul(DEADLINE_INFEASIBLE_MARGIN_PCT) / 100;
2199    if est_ms <= bound_ms {
2200        return Ok(());
2201    }
2202    let fits = deadline_fitting_max_tokens(prompt_tokens, remaining_ms);
2203    let advice = match fits {
2204        Some(fits) if fits > 0 => format!(
2205            "lower max_tokens to about {fits} for this prompt, or set \"stream\": true — a \
2206             stream's deadline bounds only the time to first token, so it may run as long \
2207             as it needs"
2208        ),
2209        _ => format!(
2210            "this prompt ({prompt_tokens} tok) needs most of the deadline before the first \
2211             token, so no max_tokens fits: set \"stream\": true"
2212        ),
2213    };
2214    Err(format!(
2215        "a non-streaming request for {max_new} tokens on a ~{prompt_tokens}-token prompt \
2216         needs an estimated ~{}s, which does not fit the {remaining_ms} ms timeout_ms \
2217         deadline (max {TIMEOUT_MS_MAX} ms — a platform ceiling: the fronting proxy fails \
2218         a non-streaming response whose headers take ~100 s). Refused before any GPU work \
2219         rather than after the deadline: {advice}",
2220        est_ms / 1_000,
2221    ))
2222}
2223
2224/// Absolute per-lane queue bound (the backpressure backstop): `MEMRA_MAX_QUEUE_DEPTH`, default
2225/// 4x the selected lane's session cap. At the bound, new requests shed with a 429 (`shed_queue`,
2226/// never billed) instead of entering an unbounded handler/worker channel. Read once.
2227fn max_queue_depth(cap: usize) -> usize {
2228    static D: std::sync::OnceLock<Option<usize>> = std::sync::OnceLock::new();
2229    D.get_or_init(|| {
2230        std::env::var("MEMRA_MAX_QUEUE_DEPTH")
2231            .ok()
2232            .and_then(|v| v.parse().ok())
2233    })
2234    .unwrap_or(cap.saturating_mul(4))
2235}
2236
2237/// Absolute queue-wait ceiling for the interactive lane: `MEMRA_QUEUE_WAIT_CEILING_S`
2238/// (default **0 = OFF by design**, darklanes#5). At `N > 0`, an interactive request whose
2239/// estimated queue wait exceeds `N` seconds sheds 429 (`shed_queue_wait`, never billed)
2240/// with `Retry-After` = the estimate, even when the caller's own deadline could absorb the
2241/// wait. `0`, absent, or unparsable = off (today's silent-queue behavior). Read once.
2242/// Full doc: docs/FLAGS.md row.
2243fn queue_wait_ceiling_s() -> u64 {
2244    static S: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
2245    *S.get_or_init(|| {
2246        std::env::var("MEMRA_QUEUE_WAIT_CEILING_S")
2247            .ok()
2248            .and_then(|v| v.parse().ok())
2249            .unwrap_or(0)
2250    })
2251}
2252
2253/// Deadline-aware admission for the interactive lane, which QUEUES beyond the session cap
2254/// (never sheds) — so before this gate a saturated box accepted every request and simply
2255/// answered late. At submission time (never after — an admitted request is never shed):
2256///
2257///   (a) absolute bound: backlog >= `max_queue_depth` => 429 `shed_queue`;
2258///   (b) deadline test: estimated queue wait > the request's remaining deadline =>
2259///       429 `shed_deadline`, Retry-After = the estimate;
2260///   (c) wait ceiling (opt-in, darklanes#5): `MEMRA_QUEUE_WAIT_CEILING_S` set to N > 0
2261///       and estimated queue wait > N => 429 `shed_queue_wait`, Retry-After = the
2262///       estimate. Independent of the caller's deadline: (b) never fires for a patient
2263///       caller, which is exactly how prod queued 133-137 s in silence.
2264///
2265/// The estimate reuses the SAME machinery as X-RateLimit-Reset (mean tokens/request x p50
2266/// step latency), scaled by how many cap-wide waves of queued requests are ahead. Honestly
2267/// coarse — a hint, not a promise — and the shed messages say so. Judge/harvest lanes
2268/// already shed at cap inside the worker; this gate is interactive-only.
2269/// Atomically reserve one slot in the handler-to-worker queue. The older
2270/// the estimator-based backpressure check it replaced is gone, but a
2271/// successful admission must use this compare-exchange immediately before the
2272/// command send so concurrent handlers cannot all pass one stale snapshot.
2273pub(crate) struct PendingAdmissionGuard {
2274    reserved: bool,
2275    lane: lanes::Lane,
2276}
2277
2278impl PendingAdmissionGuard {
2279    /// Transfer the reservation to the worker. The command-channel gauge is released when the
2280    /// worker pops the command; the hard queue reservation remains until actual model admission
2281    /// or terminal rejection. Dropping a guard before send rolls both counters back.
2282    pub(crate) fn commit(mut self) {
2283        self.reserved = false;
2284        std::mem::forget(self);
2285    }
2286}
2287
2288impl Drop for PendingAdmissionGuard {
2289    fn drop(&mut self) {
2290        if self.reserved {
2291            worker::release_pending_admit();
2292            worker::release_admission_reservation(self.lane);
2293        }
2294    }
2295}
2296
2297#[allow(clippy::result_large_err)] // allow: the fat error type is the diagnostic contract here; boxing it would change the error surface
2298pub(crate) fn reserve_pending_admit(
2299    st: &AppState,
2300    lane: lanes::Lane,
2301    rl: &RateLimit,
2302    deadline: RequestDeadline,
2303) -> Result<PendingAdmissionGuard, (Response, &'static str)> {
2304    reserve_pending_admit_with_ceiling(st, lane, rl, deadline, queue_wait_ceiling_s())
2305}
2306
2307/// `reserve_pending_admit` with the queue-wait ceiling passed explicitly, so both arms of
2308/// the flag are unit-testable in one process (the env read above is a OnceLock). Every
2309/// production ingress goes through the wrapper; only tests call this directly.
2310#[allow(clippy::result_large_err)] // allow: the fat error type is the diagnostic contract here; boxing it would change the error surface
2311fn reserve_pending_admit_with_ceiling(
2312    st: &AppState,
2313    lane: lanes::Lane,
2314    rl: &RateLimit,
2315    deadline: RequestDeadline,
2316    ceiling_s: u64,
2317) -> Result<PendingAdmissionGuard, (Response, &'static str)> {
2318    // The queue bound is a capacity safety property, not a quota-only feature. A key with
2319    // remaining rate-limit headroom can still open hundreds of concurrent requests; applying
2320    // the same bound to every interactive request keeps the normal and DSV4 unbounded channels
2321    // finite even before a per-key window reaches zero.
2322    let cap = lane_cap(lane).max(1);
2323    let bound = max_queue_depth(cap);
2324    let reservations_for_lane = &worker::ADMISSION_RESERVATIONS[lane.idx()];
2325    loop {
2326        let m = st.metrics.lock().map(|m| m.clone()).unwrap_or_default();
2327        let reservations = reservations_for_lane.load(std::sync::atomic::Ordering::Acquire);
2328        // Every production ingress reserves before sending, and step-OOM requeues re-arm their
2329        // lane explicitly. Keep this count lane-local: a harvest flood must never make an
2330        // interactive request appear queued.
2331        let backlog = reservations;
2332        let est_wait_s = reset_estimate_s(&m).saturating_mul((backlog / cap + 1) as u64);
2333        if backlog >= bound {
2334            let msg = format!(
2335                "{} queue is at its bound ({backlog} queued, bound {bound}); this \
2336                 request was not admitted and is not billed; retry after ~{est_wait_s}s (a \
2337                 coarse estimate, not a promise)",
2338                lane.as_str()
2339            );
2340            let resp = retry_contract_response(
2341                (
2342                    StatusCode::TOO_MANY_REQUESTS,
2343                    Json(error_body(
2344                        &msg,
2345                        "rate_limit_error",
2346                        None,
2347                        Some("shed_queue"),
2348                    )),
2349                )
2350                    .into_response(),
2351                Some(est_wait_s),
2352            );
2353            return Err((resp, "shed_queue"));
2354        }
2355        let remaining_ms = deadline.remaining().as_millis() as u64;
2356        // A request with a free slot (remaining > 0 and no queued work) is admitted
2357        // immediately; do not apply the coarse reset estimate to it. Once the lane is
2358        // full or another request is queued, the estimate represents real waiting time.
2359        let waits_for_capacity = rl.remaining == 0 || backlog > 0;
2360        if lane == lanes::Lane::Interactive
2361            && waits_for_capacity
2362            && est_wait_s.saturating_mul(1_000) > remaining_ms
2363        {
2364            let msg = format!(
2365                "estimated queue wait ~{est_wait_s}s exceeds this request's remaining \
2366                 timeout_ms deadline ({remaining_ms} ms); this request was not admitted and \
2367                 is not billed; retry after ~{est_wait_s}s or raise timeout_ms (a coarse \
2368                 estimate, not a promise)"
2369            );
2370            let resp = retry_contract_response(
2371                (
2372                    StatusCode::TOO_MANY_REQUESTS,
2373                    Json(error_body(
2374                        &msg,
2375                        "rate_limit_error",
2376                        None,
2377                        Some("shed_deadline"),
2378                    )),
2379                )
2380                    .into_response(),
2381                Some(est_wait_s),
2382            );
2383            return Err((resp, "shed_deadline"));
2384        }
2385        // QUEUE-WAIT CEILING (darklanes#5, opt-in): the deadline test above never fires
2386        // for a patient caller, so a burst past the session cap queued interactively for
2387        // 133-137 s of pre-header silence on prod (2026-09-01) without a single 429. With
2388        // `MEMRA_QUEUE_WAIT_CEILING_S` = N > 0, a projected wait past N sheds here with the
2389        // same retry contract instead of making the caller discover the wait by enduring
2390        // it. Same trigger posture as (b): only a request that actually waits is judged
2391        // (a free slot with an empty lane admits immediately, estimate not applied).
2392        if lane == lanes::Lane::Interactive
2393            && waits_for_capacity
2394            && ceiling_s > 0
2395            && est_wait_s > ceiling_s
2396        {
2397            let msg = format!(
2398                "estimated queue wait ~{est_wait_s}s exceeds this deployment's queue-wait \
2399                 ceiling ({ceiling_s}s); this request was not admitted and is not billed; \
2400                 retry after ~{est_wait_s}s (a coarse estimate, not a promise)"
2401            );
2402            let resp = retry_contract_response(
2403                (
2404                    StatusCode::TOO_MANY_REQUESTS,
2405                    Json(error_body(
2406                        &msg,
2407                        "rate_limit_error",
2408                        None,
2409                        Some("shed_queue_wait"),
2410                    )),
2411                )
2412                    .into_response(),
2413                Some(est_wait_s),
2414            );
2415            return Err((resp, "shed_queue_wait"));
2416        }
2417        if reservations_for_lane
2418            .compare_exchange(
2419                reservations,
2420                reservations.saturating_add(1),
2421                std::sync::atomic::Ordering::AcqRel,
2422                std::sync::atomic::Ordering::Acquire,
2423            )
2424            .is_ok()
2425        {
2426            // Keep the command-channel signal for speculative-burst yield decisions. It is
2427            // released when the worker pops the command, while the hard reservation above is
2428            // held until actual model admission or terminal rejection.
2429            worker::PENDING_ADMITS.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
2430            return Ok(PendingAdmissionGuard {
2431                reserved: true,
2432                lane,
2433            });
2434        }
2435    }
2436}
2437
2438// ---- graceful drain (serve-tail lane, 2026-08-04; gap-scan F11) ----
2439//
2440// SIGTERM flips the drain flag: new requests on the completion routes get an immediate
2441// 503 + Retry-After (never queued), /health reports "draining" (the LB is_ready signal),
2442// and the drain task waits on the in-flight gauge (the same HTTP-layer counts the
2443// rate-limit headers use — streams hold their slot until fully written) up to
2444// MEMRA_DRAIN_S (default 30s), then shuts the listener down and the process exits 0.
2445// Fleet restarts stop being SIGKILL-class in-flight loss (the chaos-receipt gap).
2446
2447/// Process-wide drain flag (set by the SIGTERM task, read by every admission gate).
2448static DRAINING: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
2449
2450fn draining() -> bool {
2451    DRAINING.load(std::sync::atomic::Ordering::SeqCst)
2452}
2453
2454/// MEMRA_DRAIN_S (default 30): how long a draining server waits for in-flight requests.
2455fn drain_deadline_s() -> u64 {
2456    static D: std::sync::OnceLock<u64> = std::sync::OnceLock::new();
2457    *D.get_or_init(|| {
2458        std::env::var("MEMRA_DRAIN_S")
2459            .ok()
2460            .and_then(|v| v.parse().ok())
2461            .unwrap_or(30)
2462    })
2463}
2464
2465/// 503 for a request that arrived during drain: OpenAI error object + Retry-After
2466/// (the drain window — by then this instance is gone and its replacement is up).
2467///
2468/// Goes through the SAME retry contract as every engine-fault class (G6): a `code` clients can
2469/// branch on, the `retry-after-ms` twin openai-python reads FIRST, and the value clamped to
2470/// 60 s because litellm ignores anything above that and openai-python abandons the retry past
2471/// 120 s. It predates the taxonomy and was the one 503 on the surface still emitting a bare
2472/// `Retry-After` with no code and no ms twin — i.e. a client that trusted `retry-after-ms`
2473/// exclusively saw no window at all on the most predictable outage memra has.
2474fn drain_response() -> Response {
2475    let resp = (
2476        StatusCode::SERVICE_UNAVAILABLE,
2477        Json(error_body(
2478            "server is draining (shutdown in progress); retry",
2479            "server_error",
2480            None,
2481            Some("draining"),
2482        )),
2483    )
2484        .into_response();
2485    retry_contract_response(resp, Some(drain_deadline_s()))
2486}
2487
2488/// One request's header values, computed at submission time (the "at admit" snapshot).
2489struct RateLimit {
2490    limit: usize,
2491    remaining: usize,
2492    reset_s: u64,
2493}
2494
2495impl RateLimit {
2496    /// Per-tenant override law (lane/api-keys): the effective cap is
2497    /// min(tenant_override, global lane cap) — the GLOBAL cap stays authoritative (an
2498    /// override can only narrow, never widen). Remaining is the tighter of the two
2499    /// headrooms (tenant cap minus tenant in-flight vs lane cap minus lane in-flight).
2500    fn at_admit(
2501        lane: lanes::Lane,
2502        n_inflight: usize,
2503        metrics: &SharedMetrics,
2504        tenant: &auth::TenantCtx,
2505        n_tenant: usize,
2506    ) -> Self {
2507        let global = lane_cap(lane);
2508        let Some(t) = tenant.rate_limit.filter(|&t| t < global) else {
2509            return Self::compute(global, n_inflight, metrics);
2510        };
2511        let headroom = t
2512            .saturating_sub(n_tenant)
2513            .min(global.saturating_sub(n_inflight));
2514        // compute() derives remaining as limit - n; feed it the effective occupancy.
2515        Self::compute(t, t - headroom, metrics)
2516    }
2517
2518    fn compute(limit: usize, n_inflight: usize, metrics: &SharedMetrics) -> Self {
2519        let remaining = limit.saturating_sub(n_inflight);
2520        let reset_s = if remaining > 0 {
2521            0
2522        } else {
2523            let m = metrics.lock().map(|m| m.clone()).unwrap_or_default();
2524            reset_estimate_s(&m)
2525        };
2526        RateLimit {
2527            limit,
2528            remaining,
2529            reset_s,
2530        }
2531    }
2532
2533    /// Stamp the X-RateLimit-* trio onto a response.
2534    fn attach(&self, mut resp: Response) -> Response {
2535        let h = resp.headers_mut();
2536        for (k, v) in [
2537            ("x-ratelimit-limit", self.limit as u64),
2538            ("x-ratelimit-remaining", self.remaining as u64),
2539            ("x-ratelimit-reset", self.reset_s),
2540        ] {
2541            if let Ok(v) = axum::http::HeaderValue::from_str(&v.to_string()) {
2542                h.insert(axum::http::HeaderName::from_static(k), v);
2543            }
2544        }
2545        resp
2546    }
2547}
2548
2549/// Take the HTTP-layer request slot or reject a tenant whose configured override is already
2550/// full. Global interactive capacity still queues as before; this gate exists only when the
2551/// key's override is narrower than the lane cap.
2552#[allow(clippy::result_large_err)] // allow: the fat error type is the diagnostic contract here; boxing it would change the error surface
2553fn acquire_request_slot(
2554    st: &AppState,
2555    lane: lanes::Lane,
2556    tenant: &auth::TenantCtx,
2557    env: &Envelope,
2558) -> Result<(InflightGuard, RateLimit), Response> {
2559    let global = lane_cap(lane);
2560    let tenant_cap = tenant.rate_limit.filter(|&cap| cap < global);
2561    match InflightGuard::try_acquire(
2562        st.inflight.clone(),
2563        lane,
2564        st.tenant_inflight.clone(),
2565        &tenant.tenant,
2566        tenant_cap,
2567    ) {
2568        Ok((guard, n_inflight, n_tenant)) => {
2569            let rl = RateLimit::at_admit(lane, n_inflight, &st.metrics, tenant, n_tenant);
2570            Ok((guard, rl))
2571        }
2572        Err(n_tenant) => {
2573            let n_inflight = st.inflight[lane.idx()].load(std::sync::atomic::Ordering::SeqCst);
2574            let rl = RateLimit::at_admit(lane, n_inflight, &st.metrics, tenant, n_tenant);
2575            let error =
2576                worker::EngineError::rate_limit("api key concurrent request limit reached; retry");
2577            Err(rl.attach(with_request_id(&env.id, engine_error_response(&error))))
2578        }
2579    }
2580}
2581
2582/// POST /v1/completions request body.
2583#[derive(Deserialize)]
2584struct CompletionReq {
2585    model: String,
2586    #[serde(default)]
2587    prompt: String,
2588    /// raw token-id prompt (the exact-token validation-gate path; bypasses the tokenizer).
2589    #[serde(default)]
2590    prompt_ids: Vec<u32>,
2591    /// Omitted (gap-scan F2) => context-bounded (session ctx - prompt, model-capped), the
2592    /// OpenAI default-when-omitted semantics — NOT a silent 128-token truncation.
2593    #[serde(default)]
2594    max_tokens: Option<usize>,
2595    /// Omitted (dogfood F4) => NOT 0.0/greedy. `serde(default)` on an f32 yielded 0.0, which
2596    /// silently locked every temperature-omitting client (the owner's own agentic pill) into
2597    /// deterministic argmax: same context in, same token out, identical tool-call cycles
2598    /// forever. Explicit `"temperature": 0` still means greedy — that's a caller decision.
2599    ///
2600    /// `Option`, not `f32` (lane/vendor-default-sampling, 2026-08-19): the resolver must be able
2601    /// to tell "the client said nothing" from "the client said a number", because an omitted
2602    /// field is what the model's own vendor recommendation substitutes for. A bare `f32` cannot
2603    /// express that distinction — which is precisely how this surface came to disagree with
2604    /// `/v1/chat/completions`, where the same fields had already been made `Option`. Every
2605    /// sampling field below is `Option` for the same reason: they resolve through the ONE
2606    /// `resolve_sampler_config` law that all four surfaces share.
2607    #[serde(default)]
2608    temperature: Option<f32>,
2609    #[serde(default)]
2610    top_p: Option<f32>,
2611    /// Not an OpenAI parameter (OpenRouter/HF convention); explicit 0 = disabled = keep all.
2612    #[serde(default)]
2613    top_k: Option<usize>,
2614    /// Not an OpenAI parameter (OpenRouter/HF convention); explicit 0.0 = disabled.
2615    #[serde(default)]
2616    min_p: Option<f32>,
2617    /// OpenAI penalties (gap-scan F3): implemented in SamplerConfig all along, now plumbed.
2618    #[serde(default)]
2619    frequency_penalty: Option<f32>,
2620    #[serde(default)]
2621    presence_penalty: Option<f32>,
2622    /// OpenRouter/HF-convention multiplicative penalty (explicit 1.0 = off).
2623    #[serde(default)]
2624    repetition_penalty: Option<f32>,
2625    /// Omitted (dogfood F4, second half) => a FRESH RANDOM seed per request. `Option`, not
2626    /// `u64`: `serde(default)` gave 0, which is a perfectly valid FIXED seed, so every
2627    /// seed-omitting client replayed one single sampled stream — the same loop the
2628    /// temperature default caused, surviving the temperature fix. OpenAI's `seed` is
2629    /// explicitly best-effort determinism WHEN SUPPLIED; omitting it must not pin the RNG.
2630    #[serde(default)]
2631    seed: Option<u64>,
2632    #[serde(default)]
2633    stop: StopSequences,
2634    /// Unsupported-but-semantic fields (gap-scan F4): captured so they 400 loudly instead
2635    /// of being silently swallowed by serde (policy: clean 400s, not silent downgrades).
2636    #[serde(default)]
2637    logit_bias: Option<serde_json::Value>,
2638    #[serde(default)]
2639    logprobs: Option<serde_json::Value>,
2640    #[serde(default)]
2641    n: Option<usize>,
2642    #[serde(default)]
2643    best_of: Option<usize>,
2644    /// wrap the prompt in the model's chat template (single user turn).
2645    #[serde(default)]
2646    chat: bool,
2647    /// stream tokens via SSE; else return one JSON when done.
2648    #[serde(default)]
2649    stream: bool,
2650    /// optional hard context cap.
2651    #[serde(default)]
2652    max_ctx: Option<usize>,
2653    /// Stable calibration-record identity written only when confidence tracing is enabled.
2654    #[serde(default)]
2655    trace_id: Option<String>,
2656    /// PC-ISO prefix-cache namespace (vLLM `cache_salt` convention, optional): requests
2657    /// only share cached prefixes with requests carrying the SAME salt. Absent/"" = the
2658    /// default single-tenant namespace (pre-PC-ISO behavior). See `cache_namespace`.
2659    #[serde(default)]
2660    cache_salt: Option<String>,
2661    /// SESSION AFFINITY explicit tier (lane/session-affinity): the caller's own name for
2662    /// this conversation. See `affinity_key`. `session_id` is the explicit spelling;
2663    /// `user` is OpenAI's field that real clients already send.
2664    #[serde(default)]
2665    session_id: Option<String>,
2666    #[serde(default)]
2667    user: Option<String>,
2668    /// Request deadline in milliseconds (lane/deadline-billing-20260823) — see
2669    /// `parse_timeout_ms` for the range, the platform ceiling, and the billing promise.
2670    /// Kept as a raw `Value` so a wrong type is OUR named 400, not serde's body-wide one.
2671    #[serde(default)]
2672    timeout_ms: Option<serde_json::Value>,
2673}
2674
2675#[derive(Deserialize)]
2676struct ChatMessage {
2677    role: String,
2678    /// string, null, or an array of `{type:"text",text}` parts (OpenAI content shapes).
2679    #[serde(default)]
2680    content: serde_json::Value,
2681    /// OpenAI assistant-history tool calls, re-rendered into the template on the next turn.
2682    #[serde(default)]
2683    tool_calls: Vec<ReqToolCall>,
2684    /// role:"tool" pairing. The qwen/step dialects pair positionally; the gemma4 tooluse
2685    /// dialect resolves the response NAME by matching this against the assistant call id.
2686    #[serde(default)]
2687    tool_call_id: Option<String>,
2688    /// role:"tool" function name (some clients send it) — gemma4 fallback when the id does
2689    /// not resolve. Harmless to the positional dialects.
2690    #[serde(default)]
2691    name: Option<String>,
2692    /// Assistant-history reasoning echoed back by a stateless client (OpenRouter shape). The
2693    /// gemma4 and dsv4 arms re-render it into the prompt; the qwen arm does NOT.
2694    ///
2695    /// That last part used to be documented as "their templates carry no history-reasoning
2696    /// grammar", and for qwen3.8 that is FALSE (lane/reasoning-schema-20260823): its template
2697    /// reads `message.reasoning_content` and replays it inside a `<think>` block by default. So
2698    /// this field is silently dropped on that dialect where the vendor would have used it, which
2699    /// is a named follow-up — `chat_template_kwargs.preserve_thinking` refuses for the same
2700    /// reason. Recorded here rather than left as a comment that reads as if nothing were missing.
2701    #[serde(default, alias = "reasoning_content")]
2702    reasoning: Option<String>,
2703}
2704
2705#[derive(Deserialize)]
2706struct ReqToolCall {
2707    #[serde(default)]
2708    #[allow(dead_code)]
2709    id: Option<String>,
2710    function: ReqToolFunction,
2711}
2712
2713#[derive(Deserialize)]
2714struct ReqToolFunction {
2715    name: String,
2716    /// OpenAI sends a JSON-encoded STRING; inline objects are accepted too.
2717    #[serde(default)]
2718    arguments: serde_json::Value,
2719}
2720
2721#[derive(Clone, Default, Deserialize)]
2722#[serde(untagged)]
2723enum StopSequences {
2724    One(String),
2725    Many(Vec<String>),
2726    #[default]
2727    None,
2728}
2729
2730impl StopSequences {
2731    /// Empty elements are dropped HERE, at the one ingestion choke point (hermes finding,
2732    /// fixed 2026-08-23): `"".contains`/`find("")` match at every position, so an empty
2733    /// stop element ended every decode at the first token and `truncate_at_stop` cut the
2734    /// whole completion to "". OpenAI treats empty stop strings as invalid; dropping them
2735    /// matches the None/omitted semantics without 400ing batch clients that pad arrays.
2736    fn into_vec(self) -> Vec<String> {
2737        let stops = match self {
2738            Self::One(stop) => vec![stop],
2739            Self::Many(stops) => stops,
2740            Self::None => Vec::new(),
2741        };
2742        stops.into_iter().filter(|s| !s.is_empty()).collect()
2743    }
2744
2745    fn validate(&self) -> Result<(), String> {
2746        let stops: &[String] = match self {
2747            Self::One(stop) => std::slice::from_ref(stop),
2748            Self::Many(stops) => stops,
2749            Self::None => &[],
2750        };
2751        if stops.len() > MAX_STOP_SEQUENCES {
2752            return Err(format!(
2753                "stop accepts at most {MAX_STOP_SEQUENCES} sequences"
2754            ));
2755        }
2756        let mut total = 0usize;
2757        for stop in stops {
2758            let bytes = stop.len();
2759            if bytes > MAX_STOP_SEQUENCE_BYTES {
2760                return Err(format!(
2761                    "each stop sequence must be at most {MAX_STOP_SEQUENCE_BYTES} UTF-8 bytes"
2762                ));
2763            }
2764            total = total
2765                .checked_add(bytes)
2766                .ok_or_else(|| "stop sequence byte count overflowed".to_string())?;
2767        }
2768        if total > MAX_STOP_SEQUENCES_BYTES {
2769            return Err(format!(
2770                "stop sequences must total at most {MAX_STOP_SEQUENCES_BYTES} UTF-8 bytes"
2771            ));
2772        }
2773        Ok(())
2774    }
2775}
2776
2777/// OpenAI-compatible multi-turn chat request. `tools`/`tool_choice`/role:"tool" are accepted
2778/// (serve-tools lane, 2026-08-02): tool schemas render into the model chat template's own
2779/// <tools> branch and emitted `<tool_call>` blocks parse back into OpenAI `tool_calls` — the
2780/// model's GGUF chat template remains the sole source of prompt formatting, and the tools
2781/// path is TEMPLATE + PARSING only (zero engine changes).
2782#[derive(Deserialize)]
2783struct ChatCompletionReq {
2784    model: String,
2785    messages: Vec<ChatMessage>,
2786    /// Omitted (gap-scan F2) => context-bounded (session ctx - prompt, model-capped), the
2787    /// OpenAI default-when-omitted semantics — NOT a silent 128-token truncation.
2788    #[serde(default, alias = "max_completion_tokens")]
2789    max_tokens: Option<usize>,
2790    /// Kept as Option so loaded-model capabilities can apply a provider-published default only
2791    /// when the caller omitted the field. Explicit values, including 0 and 1, remain authoritative.
2792    #[serde(default)]
2793    temperature: Option<f32>,
2794    #[serde(default)]
2795    top_p: Option<f32>,
2796    /// Not an OpenAI parameter (OpenRouter/HF convention); explicit 0 = disabled = keep all.
2797    /// `Option` so a vendor `default_top_k` can fill the OMITTED case while an explicit 0
2798    /// stays an explicit "keep all" (lane/vendor-default-sampling, 2026-08-19).
2799    #[serde(default)]
2800    top_k: Option<usize>,
2801    /// Not an OpenAI parameter (OpenRouter/HF convention); explicit 0.0 = disabled.
2802    #[serde(default)]
2803    min_p: Option<f32>,
2804    /// OpenAI penalties (gap-scan F3): implemented in SamplerConfig all along, now plumbed.
2805    #[serde(default)]
2806    frequency_penalty: Option<f32>,
2807    #[serde(default)]
2808    presence_penalty: Option<f32>,
2809    /// OpenRouter/HF-convention multiplicative penalty (explicit 1.0 = off).
2810    #[serde(default)]
2811    repetition_penalty: Option<f32>,
2812    /// Omitted (dogfood F4, second half) => a FRESH RANDOM seed per request. See CompletionReq.
2813    #[serde(default)]
2814    seed: Option<u64>,
2815    #[serde(default)]
2816    stop: StopSequences,
2817    #[serde(default)]
2818    stream: bool,
2819    #[serde(default)]
2820    max_ctx: Option<usize>,
2821    /// OpenAI `response_format` (constrained decoding, lane/constrained 2026-08-03):
2822    /// `{"type":"text"}` (no-op), `{"type":"json_object"}`, and
2823    /// `{"type":"json_schema","json_schema":{...,"schema":{...}}}` are supported — the
2824    /// grammar masks logits per decode step (llguidance). Unknown types 400 loudly.
2825    #[serde(default)]
2826    response_format: Option<serde_json::Value>,
2827    #[serde(default)]
2828    logit_bias: Option<serde_json::Value>,
2829    #[serde(default)]
2830    logprobs: Option<serde_json::Value>,
2831    #[serde(default)]
2832    top_logprobs: Option<usize>,
2833    #[serde(default)]
2834    n: Option<usize>,
2835    /// OpenAI tool schemas: `[{"type":"function","function":{name,description?,parameters?}}]`.
2836    #[serde(default)]
2837    tools: Vec<serde_json::Value>,
2838    /// "auto" (default) | "none". "required"/named-function need constrained decoding -> 400.
2839    #[serde(default)]
2840    tool_choice: Option<serde_json::Value>,
2841    /// OpenAI reasoning effort — ONE surface, per-arch native mapping (see `parse_think`'s
2842    /// table): low|medium|high = thinking ON at that budget, none|minimal = thinking OFF,
2843    /// absent = the model's own default. Binary-switch templates (qwen enable_thinking,
2844    /// gemma4) take the on/off half; level-consuming templates (step35 `Reasoning:`,
2845    /// hy3 `reasoning_effort:`) also receive the level.
2846    #[serde(default)]
2847    reasoning_effort: Option<String>,
2848    /// OpenRouter object form. Exactly THREE keys are understood — `effort`, `enabled`,
2849    /// `exclude` — and every other key is a named 400 (`parse_reasoning_object`), including
2850    /// `max_tokens`. Until lane/reasoning-schema-20260823 this was a bare `Value` whose
2851    /// unknown keys were silently ignored: `reasoning:{max_tokens:1024}` returned 200 and
2852    /// changed nothing, which is the accepted-and-ignored class the standard-surface law bans.
2853    /// `reasoning.max_tokens` in particular cannot be honoured here by owner ruling — reasoning
2854    /// is output and `max_tokens` is the ONE output budget covering it, so there is no separate
2855    /// reasoning budget to spend against.
2856    #[serde(default)]
2857    reasoning: Option<serde_json::Value>,
2858    /// OpenRouter legacy switch — and on this server it STOPS REASONING rather than hiding it.
2859    ///
2860    /// OWNER RULING (2026-08-23): *"we have to actually reason or not reason"*. Reasoning is
2861    /// compute and output, billed as output, so a flag that merely withheld the text meant we
2862    /// spent the compute, billed the customer, and delivered less than we charged for. That
2863    /// third state — generate, bill, withhold — is gone: `include_reasoning:false` and
2864    /// `reasoning.exclude:true` are now first-class ALIASES of reasoning-off
2865    /// (`reasoning.enabled:false`), mapping into the one schema as exactly that. There is no
2866    /// suppression mode left in the server, so there is nothing to hide because nothing is
2867    /// produced, and the caller gets the cheaper and faster request they asked for.
2868    ///
2869    /// Consequence a caller should know: on a model whose template cannot turn reasoning off,
2870    /// `include_reasoning:false` is now the same named 400 as any other off-request, instead of
2871    /// a 200 that quietly billed for a hidden reasoning block.
2872    #[serde(default)]
2873    include_reasoning: Option<bool>,
2874    /// vLLM/HF-idiom thinking switch, accepted here as a first-class ALIAS of the
2875    /// OpenAI/OpenRouter switch (`reasoning.enabled`) — same precedence, same table
2876    /// (`parse_think`). It exists because the whole vLLM-shaped ecosystem sends it and we
2877    /// used to drop it: `ChatCompletionReq` has no `deny_unknown_fields`, so
2878    /// `enable_thinking:false` was accepted with 200 and silently ignored while the model
2879    /// went on reasoning (lane/reasoning-control-20260823, receipted on the live endpoint).
2880    /// Silent acceptance of an ignored parameter is banned; this field is now wired, and
2881    /// a model whose template cannot honour it REFUSES with a named error.
2882    #[serde(default)]
2883    enable_thinking: Option<bool>,
2884    /// vLLM `chat_template_kwargs`. This server renders templates in Rust rather than
2885    /// executing jinja, so it cannot honour arbitrary kwargs — the ONLY key it understands
2886    /// is `enable_thinking`. Every other key is a loud 400 naming the key, never a silent
2887    /// drop: passing a kwarg that changes nothing is the same defect as `enable_thinking`
2888    /// being ignored, one level down.
2889    #[serde(default)]
2890    chat_template_kwargs: Option<serde_json::Value>,
2891    /// PC-ISO prefix-cache namespace (vLLM `cache_salt` convention, optional): requests
2892    /// only share cached prefixes with requests carrying the SAME salt. Absent/"" = the
2893    /// default single-tenant namespace (pre-PC-ISO behavior). See `cache_namespace`.
2894    #[serde(default)]
2895    cache_salt: Option<String>,
2896    /// SESSION AFFINITY explicit tier — see `CompletionReq::session_id` / `affinity_key`.
2897    #[serde(default)]
2898    session_id: Option<String>,
2899    #[serde(default)]
2900    user: Option<String>,
2901    /// Request deadline in milliseconds (lane/deadline-billing-20260823), identical on all
2902    /// four surfaces (the translators pass it through to this field). See
2903    /// `parse_timeout_ms` for the range, the platform ceiling, and the billing promise.
2904    /// Raw `Value` so a wrong type is OUR named 400, not serde's body-wide one.
2905    #[serde(default)]
2906    timeout_ms: Option<serde_json::Value>,
2907}
2908fn one() -> f32 {
2909    1.0
2910}
2911/// OpenAI's documented default for an omitted `temperature` on every completion surface, and
2912/// the LAST resort in `resolve_sampler_config`: it applies only when neither the client, the
2913/// operator's vendor block, nor the engine's arch caps expressed anything. Kept distinct from
2914/// `one()` so the intent is greppable: this is a COMPAT default, not a coincidence that it
2915/// equals the top_p disable value.
2916fn default_temperature() -> f32 {
2917    1.0
2918}
2919
2920/// Per-model sampling defaults for OMITTED request fields — the vendor's own recommendation
2921/// for this model, resolved once per request (lane/vendor-default-sampling, 2026-08-19).
2922///
2923/// Owner ruling: "we don't have to serve greedy, we measure greedy but we serve what the user
2924/// chooses" / "we default to what are the recommendations" / "greedy can create issues". So the
2925/// value a client gets when it says nothing is the MODEL VENDOR's published recommendation, not
2926/// greedy and not a house guess.
2927///
2928/// Two sources, in this precedence:
2929/// 1. `MEMRA_MODEL_METADATA`'s per-model `default_*` keys — operator-declared for THIS
2930///    deployment, boot-validated, carrying the vendor citation in the TOML comment.
2931/// 2. `ModelCaps`' arch-keyed defaults (`chat_temperature_default` / `chat_top_p_default`) —
2932///    the engine's own built-in knowledge for architectures that publish API defaults
2933///    (step35 = StepFun's 0.5/0.9). Kept as the fallback so a box with no metadata file
2934///    behaves exactly as it did before this lane.
2935///
2936/// A `None` field means "nothing was recommended for this parameter" and falls through to the
2937/// API-standard default. Per the lane brief: where a vendor recommends nothing we leave the
2938/// API-standard value alone rather than inventing one.
2939#[derive(Debug, Clone, Copy, Default, PartialEq)]
2940struct SamplingDefaults {
2941    temperature: Option<f32>,
2942    top_p: Option<f32>,
2943    top_k: Option<usize>,
2944    min_p: Option<f32>,
2945    frequency_penalty: Option<f32>,
2946    presence_penalty: Option<f32>,
2947    repetition_penalty: Option<f32>,
2948}
2949
2950impl SamplingDefaults {
2951    /// Metadata wins over caps: the operator's declaration is about the artifact actually
2952    /// loaded on this box, while the arch cap is a family-level guess made at spawn.
2953    fn resolve(metadata: Option<&OpenRouterModelMetadata>, caps: Option<&ModelCaps>) -> Self {
2954        SamplingDefaults {
2955            temperature: metadata
2956                .and_then(|m| m.default_temperature)
2957                .or_else(|| caps.and_then(|c| c.chat_temperature_default)),
2958            top_p: metadata
2959                .and_then(|m| m.default_top_p)
2960                .or_else(|| caps.and_then(|c| c.chat_top_p_default)),
2961            top_k: metadata.and_then(|m| m.default_top_k),
2962            min_p: metadata.and_then(|m| m.default_min_p),
2963            frequency_penalty: metadata.and_then(|m| m.default_frequency_penalty),
2964            presence_penalty: metadata.and_then(|m| m.default_presence_penalty),
2965            repetition_penalty: metadata.and_then(|m| m.default_repetition_penalty),
2966        }
2967    }
2968}
2969
2970/// BOTH of a model's vendor sampling arms, resolved once per request (lane/per-mode-sampling,
2971/// 2026-08-24). Some vendors publish two recommendations — one for thinking mode, one for
2972/// non-thinking (qwen3.8: 1.0/0.95/20 thinking vs 0.7/0.80/20 + presence 1.5 non-thinking).
2973/// memra used to carry ONE default per model, so a request that turned thinking OFF was
2974/// still served the thinking arm's numbers; per the repo law "served models default to the
2975/// VENDOR's recommendation", the correct default for a thinking-off request whose sampling
2976/// params are unset is the vendor's non-thinking arm.
2977///
2978/// `thinking` is the PRIMARY arm — exactly what `SamplingDefaults::resolve` returned before
2979/// this type existed (flat `default_*` metadata keys, arch caps fallback). `non_thinking` is
2980/// present only when the operator declared a `non_thinking_sampling` table; a single-arm
2981/// model resolves every mode to `thinking` and is byte-identical to before.
2982#[derive(Debug, Clone, Copy, Default, PartialEq)]
2983struct ModelSamplingDefaults {
2984    thinking: SamplingDefaults,
2985    non_thinking: Option<SamplingDefaults>,
2986}
2987
2988impl ModelSamplingDefaults {
2989    fn resolve(metadata: Option<&OpenRouterModelMetadata>, caps: Option<&ModelCaps>) -> Self {
2990        ModelSamplingDefaults {
2991            thinking: SamplingDefaults::resolve(metadata, caps),
2992            // The non-thinking arm is the operator's declaration ALONE — no arch-caps
2993            // fallback and no field-by-field inheritance from the thinking arm. The two
2994            // arms are separate vendor programs; a field the vendor left out of one arm
2995            // falls to the API-standard default exactly like an undeclared flat key.
2996            non_thinking: metadata
2997                .and_then(|m| m.non_thinking_sampling.as_ref())
2998                .map(|arm| SamplingDefaults {
2999                    temperature: arm.temperature,
3000                    top_p: arm.top_p,
3001                    top_k: arm.top_k,
3002                    min_p: arm.min_p,
3003                    frequency_penalty: arm.frequency_penalty,
3004                    presence_penalty: arm.presence_penalty,
3005                    repetition_penalty: arm.repetition_penalty,
3006                }),
3007        }
3008    }
3009
3010    /// THE arm-selection law: the request's RESOLVED thinking mode picks the arm.
3011    /// `NoThink` — produced by any off spelling (`reasoning_effort:"none"|"minimal"`,
3012    /// `enable_thinking:false`, `chat_template_kwargs.enable_thinking:false`,
3013    /// `reasoning:{enabled:false}`, `include_reasoning:false`, Anthropic
3014    /// `thinking.type:"disabled"`), by an operator `default_reasoning_effort = "none"`
3015    /// resolving an unset request, or by the response_format constraint forcing the
3016    /// think switch off — takes the non-thinking arm when one is declared. `Default`
3017    /// deliberately does NOT: it means "the template's own mode", and every model that
3018    /// carries a non-thinking arm today defaults thinking ON; a deployment whose unset
3019    /// case should be non-thinking says so with `default_reasoning_effort = "none"`,
3020    /// which resolves to `NoThink` upstream and lands here. Models without the arm
3021    /// return `thinking` for every mode — the exact pre-lane behavior.
3022    fn for_mode(&self, think: ThinkMode) -> &SamplingDefaults {
3023        match (think, &self.non_thinking) {
3024            (ThinkMode::NoThink, Some(non_thinking)) => non_thinking,
3025            _ => &self.thinking,
3026        }
3027    }
3028
3029    /// A single-arm carrier for surfaces/tests that resolve without per-mode metadata —
3030    /// behaviorally the pre-lane `SamplingDefaults` value, on every mode.
3031    #[cfg(test)] // only test surfaces resolve without per-mode metadata today
3032    fn single(thinking: SamplingDefaults) -> Self {
3033        ModelSamplingDefaults {
3034            thinking,
3035            non_thinking: None,
3036        }
3037    }
3038}
3039
3040/// The client's own sampling expression: `Some` = the client said this, `None` = the client said
3041/// nothing. Every surface funnels its body into this shape so there is exactly ONE place where
3042/// an omitted field becomes a number (standard-surface law: `/v1/completions`,
3043/// `/v1/chat/completions`, `/v1/messages` and `/v1/responses` must not disagree, and the way to
3044/// guarantee that is to give them one resolver rather than three matching ones).
3045#[derive(Debug, Clone, Copy, Default)]
3046struct ClientSampling {
3047    temperature: Option<f32>,
3048    top_p: Option<f32>,
3049    top_k: Option<usize>,
3050    min_p: Option<f32>,
3051    frequency_penalty: Option<f32>,
3052    presence_penalty: Option<f32>,
3053    repetition_penalty: Option<f32>,
3054    seed: Option<u64>,
3055}
3056
3057impl From<&CompletionReq> for ClientSampling {
3058    fn from(r: &CompletionReq) -> Self {
3059        ClientSampling {
3060            temperature: r.temperature,
3061            top_p: r.top_p,
3062            top_k: r.top_k,
3063            min_p: r.min_p,
3064            frequency_penalty: r.frequency_penalty,
3065            presence_penalty: r.presence_penalty,
3066            repetition_penalty: r.repetition_penalty,
3067            seed: r.seed,
3068        }
3069    }
3070}
3071
3072impl From<&ChatCompletionReq> for ClientSampling {
3073    fn from(r: &ChatCompletionReq) -> Self {
3074        ClientSampling {
3075            temperature: r.temperature,
3076            top_p: r.top_p,
3077            top_k: r.top_k,
3078            min_p: r.min_p,
3079            frequency_penalty: r.frequency_penalty,
3080            presence_penalty: r.presence_penalty,
3081            repetition_penalty: r.repetition_penalty,
3082            seed: r.seed,
3083        }
3084    }
3085}
3086
3087/// THE resolution law. Client value > vendor/operator default > API-standard default.
3088///
3089/// The one invariant that must never bend: an EXPLICIT `temperature: 0` produces true greedy,
3090/// because `Some(0.0)` short-circuits before any default is consulted. Greedy is a caller
3091/// decision and stays exactly reachable; it just stops being what an omitting client gets.
3092fn resolve_sampler_config(client: ClientSampling, defaults: &SamplingDefaults) -> SamplerConfig {
3093    sampler_config(
3094        client
3095            .temperature
3096            .or(defaults.temperature)
3097            .unwrap_or_else(default_temperature),
3098        client.top_k.or(defaults.top_k).unwrap_or(0),
3099        client.top_p.or(defaults.top_p).unwrap_or_else(one),
3100        client.min_p.or(defaults.min_p).unwrap_or(0.0),
3101        client
3102            .frequency_penalty
3103            .or(defaults.frequency_penalty)
3104            .unwrap_or(0.0),
3105        client
3106            .presence_penalty
3107            .or(defaults.presence_penalty)
3108            .unwrap_or(0.0),
3109        client
3110            .repetition_penalty
3111            .or(defaults.repetition_penalty)
3112            .unwrap_or_else(one),
3113        client.seed,
3114    )
3115}
3116
3117#[derive(Serialize)]
3118struct CompletionResp {
3119    model: String,
3120    text: String,
3121    tokens: Vec<u32>,
3122    /// Worker stop reason. `Deadline` (lane/deadline-partial-20260826) means the request's
3123    /// `timeout_ms` cut generation and the text above is what had been produced — the native
3124    /// twin of the OpenAI shapes' `finish_reason: "error"`.
3125    stop_reason: String,
3126    /// Present ONLY on a deadline-cut partial, carrying the same message/code/metadata the
3127    /// OpenAI shapes put in their `error` object. Absent on every normal completion, so the
3128    /// shape is unchanged for them. Without this the native surface learned nothing
3129    /// actionable from a cut — flagged by review.
3130    #[serde(default, skip_serializing_if = "Option::is_none")]
3131    error: Option<serde_json::Value>,
3132    n_tokens: usize,
3133    /// worker-truth prompt accounting (prompt caching): total prompt tokens, and how many
3134    /// were served from cache (continuation pool / spec resume / cross-request prefix cache).
3135    prompt_tokens: usize,
3136    cached_tokens: usize,
3137    elapsed_s: f64,
3138}
3139
3140/// OpenAI-schema usage object, shared by every response shape. `prompt_tokens_details.
3141/// cached_tokens` is the marketplace prompt-caching field (cache reads bill at a discount;
3142/// the value is worker-truth — tokens whose KV was resumed instead of computed).
3143/// `spec` (lane/accept-telemetry) is an ADDITIVE extension: this request's spec-decode
3144/// rounds/drafted/accepted + acceptance rate. Present only when the request actually ran
3145/// spec rounds — official SDKs ignore unknown usage fields (extra fields ok, existing
3146/// fields untouched), and spec-off responses are byte-identical to before.
3147fn usage_json(
3148    n_prompt: usize,
3149    n_tokens: usize,
3150    n_cached: usize,
3151    elapsed_s: f64,
3152    spec: Option<worker::SpecUsage>,
3153) -> serde_json::Value {
3154    let mut u = json!({
3155        "prompt_tokens": n_prompt,
3156        "completion_tokens": n_tokens,
3157        "total_tokens": n_prompt + n_tokens,
3158        "prompt_tokens_details": { "cached_tokens": n_cached },
3159        "elapsed_s": elapsed_s,
3160    });
3161    if let Some(sp) = spec {
3162        u["spec"] = json!({
3163            "rounds": sp.rounds,
3164            "drafted": sp.drafted,
3165            "accepted": sp.accepted,
3166            "acceptance_rate": if sp.drafted > 0 {
3167                sp.accepted as f64 / sp.drafted as f64 } else { 0.0 },
3168        });
3169    }
3170    u
3171}
3172
3173// ---- OpenAI response envelope (serve-compat lane, 2026-08-03; gap-scan F1) ----
3174//
3175// The official `openai` SDKs pydantic-validate every response: `ChatCompletion` /
3176// `ChatCompletionChunk` REQUIRE `id: str` and `created: int`, so a response without them
3177// is rejected client-side before the caller ever sees the content. Every OpenAI-shape
3178// completion and every stream chunk therefore carries `id` + `created` +
3179// `system_fingerprint`; the id doubles as the `x-request-id` response header (vLLM
3180// convention, serving_engine.py) for support/tracing. The memra-native response shape
3181// (non-chat, MEMRA_COMPAT unset) is untouched — validation harnesses depend on it.
3182
3183/// Backend-config fingerprint: `memra-<crate version>-<content id>`, baked by `build.rs`
3184/// from the crate version plus a digest of the workspace's compiled inputs. Together with
3185/// `seed`, responses are checkable for determinism across deploys — the OpenAI
3186/// `system_fingerprint` contract.
3187///
3188/// It is derived from file CONTENT, not from git history, and that is the whole point:
3189///
3190/// - **It cannot degrade to a label.** The old form was `concat!("memra-", <git sha>)`, and
3191///   a git failure inside darklanes' release container silently baked the literal
3192///   `unknown`. Prod served `system_fingerprint: memra-unknown` to every request for a
3193///   deploy generation, which also meant darklanes' `tools/check-claim-builds.mjs --live`
3194///   had nothing to verify published performance pins against. See `build.rs` for the
3195///   receipt chain.
3196/// - **It survives a history rewrite.** Rewriting commits changes every SHA while the bytes
3197///   of the tree stay put, so a fingerprint quoted in a published claim, a research
3198///   receipt, or a customer's own response keeps naming the same build afterwards.
3199///
3200/// Deliberately NOT in the value: a build timestamp. Two builds of the same source must
3201/// produce the same fingerprint, because `check-claim-builds` compares it for EQUALITY
3202/// against a published pin and a per-rebuild value would churn every pin. Build time is an
3203/// artifact-registry fact (the filename and the file's mtime), not an identity.
3204pub const SYSTEM_FINGERPRINT: &str = concat!(
3205    "memra-",
3206    env!("CARGO_PKG_VERSION"),
3207    "-",
3208    env!("MEMRA_BUILD_ID")
3209);
3210
3211/// How `SYSTEM_FINGERPRINT`'s id was derived: `source-tree` (real) or `degraded`.
3212pub const BUILD_ID_SRC: &str = env!("MEMRA_BUILD_ID_SRC");
3213
3214/// Why the id is degraded. Empty when it is not.
3215pub const BUILD_ID_NOTE: &str = env!("MEMRA_BUILD_ID_NOTE");
3216
3217/// The build's git sha when the build could read a repo, else `unknown`. An EXTRA
3218/// provenance field: convenient, never the identity. A shipped binary outlives the commit it
3219/// was cut from, and after an authorized history rewrite the sha names nothing at all.
3220pub const BUILD_GIT_SHA: &str = env!("MEMRA_BUILD_SHA");
3221
3222/// One line of build provenance, printed at boot by EVERY binary that links this server
3223/// (the stock bin and darklanes' deployment bin both enter through `serve_with`).
3224pub fn build_identity_line() -> String {
3225    format!("[server] build: {SYSTEM_FINGERPRINT} (id: {BUILD_ID_SRC}, git: {BUILD_GIT_SHA})")
3226}
3227
3228/// 128 random-ish hex bits: two RandomState-seeded hashes over a process counter + time.
3229/// Uniqueness class (request ids), not crypto.
3230fn gen_hex128() -> String {
3231    use std::hash::{BuildHasher, Hasher};
3232    static SEQ: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
3233    let n = SEQ.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
3234    let t = std::time::SystemTime::now()
3235        .duration_since(std::time::UNIX_EPOCH)
3236        .map(|d| d.as_nanos() as u64)
3237        .unwrap_or(0);
3238    let mut h1 = std::collections::hash_map::RandomState::new().build_hasher();
3239    h1.write_u64(n);
3240    h1.write_u64(t);
3241    let mut h2 = std::collections::hash_map::RandomState::new().build_hasher();
3242    h2.write_u64(t.rotate_left(17));
3243    h2.write_u64(n);
3244    format!("{:016x}{:016x}", h1.finish(), h2.finish())
3245}
3246
3247/// One request's envelope identity: the completion `id` (`chatcmpl-…` chat, `cmpl-…`
3248/// text) + `created` unix seconds, shared by the response and every chunk of its stream.
3249#[derive(Clone)]
3250struct Envelope {
3251    id: String,
3252    created: u64,
3253}
3254
3255impl Envelope {
3256    fn new(chat: bool) -> Self {
3257        Envelope {
3258            id: format!(
3259                "{}-{}",
3260                if chat { "chatcmpl" } else { "cmpl" },
3261                gen_hex128()
3262            ),
3263            created: std::time::SystemTime::now()
3264                .duration_since(std::time::UNIX_EPOCH)
3265                .map(|d| d.as_secs())
3266                .unwrap_or(0),
3267        }
3268    }
3269
3270    /// The ledger identity of ONE admitted capture inside a multi-item capture request
3271    /// (`/v1/embeddings` with N inputs, `/v1/rerank` with N documents): `<parent id>.<index>`.
3272    ///
3273    /// Every capture runs the full admission sequence and opens its own receipt, so it is
3274    /// a separately priced request to the budget ledger. The ledger keys debits by request
3275    /// id as a REPLAY GUARD: a second debit under an already-debited id is swallowed when
3276    /// the amount matches and refused (`conflicting budget debits`) when it does not. N
3277    /// captures sharing the parent id therefore billed as one capture when their costs
3278    /// rounded equal and failed the whole request with HTTP 500 when they did not
3279    /// (darklanes research/fleet-consolidation-tx-20260902/INCIDENT-rerank-ledger-conflict.md,
3280    /// 2026-09-02: rerank documents of 80 and 81 prompt tokens at $0.05/1M -> debits 4 and 5).
3281    /// A distinct child id per capture makes each capture debit exactly once. The HTTP
3282    /// response and `x-request-id` keep the parent id; children nest under it by prefix
3283    /// (`starts_with("<parent>.")`, never the bare parent: hex ids carry no `.`, so the dotted
3284    /// prefix cannot alias another parent or another child) for reconciliation and log
3285    /// attribution.
3286    fn capture_child(&self, index: usize) -> Self {
3287        Envelope {
3288            id: format!("{}.{index}", self.id),
3289            created: self.created,
3290        }
3291    }
3292
3293    /// Stamp the envelope fields onto one completion/chunk payload.
3294    fn stamp(&self, mut v: serde_json::Value) -> serde_json::Value {
3295        v["id"] = json!(self.id);
3296        v["created"] = json!(self.created);
3297        v["system_fingerprint"] = json!(SYSTEM_FINGERPRINT);
3298        v
3299    }
3300}
3301
3302/// Attach the request id as the `x-request-id` response header.
3303fn with_request_id(id: &str, mut resp: Response) -> Response {
3304    if let Ok(v) = axum::http::HeaderValue::from_str(id) {
3305        resp.headers_mut()
3306            .insert(axum::http::HeaderName::from_static("x-request-id"), v);
3307    }
3308    resp
3309}
3310
3311/// OpenAI-compat mapping (2026-07-05, serve-parity arc): the pi daily client speaks
3312/// `openai-completions` — POST /v1/completions with the OpenAI body, expecting
3313/// `{choices:[{text, finish_reason, index}], usage:{...}}` and, when streaming, OpenAI SSE
3314/// chunks (`data: {choices:[{text}]}` ... `data: [DONE]`). pi renders the chat template
3315/// CLIENT-side (thinkingFormat qwen-chat-template), so raw-prompt completions is the whole
3316/// contract. MEMRA_COMPAT=openai (default when MEMRA_API_KEY is set — the pi setup) switches the
3317/// response shape; the native memra shape stays default otherwise (validation harnesses use it).
3318fn openai_compat() -> bool {
3319    static C: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
3320    *C.get_or_init(|| match std::env::var("MEMRA_COMPAT").as_deref() {
3321        Ok("openai") => true,
3322        Ok(_) => false,
3323        Err(_) => std::env::var("MEMRA_API_KEY").is_ok(),
3324    })
3325}
3326
3327/// PC-ISO (lane/pc-iso, 2026-08-02): extract the raw cache namespace for request builders —
3328/// the vLLM `cache_salt` design (research/cache-tools-20260802/REPORT.md §4): the explicit
3329/// `cache_salt` body field (OpenAI-compatible extension), else "" — the default
3330/// single-tenant namespace, byte-identical to pre-PC-ISO behavior. The HTTP handlers validate
3331/// this value with `validate_cache_namespace` before any Request reaches the worker. When a
3332/// keyring is configured (MEMRA_API_KEYS) the handlers wrap it in the tenant scope —
3333/// `tenant_namespace` -> `t:<tenant>\x1f<salt>` (lane/api-keys) — so per-key identity
3334/// DOES fold in now; without a keyring the validated raw form passes through unchanged.
3335/// Cross-request KV reuse (prefix cache, continuation pool, spec pool)
3336/// only ever matches entries with an IDENTICAL namespace, so the `cached_tokens` hit oracle
3337/// can only reveal the caller's own namespace's history (CacheProbe/PROMPTPEEK mitigation).
3338fn cache_namespace(cache_salt: &Option<String>) -> String {
3339    cache_salt.clone().unwrap_or_default()
3340}
3341
3342const CACHE_SALT_MAX_BYTES: usize = 64;
3343
3344fn validate_cache_namespace(
3345    cache_salt: &Option<String>,
3346    keyring_configured: bool,
3347) -> Result<String, &'static str> {
3348    let raw = cache_namespace(cache_salt);
3349    if raw.len() > CACHE_SALT_MAX_BYTES {
3350        return Err("cache_salt must be at most 64 bytes");
3351    }
3352    if !keyring_configured && raw.starts_with("t:") {
3353        return Err("cache_salt must not use the reserved t: prefix without a keyring");
3354    }
3355    if !raw
3356        .bytes()
3357        .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_' | b'.' | b'+' | b'/' | b'='))
3358    {
3359        return Err("cache_salt contains unsupported characters");
3360    }
3361    Ok(raw)
3362}
3363
3364/// SESSION AFFINITY explicit tier (lane/session-affinity, 2026-08-05): the caller's own name
3365/// for this conversation, if it supplies one. A named conversation resumes its parked session
3366/// directly — no fingerprint guess needed. Accepted conventions, in priority order:
3367///   1. `session_id` body field — the explicit spelling.
3368///   2. `user` body field — OpenAI's own field; real clients already send a stable per-user
3369///      (often per-conversation) value here, so honoring it costs the caller nothing.
3370///   3. `x-session-id` request header — the convention proxies in front of vLLM/TGI use.
3371///      Body beats header: the body is the caller's own statement of identity, while a header can
3372///      be rewritten by an intermediary. Blank/whitespace values are treated as absent (a client
3373///      sending `"user": ""` must not collapse every conversation onto one session).
3374///
3375/// The key is NOT authoritative over tokens. It only NOMINATES a parked session for the exact
3376/// token-diff test in the worker (`affinity_match`), and only within the request's own
3377/// (model, cache_ns) pool — so a reused or guessed id can cost a wasted probe, never a wrong
3378/// resume and never cross-tenant reach.
3379fn affinity_key(
3380    session_id: &Option<String>,
3381    user: &Option<String>,
3382    headers: &axum::http::HeaderMap,
3383) -> Result<Option<String>, String> {
3384    let clean = |s: &str| -> Result<Option<String>, String> {
3385        let t = s.trim();
3386        if t.is_empty() {
3387            Ok(None)
3388        } else if t.len() > MAX_CLIENT_IDENTIFIER_BYTES {
3389            Err(format!(
3390                "session identity must be at most {MAX_CLIENT_IDENTIFIER_BYTES} UTF-8 bytes"
3391            ))
3392        } else if t.chars().any(char::is_control) {
3393            Err("session identity must not contain control characters".into())
3394        } else {
3395            Ok(Some(t.to_string()))
3396        }
3397    };
3398    if let Some(value) = session_id.as_deref()
3399        && let Some(value) = clean(value)?
3400    {
3401        return Ok(Some(value));
3402    }
3403    if let Some(value) = user.as_deref()
3404        && let Some(value) = clean(value)?
3405    {
3406        return Ok(Some(value));
3407    }
3408    match headers.get("x-session-id") {
3409        Some(value) => clean(
3410            value
3411                .to_str()
3412                .map_err(|_| "x-session-id must contain visible ASCII or UTF-8 text")?,
3413        ),
3414        None => Ok(None),
3415    }
3416}
3417
3418fn validate_client_identifier(value: Option<&str>, name: &str) -> Result<(), String> {
3419    let Some(value) = value else {
3420        return Ok(());
3421    };
3422    if value.len() > MAX_CLIENT_IDENTIFIER_BYTES {
3423        return Err(format!(
3424            "{name} must be at most {MAX_CLIENT_IDENTIFIER_BYTES} UTF-8 bytes"
3425        ));
3426    }
3427    if value.chars().any(char::is_control) {
3428        return Err(format!("{name} must not contain control characters"));
3429    }
3430    Ok(())
3431}
3432
3433/// OpenAI error body: `{"error": {"message", "type", "param", "code"}}` — the object
3434/// shape every OpenAI SDK parses (gap-scan F1; the old `{"error": "<string>"}` made
3435/// clients show a blank error). `type` follows the OpenAI vocabulary:
3436/// invalid_request_error / authentication_error / not_found_error / server_error.
3437fn error_body(
3438    message: &str,
3439    etype: &str,
3440    param: Option<&str>,
3441    code: Option<&str>,
3442) -> serde_json::Value {
3443    json!({ "error": {
3444        "message": message,
3445        "type": etype,
3446        "param": param,
3447        "code": code,
3448    } })
3449}
3450
3451fn error_response(status: StatusCode, message: &str, etype: &str, param: Option<&str>) -> Response {
3452    error_response_coded(status, message, etype, param, None)
3453}
3454
3455/// Same, with an explicit OpenAI `code`. Handler-layer refusals (auth, lane, request parsing)
3456/// land here; engine-produced faults land in `engine_error_response`. Both attach
3457/// `x-should-retry: false` on a 4xx that retrying the identical bytes cannot fix, so the two
3458/// halves of the surface behave identically to a client that retries by status alone.
3459fn error_response_coded(
3460    status: StatusCode,
3461    message: &str,
3462    etype: &str,
3463    param: Option<&str>,
3464    code: Option<&str>,
3465) -> Response {
3466    let mut resp = (status, Json(error_body(message, etype, param, code))).into_response();
3467    if status.is_client_error()
3468        && status != StatusCode::TOO_MANY_REQUESTS
3469        && status != StatusCode::REQUEST_TIMEOUT
3470        && status != StatusCode::CONFLICT
3471    {
3472        resp.headers_mut().insert(
3473            "x-should-retry",
3474            axum::http::HeaderValue::from_static("false"),
3475        );
3476    }
3477    resp
3478}
3479
3480fn bad_request(message: &str, param: Option<&str>) -> Response {
3481    error_response(
3482        StatusCode::BAD_REQUEST,
3483        message,
3484        "invalid_request_error",
3485        param,
3486    )
3487}
3488
3489// ---- engine-fault taxonomy -> HTTP (lane/serve-hardening, G6) --------------------------
3490//
3491// WHAT THIS REPLACES. Every worker failure — CUDA errors, VRAM exhaustion, admission sheds,
3492// tokenizer failures, graph faults — used to funnel into ONE line: `bad_request(&msg, None)`,
3493// i.e. HTTP 400 invalid_request_error. That is wrong in both directions and both directions
3494// cost money:
3495//   * a client SDK never retries a 400 (openai-python retries 408/409/429/>=500 only), so a
3496//     transient capacity blip became a hard user-visible failure with no retry;
3497//   * a router cannot tell "your request was malformed" from "my GPU fell over", so it keeps
3498//     sending traffic to a broken box instead of failing over.
3499// The class now comes from the PRODUCER (worker.rs::EngineError), not from re-guessing at the
3500// HTTP layer, with exactly one deliberate text rule (`is_cuda_oom` -> Overloaded).
3501//
3502// THE RETRY CONTRACT, verified against the client code rather than the docs:
3503//   * `Retry-After` is INTEGER seconds (RFC 9110 §10.2.3 delay-seconds — a float here is
3504//     simply unparseable), and openai-python ABANDONS the retry entirely if the value exceeds
3505//     its MAX_RETRY_AFTER_DELAY of 120 s. litellm honors the header only for 0 < v <= 60.
3506//     So every value memra emits is an integer and <= 60.
3507//   * `retry-after-ms` is read FIRST by openai-python, which lets us express sub-second
3508//     backoff to SDKs that support it while the integer header stays correct for everyone
3509//     else. Both are sent; they agree.
3510//   * `x-should-retry: false` is openai-python's explicit override, used where retrying is
3511//     provably pointless (a 400-class fault), so a client that retries by status alone does
3512//     not hammer a request that can never succeed.
3513const RETRY_AFTER_S_RATE_LIMIT: u64 = 2; // QoS shed: the lane's own budget window
3514const RETRY_AFTER_S_OVERLOADED: u64 = 5; // VRAM/capacity: needs a session to finish first
3515
3516/// Status + OpenAI `type` + `code` for one engine error class.
3517fn class_http(class: worker::ErrClass) -> (StatusCode, &'static str, Option<&'static str>) {
3518    use worker::ErrClass as C;
3519    match class {
3520        C::InvalidRequest => (StatusCode::BAD_REQUEST, "invalid_request_error", None),
3521        C::ContextLength => (
3522            StatusCode::BAD_REQUEST,
3523            "invalid_request_error",
3524            Some("context_length_exceeded"),
3525        ),
3526        C::ModelNotFound => (
3527            StatusCode::BAD_REQUEST,
3528            "invalid_request_error",
3529            Some("model_not_found"),
3530        ),
3531        C::RateLimit => (
3532            StatusCode::TOO_MANY_REQUESTS,
3533            "rate_limit_error",
3534            Some("rate_limit_exceeded"),
3535        ),
3536        C::Overloaded => (
3537            StatusCode::SERVICE_UNAVAILABLE,
3538            "server_error",
3539            Some("overloaded"),
3540        ),
3541        C::Engine => (
3542            StatusCode::INTERNAL_SERVER_ERROR,
3543            "server_error",
3544            Some("engine_error"),
3545        ),
3546    }
3547}
3548
3549/// Retry-After seconds for a class, or None when retrying cannot help.
3550fn class_retry_after_s(class: worker::ErrClass) -> Option<u64> {
3551    use worker::ErrClass as C;
3552    match class {
3553        C::RateLimit => Some(RETRY_AFTER_S_RATE_LIMIT),
3554        C::Overloaded => Some(RETRY_AFTER_S_OVERLOADED),
3555        // An engine fault is not time-bounded: this process may need to be restarted. Say
3556        // nothing rather than promise a window we cannot honor — the SDK's own exponential
3557        // backoff (500s are retryable by default) is the honest behavior here.
3558        C::Engine | C::InvalidRequest | C::ContextLength | C::ModelNotFound => None,
3559    }
3560}
3561
3562/// The JSON body for an engine error, shared by the blocking and the streaming paths so a
3563/// client sees the SAME object either way.
3564fn engine_error_body(e: &worker::EngineError) -> serde_json::Value {
3565    let (_, etype, code) = class_http(e.class);
3566    error_body(&e.message, etype, e.param, code)
3567}
3568
3569/// Full HTTP response for an engine error: status, OpenAI body, and the retry headers.
3570/// A producer-computed `retry_after_s` (D2 gap G6: the predictive-admission reject's
3571/// earliest predicted in-flight completion) overrides the per-class default; both take
3572/// the SAME `retry_contract_response` path, so the header pair stays byte-compatible
3573/// with the shed contract regardless of who chose the value.
3574fn engine_error_response(e: &worker::EngineError) -> Response {
3575    engine_error_response_with_retry_after(
3576        e,
3577        e.retry_after_s.or_else(|| class_retry_after_s(e.class)),
3578    )
3579}
3580
3581fn engine_error_response_with_retry_after(
3582    e: &worker::EngineError,
3583    retry_after_s: Option<u64>,
3584) -> Response {
3585    let (status, _, _) = class_http(e.class);
3586    let resp = (status, Json(engine_error_body(e))).into_response();
3587    retry_contract_response(resp, retry_after_s)
3588}
3589
3590/// Apply memra's retry headers to any response body.
3591fn retry_contract_response(mut resp: Response, retry_after_s: Option<u64>) -> Response {
3592    let status = resp.status();
3593    let h = resp.headers_mut();
3594    match retry_after_s {
3595        Some(secs) => {
3596            // Integer seconds in the SDK-honored 1..=60 window (see the contract note above).
3597            let secs = secs.clamp(1, 60);
3598            if let Ok(v) = axum::http::HeaderValue::from_str(&secs.to_string()) {
3599                h.insert(axum::http::header::RETRY_AFTER, v);
3600            }
3601            if let Ok(v) = axum::http::HeaderValue::from_str(&(secs * 1000).to_string()) {
3602                h.insert("retry-after-ms", v);
3603            }
3604        }
3605        None if status.is_client_error() => {
3606            // A malformed request, an unknown model, an over-long prompt: retrying the
3607            // identical bytes cannot succeed. Say so explicitly.
3608            h.insert(
3609                "x-should-retry",
3610                axum::http::HeaderValue::from_static("false"),
3611            );
3612        }
3613        None => {}
3614    }
3615    resp
3616}
3617
3618fn worker_unavailable_response() -> Response {
3619    engine_error_response_with_retry_after(
3620        &worker::EngineError::overloaded("worker unavailable"),
3621        Some(worker::WORKER_RESPAWN_BACKOFF_BASE_S),
3622    )
3623}
3624
3625fn stop_reason_to_finish(r: &str) -> &'static str {
3626    match r {
3627        "Eos" | "Callback" => "stop",
3628        "MaxNew" | "ContextFull" => "length",
3629        _ => "stop",
3630    }
3631}
3632
3633// ---- tools surface helpers (serve-tools lane, 2026-08-02) ----
3634
3635/// Flatten an OpenAI `content` value to text: string, null (-> ""), or `{type:"text"}` parts.
3636fn content_to_text(v: &serde_json::Value) -> Result<String, String> {
3637    match v {
3638        serde_json::Value::Null => Ok(String::new()),
3639        serde_json::Value::String(s) => Ok(s.clone()),
3640        serde_json::Value::Array(parts) => {
3641            let mut out = String::new();
3642            for p in parts {
3643                match p.get("type").and_then(|t| t.as_str()) {
3644                    Some("text") | None => match p.get("text").and_then(|t| t.as_str()) {
3645                        Some(t) => out.push_str(t),
3646                        None => return Err("content part has no text field".into()),
3647                    },
3648                    Some(other) => {
3649                        return Err(format!(
3650                            "unsupported content part type {other:?} (text only)"
3651                        ));
3652                    }
3653                }
3654            }
3655            Ok(out)
3656        }
3657        _ => Err("content must be a string, null, or an array of text parts".into()),
3658    }
3659}
3660
3661/// Vision PLACEMENT admissibility, published by the worker at boot for EVERY vision family
3662/// (worker.rs `vision_placement_admissible`) and read at every MEDIA PART below
3663/// (`vision_placement_admits`), never by the family switches: those route the content
3664/// walkers, and step37's text-separator law lives only in its walker, so folding the
3665/// placement into a switch would move prompt bytes on text-only traffic (revuto, #46).
3666///
3667/// A loaded tower is not sufficient to serve images: the overlay's rows have to be resident
3668/// in the CUDA context of the engine that embeds (pp stage 0 under a per-stage-stream ppN
3669/// split), and `MEMRA_VISION_OVERLAY_PUBLISH=0` forbids putting them there. Deciding that
3670/// ONCE at boot and refusing at the waist is what lane/glm53-vision-ppn shipped for glm5 —
3671/// but the door it reads is the first line of `EmbedOverlay::new_published` for all four
3672/// families, so a gemma4 / qwen-VL / step37 deployment with the same pin (or a mistyped door
3673/// value) booted clean and 500'd MID-PREFILL on a live request, the exact failure removed for
3674/// glm5. step37 serves vision in production, which made that a live exposure (memra #25).
3675///
3676/// `true` until the worker publishes: readiness gates customer traffic behind the worker's
3677/// spawn, and a unit test that never spawns a worker must see the pre-lane program.
3678pub(crate) static VISION_PLACEMENT_SERVING: std::sync::atomic::AtomicBool =
3679    std::sync::atomic::AtomicBool::new(true);
3680
3681fn vision_placement_serving() -> bool {
3682    VISION_PLACEMENT_SERVING.load(std::sync::atomic::Ordering::Acquire)
3683}
3684
3685/// The one placement gate every media-accepting arm passes BEFORE it plans anything: an
3686/// `image_url`/`video_url` part on a placement that cannot deliver an overlay to embedding
3687/// intake refuses with a named 400 here, at the waist, instead of 500ing mid-prefill. Pure so
3688/// its contract is unit-tested without touching process state; `vision_placement_admits` is
3689/// the live wrapper that feeds the worker's decision in. `kind` is `"image"` or `"video"`.
3690fn vision_media_admissible(placement: bool, kind: &str) -> Result<(), String> {
3691    if placement {
3692        Ok(())
3693    } else {
3694        Err(format!(
3695            "{kind} input is not enabled on this deployment (vision overlay placement \
3696             inadmissible at boot: see the worker's IMAGE INPUT DISABLED line)"
3697        ))
3698    }
3699}
3700
3701fn vision_placement_admits(kind: &str) -> Result<(), String> {
3702    vision_media_admissible(vision_placement_serving(), kind)
3703}
3704
3705/// Vision enablement (lane/vision): the worker loads the tower iff MEMRA_VISION_DIR is
3706/// set, so the HTTP layer accepts image parts under exactly the same condition. Armed-only
3707/// by design: the placement half is applied per media part (`vision_placement_admits`).
3708fn vision_enabled() -> bool {
3709    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
3710    *ON.get_or_init(|| {
3711        std::env::var("MEMRA_VISION_DIR").is_ok()
3712            && std::env::var("MEMRA_VISION").as_deref() != Ok("0")
3713    })
3714}
3715
3716/// Gemma-4 vision seam (lane/gemma-vision): a deployment serves ONE vision family
3717/// (one model per GPU), so this process-wide switch decides which placeholder + prep
3718/// the image parts take. Default OFF — gemma image input refuses until an operator
3719/// sets MEMRA_GEMMA_VISION=1 with a gemma4v mmproj at MEMRA_GEMMA_MMPROJ.
3720fn gemma_vision_enabled() -> bool {
3721    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
3722    *ON.get_or_init(|| {
3723        std::env::var("MEMRA_GEMMA_VISION").as_deref() == Ok("1")
3724            && std::env::var("MEMRA_GEMMA_MMPROJ").is_ok()
3725    })
3726}
3727
3728/// glm5_next vision serving decision, published by the worker at spawn (worker.rs tower
3729/// load) and read by the HTTP intake. DEFAULT ON (owner order 2026-08-30,
3730/// lane/glm5-vision-default-on): true iff a glm5 tower actually loaded — from the served
3731/// glm5_next artifact's own `model.visual.*` tensors by default, from
3732/// MEMRA_GLM5_VISION_DIR when set; false when the artifact carries no tower or
3733/// MEMRA_GLM5_VISION=0 (the rollback seam). Not an env read: the intake must route image
3734/// parts to the glm5 planner exactly when the worker can prime them. Already folds in the
3735/// placement decision (`VISION_PLACEMENT_SERVING`): the worker stores
3736/// `tower loaded && placement admissible`.
3737pub(crate) static GLM5_VISION_SERVING: std::sync::atomic::AtomicBool =
3738    std::sync::atomic::AtomicBool::new(false);
3739
3740/// glm5_next vision seam (lane/glm5-vision): same one-family-per-deployment law as the
3741/// gemma seam. See `GLM5_VISION_SERVING` for the decision's source of truth.
3742fn glm5_vision_enabled() -> bool {
3743    GLM5_VISION_SERVING.load(std::sync::atomic::Ordering::Acquire)
3744}
3745
3746/// step37 vision seam (lane/step37-vision): same one-vision-family-per-process law as
3747/// the two above. The worker loads the perception_encoder tower from the serving
3748/// artifact's own directory iff MEMRA_STEP_VISION_DIR is set (the vision tensors live
3749/// unquantized inside the checkpoint), so the HTTP layer accepts image parts under
3750/// exactly the same condition; MEMRA_STEP_VISION=0 is the kill switch (both sides).
3751/// Armed-only by design: this switch selects the step content walker, whose TEXT separator
3752/// law must not move with the placement; image parts pass `vision_placement_admits` inside.
3753fn step_vision_enabled() -> bool {
3754    static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
3755    *ON.get_or_init(|| {
3756        std::env::var("MEMRA_STEP_VISION_DIR").is_ok()
3757            && std::env::var("MEMRA_STEP_VISION").as_deref() != Ok("0")
3758    })
3759}
3760
3761/// Per-request image cap (v1 envelope; the context cap bounds total vision tokens).
3762const VISION_MAX_IMAGES: usize = 8;
3763
3764/// Bound the host memory retained by decoded vision patches. The previous per-image pixel cap
3765/// allowed eight Qwen images to materialize roughly 3 GiB of f32 patch rows before the HTTP
3766/// concurrency gate ran. A process-wide reservation keeps both one request and concurrent
3767/// requests within a finite budget; the request slot remains a separate serving/QoS control.
3768pub(crate) const MAX_VISION_PATCH_BYTES: usize = 1 << 30; // 1 GiB
3769static VISION_PATCH_BYTES_IN_USE: std::sync::atomic::AtomicUsize =
3770    std::sync::atomic::AtomicUsize::new(0);
3771/// GIF/video preprocessing is bounded separately from request admission because its decoder must
3772/// discover sampled frames and timestamps while constructing the prompt plan. Serializing this
3773/// phase prevents multiple requests from simultaneously holding their transient RGB canvases.
3774pub(crate) static VISION_PREPROCESS_SEMAPHORE: tokio::sync::Semaphore =
3775    tokio::sync::Semaphore::const_new(1);
3776
3777// Axum handlers use `Response` as their rejection type. Boxing this rare 429/503 response
3778// would add allocation and conversion at every `?` boundary for no reduction in retained state.
3779#[allow(clippy::result_large_err)]
3780pub(crate) fn try_vision_preprocess(
3781    required: bool,
3782) -> Result<Option<tokio::sync::SemaphorePermit<'static>>, Response> {
3783    try_vision_preprocess_with(required, &VISION_PREPROCESS_SEMAPHORE)
3784}
3785
3786#[allow(clippy::result_large_err)]
3787fn try_vision_preprocess_with(
3788    required: bool,
3789    semaphore: &'static tokio::sync::Semaphore,
3790) -> Result<Option<tokio::sync::SemaphorePermit<'static>>, Response> {
3791    if !required {
3792        return Ok(None);
3793    }
3794    match semaphore.try_acquire() {
3795        Ok(permit) => Ok(Some(permit)),
3796        Err(tokio::sync::TryAcquireError::NoPermits) => Err(retry_contract_response(
3797            error_response_coded(
3798                StatusCode::TOO_MANY_REQUESTS,
3799                "vision preprocessing is busy",
3800                "rate_limit_error",
3801                Some("messages"),
3802                Some("vision_preprocess_busy"),
3803            ),
3804            Some(BODY_ADMISSION_RETRY_AFTER_S),
3805        )),
3806        Err(tokio::sync::TryAcquireError::Closed) => Err(error_response_coded(
3807            StatusCode::SERVICE_UNAVAILABLE,
3808            "vision preprocessing is unavailable",
3809            "server_error",
3810            Some("messages"),
3811            Some("vision_preprocess_unavailable"),
3812        )),
3813    }
3814}
3815
3816pub(crate) struct VisionMemoryPermit {
3817    bytes: usize,
3818}
3819
3820#[derive(Debug)]
3821pub(crate) enum VisionMemoryError {
3822    Request(String),
3823    Capacity(String),
3824}
3825
3826impl Drop for VisionMemoryPermit {
3827    fn drop(&mut self) {
3828        if self.bytes != 0 {
3829            VISION_PATCH_BYTES_IN_USE.fetch_sub(self.bytes, std::sync::atomic::Ordering::AcqRel);
3830        }
3831    }
3832}
3833
3834fn try_reserve_vision_memory(
3835    bytes: usize,
3836) -> Result<Option<VisionMemoryPermit>, VisionMemoryError> {
3837    if bytes == 0 {
3838        return Ok(None);
3839    }
3840    if bytes > MAX_VISION_PATCH_BYTES {
3841        return Err(VisionMemoryError::Request(format!(
3842            "vision preprocessing requires {bytes} bytes of patch memory, exceeding the {} MiB request limit",
3843            MAX_VISION_PATCH_BYTES / (1024 * 1024)
3844        )));
3845    }
3846    let mut in_use = VISION_PATCH_BYTES_IN_USE.load(std::sync::atomic::Ordering::Acquire);
3847    loop {
3848        let Some(next) = in_use.checked_add(bytes) else {
3849            return Err(VisionMemoryError::Capacity(
3850                "vision patch memory reservation overflowed".into(),
3851            ));
3852        };
3853        if next > MAX_VISION_PATCH_BYTES {
3854            return Err(VisionMemoryError::Capacity(format!(
3855                "vision preprocessing is at capacity ({} MiB reserved; request needs {} MiB)",
3856                in_use / (1024 * 1024),
3857                bytes / (1024 * 1024)
3858            )));
3859        }
3860        match VISION_PATCH_BYTES_IN_USE.compare_exchange_weak(
3861            in_use,
3862            next,
3863            std::sync::atomic::Ordering::AcqRel,
3864            std::sync::atomic::Ordering::Acquire,
3865        ) {
3866            Ok(_) => return Ok(Some(VisionMemoryPermit { bytes })),
3867            Err(actual) => in_use = actual,
3868        }
3869    }
3870}
3871
3872pub(crate) fn vision_memory_error_response(
3873    error: VisionMemoryError,
3874    param: Option<&str>,
3875) -> Response {
3876    match error {
3877        VisionMemoryError::Request(message) => bad_request(&message, param),
3878        VisionMemoryError::Capacity(message) => retry_contract_response(
3879            error_response_coded(
3880                StatusCode::SERVICE_UNAVAILABLE,
3881                &message,
3882                "server_error",
3883                None,
3884                Some("vision_memory_busy"),
3885            ),
3886            Some(RETRY_AFTER_S_OVERLOADED),
3887        ),
3888    }
3889}
3890
3891/// One qwen vision unit as PLANNED at request build — pre-admission, header-only
3892/// (hermes decode-bomb finding, fixed 2026-08-23). `Still` carries the raw bytes plus
3893/// the grid its header plans to; the pixels decode in `decode_pending_vision`, AFTER
3894/// budget admission. `Video` carries a metadata-only GIF plan (sampled timestamps and grids);
3895/// frame pixels decode in `decode_pending_vision` after admission as well.
3896enum PendingVisionUnit {
3897    Still {
3898        bytes: Vec<u8>,
3899        gh: usize,
3900        gw: usize,
3901    },
3902    Video {
3903        bytes: Vec<u8>,
3904        groups: Vec<memra_engine::vision_pre::PlannedVideoGroup>,
3905        video: usize,
3906    },
3907}
3908
3909/// The gemma twin of `PendingVisionUnit::Still` (gemma has no video input).
3910struct PendingGemmaImage {
3911    bytes: Vec<u8>,
3912    gw: usize,
3913    gh: usize,
3914}
3915
3916/// The glm5_next twin (lane/glm5-vision). Video arms are censused but NOT served —
3917/// out of scope for the lane; `video_url` on a glm5 deployment refuses loudly.
3918struct PendingGlm5Image {
3919    bytes: Vec<u8>,
3920    gh: usize,
3921    gw: usize,
3922}
3923
3924/// The step37 twin: header-planned tiling (crop count + newline mask) awaiting its
3925/// post-admission pixel decode. step37 has no video input either.
3926struct PendingStepImage {
3927    bytes: Vec<u8>,
3928    plan: memra_engine::vision_step::StepImagePlan,
3929}
3930
3931/// step37 arm of `content_to_text_vision` (fires only when `step_vision_enabled()`).
3932/// Two vendor laws live here and nowhere else (chat_template.jinja at the pinned rev,
3933/// `render_message_content`): adjacent TEXT parts join with ONE space, and an image
3934/// part resets that separator (text directly after an image abuts it). Each image
3935/// renders as its exact expansion — the processor law, crops FIRST then the main view:
3936/// `<patch_start>` + 81 pads + `<patch_end>` (+ `<patch_newline>` per full tile row,
3937/// except a trailing one), then `<im_start>` + 169 pads + `<im_end>`. The worker
3938/// re-derives the runs from the TOKENIZED prompt and aligns them with `step_images`,
3939/// so user text faking pad tokens fails validation loudly. Data URIs only (SSRF off).
3940fn content_to_text_vision_step(
3941    v: &serde_json::Value,
3942    step_images: &mut Vec<PendingStepImage>,
3943) -> Result<String, String> {
3944    use memra_engine::vision_step::{SV_MAIN_ROWS, SV_TILE_ROWS};
3945    let parts = match v {
3946        serde_json::Value::Array(parts) => parts,
3947        _ => return content_to_text(v),
3948    };
3949    let mut out = String::new();
3950    let mut needs_sep = false;
3951    for p in parts {
3952        match p.get("type").and_then(|t| t.as_str()) {
3953            Some("text") | None => match p.get("text").and_then(|t| t.as_str()) {
3954                Some(t) => {
3955                    if needs_sep {
3956                        out.push(' ');
3957                    }
3958                    out.push_str(t);
3959                    needs_sep = true;
3960                }
3961                None => return Err("content part has no text field".into()),
3962            },
3963            Some("image_url") => {
3964                vision_placement_admits("image")?;
3965                let url = p
3966                    .get("image_url")
3967                    .and_then(|u| {
3968                        if u.is_string() {
3969                            u.as_str()
3970                        } else {
3971                            u.get("url").and_then(|x| x.as_str())
3972                        }
3973                    })
3974                    .ok_or("image_url part has no url")?;
3975                if !url.starts_with("data:") {
3976                    return Err(
3977                        "image_url must be a base64 data URI (http(s) fetch is disabled)".into(),
3978                    );
3979                }
3980                if step_images.len() >= VISION_MAX_IMAGES {
3981                    return Err(format!("too many images (max {VISION_MAX_IMAGES})"));
3982                }
3983                // PLAN, don't decode (hermes decode-bomb law): the expansion derives
3984                // from HEADER dims; the canvas expands only after budget admission
3985                // (decode_pending_vision).
3986                let bytes = memra_engine::vision_pre::decode_data_uri(url)
3987                    .map_err(|e| format!("image {}: {e}", step_images.len() + 1))?;
3988                let plan = memra_engine::vision_step::step_plan_image(&bytes)
3989                    .map_err(|e| format!("image {}: {e}", step_images.len() + 1))?;
3990                for i in 0..plan.n_tiles {
3991                    out.push_str("<patch_start>");
3992                    for _ in 0..SV_TILE_ROWS {
3993                        out.push_str("<im_patch>");
3994                    }
3995                    out.push_str("<patch_end>");
3996                    if plan.newline_mask[i] {
3997                        out.push_str("<patch_newline>");
3998                    }
3999                }
4000                out.push_str("<im_start>");
4001                for _ in 0..SV_MAIN_ROWS {
4002                    out.push_str("<im_patch>");
4003                }
4004                out.push_str("<im_end>");
4005                step_images.push(PendingStepImage { bytes, plan });
4006                needs_sep = false;
4007            }
4008            Some("video_url") => {
4009                return Err("step37 has no video input (image-only processor)".into());
4010            }
4011            Some(other) => {
4012                return Err(format!("unsupported content part type {other:?}"));
4013            }
4014        }
4015    }
4016    Ok(out)
4017}
4018
4019/// `content_to_text` twin that also accepts `image_url` parts: each image is PLANNED
4020/// here (header dims -> pre-decode pixel admission -> grid) and renders as its exact pad
4021/// run — `<|vision_start|>` + `<|image_pad|>` x n_tokens + `<|vision_end|>` — at its
4022/// position in the part order; the pixel decode itself runs after budget admission
4023/// (`decode_pending_vision`). The worker re-derives the runs from the TOKENIZED prompt
4024/// and aligns them 1:1 with `images`, so user text faking pad tokens fails validation
4025/// loudly. v1 posture: data URIs only — http(s) fetch stays off (SSRF), video parts
4026/// follow images.
4027fn content_to_text_vision(
4028    v: &serde_json::Value,
4029    images: &mut Vec<PendingVisionUnit>,
4030    gemma_images: &mut Vec<PendingGemmaImage>,
4031    glm5_images: &mut Vec<PendingGlm5Image>,
4032    step_images: &mut Vec<PendingStepImage>,
4033    next_video: &mut usize,
4034) -> Result<String, String> {
4035    // step37 deployments take their own walker: its placeholder expansion AND its
4036    // text-part separator law come from the step template, and both differ from the
4037    // qwen/gemma arms below. Fires only when the operator armed the step seam.
4038    if step_vision_enabled() {
4039        return content_to_text_vision_step(v, step_images);
4040    }
4041    let parts = match v {
4042        serde_json::Value::Array(parts) => parts,
4043        _ => return content_to_text(v),
4044    };
4045    let mut out = String::new();
4046    for p in parts {
4047        match p.get("type").and_then(|t| t.as_str()) {
4048            Some("text") | None => match p.get("text").and_then(|t| t.as_str()) {
4049                Some(t) => out.push_str(t),
4050                None => return Err("content part has no text field".into()),
4051            },
4052            Some("image_url") if glm5_vision_enabled() => {
4053                let url = p
4054                    .get("image_url")
4055                    .and_then(|u| {
4056                        if u.is_string() {
4057                            u.as_str()
4058                        } else {
4059                            u.get("url").and_then(|x| x.as_str())
4060                        }
4061                    })
4062                    .ok_or("image_url part has no url")?;
4063                if !url.starts_with("data:") {
4064                    return Err(
4065                        "image_url must be a base64 data URI (http(s) fetch is disabled)".into(),
4066                    );
4067                }
4068                if glm5_images.len() >= VISION_MAX_IMAGES {
4069                    return Err(format!("too many images (max {VISION_MAX_IMAGES})"));
4070                }
4071                // PLAN, don't decode (hermes decode-bomb law): header dims -> pre-decode
4072                // pixel admission -> grid; the placeholder run derives from the grid and
4073                // the canvas expands only after budget admission (decode_pending_vision).
4074                let bytes = memra_engine::vision_pre::decode_data_uri(url)
4075                    .map_err(|e| format!("image {}: {e}", glm5_images.len() + 1))?;
4076                let (gh, gw) = memra_engine::vision_glm5::glm5_plan_image(&bytes)
4077                    .map_err(|e| format!("image {}: {e}", glm5_images.len() + 1))?;
4078                // glm5_next placeholder run: <|begin_of_image|> + n x <|image|> +
4079                // <|end_of_image|> — the upstream Glm5NextProcessor.replace_image_token
4080                // expansion, rendered here so the tokenized prompt matches upstream.
4081                out.push_str("<|begin_of_image|>");
4082                for _ in 0..memra_engine::vision_glm5::n_merged_for_grid(gh, gw) {
4083                    out.push_str("<|image|>");
4084                }
4085                out.push_str("<|end_of_image|>");
4086                glm5_images.push(PendingGlm5Image { bytes, gh, gw });
4087            }
4088            Some("video_url") if glm5_vision_enabled() => {
4089                return Err(
4090                    "glm5 video input is not served (tensor census only; image input is the \
4091                     supported surface)"
4092                        .into(),
4093                );
4094            }
4095            Some("image_url") if gemma_vision_enabled() => {
4096                vision_placement_admits("image")?;
4097                let url = p
4098                    .get("image_url")
4099                    .and_then(|u| {
4100                        if u.is_string() {
4101                            u.as_str()
4102                        } else {
4103                            u.get("url").and_then(|x| x.as_str())
4104                        }
4105                    })
4106                    .ok_or("image_url part has no url")?;
4107                if !url.starts_with("data:") {
4108                    return Err(
4109                        "image_url must be a base64 data URI (http(s) fetch is disabled)".into(),
4110                    );
4111                }
4112                if gemma_images.len() >= VISION_MAX_IMAGES {
4113                    return Err(format!("too many images (max {VISION_MAX_IMAGES})"));
4114                }
4115                // PLAN, don't decode (hermes decode-bomb finding, fixed 2026-08-23): the
4116                // pad run derives from HEADER dims + the pre-decode pixel admission; the
4117                // canvas expands only after budget admission (decode_pending_vision).
4118                let bytes = memra_engine::vision_gemma::gemma_decode_data_uri(url)
4119                    .map_err(|e| format!("image {}: {e}", gemma_images.len() + 1))?;
4120                let (gw, gh) = memra_engine::vision_gemma::gemma_plan_image(&bytes)
4121                    .map_err(|e| format!("image {}: {e}", gemma_images.len() + 1))?;
4122                // gemma-4 placeholder: <|image> + n_soft * <|image|> + <image|>
4123                out.push_str("<|image>");
4124                for _ in 0..memra_engine::vision_gemma::n_soft_for_grid(gw, gh) {
4125                    out.push_str("<|image|>");
4126                }
4127                out.push_str("<image|>");
4128                gemma_images.push(PendingGemmaImage { bytes, gw, gh });
4129            }
4130            Some("image_url") => {
4131                if !vision_enabled() {
4132                    return Err("image input is not enabled on this deployment".into());
4133                }
4134                vision_placement_admits("image")?;
4135                let url = p
4136                    .get("image_url")
4137                    .and_then(|u| {
4138                        if u.is_string() {
4139                            u.as_str()
4140                        } else {
4141                            u.get("url").and_then(|x| x.as_str())
4142                        }
4143                    })
4144                    .ok_or("image_url part has no url")?;
4145                if !url.starts_with("data:") {
4146                    return Err(
4147                        "image_url must be a base64 data URI (http(s) fetch is disabled)".into(),
4148                    );
4149                }
4150                if images
4151                    .iter()
4152                    .filter(|u| matches!(u, PendingVisionUnit::Still { .. }))
4153                    .count()
4154                    >= VISION_MAX_IMAGES
4155                {
4156                    return Err(format!("too many images (max {VISION_MAX_IMAGES})"));
4157                }
4158                // PLAN, don't decode (hermes decode-bomb finding, fixed 2026-08-23):
4159                // header dims -> pre-decode pixel admission -> grid; the pad run derives
4160                // from the grid, and the canvas expands only after budget admission
4161                // (decode_pending_vision).
4162                let bytes = memra_engine::vision_pre::decode_data_uri(url)
4163                    .map_err(|e| format!("image {}: {e}", images.len() + 1))?;
4164                let (gh, gw) = memra_engine::vision_pre::plan_image_bytes(&bytes)
4165                    .map_err(|e| format!("image {}: {e}", images.len() + 1))?;
4166                out.push_str("<|vision_start|>");
4167                for _ in 0..memra_engine::vision_pre::n_tokens_for_grid(gh, gw) {
4168                    out.push_str("<|image_pad|>");
4169                }
4170                out.push_str("<|vision_end|>");
4171                images.push(PendingVisionUnit::Still { bytes, gh, gw });
4172            }
4173            Some("video_url") if gemma_vision_enabled() => {
4174                return Err("gemma-4 has no video input (image-only projector)".into());
4175            }
4176            Some("video_url") => {
4177                if !vision_enabled() {
4178                    return Err("video input is not enabled on this deployment".into());
4179                }
4180                vision_placement_admits("video")?;
4181                let url = p
4182                    .get("video_url")
4183                    .and_then(|u| {
4184                        if u.is_string() {
4185                            u.as_str()
4186                        } else {
4187                            u.get("url").and_then(|x| x.as_str())
4188                        }
4189                    })
4190                    .ok_or("video_url part has no url")?;
4191                if !url.starts_with("data:") {
4192                    return Err(
4193                        "video_url must be a base64 data URI (http(s) fetch is disabled)".into(),
4194                    );
4195                }
4196                if *next_video >= 2 {
4197                    return Err("too many videos (max 2)".into());
4198                }
4199                // v1 container: animated GIF (metadata planned here; frames decoded after
4200                // admission, in-process, with no ffmpeg dependency).
4201                let bytes = memra_engine::vision_pre::decode_data_uri(url)?;
4202                let vid = memra_engine::vision_pre::plan_video_gif(&bytes)
4203                    .map_err(|e| format!("video: {e}"))?;
4204                let vidx = *next_video;
4205                *next_video += 1;
4206                // HF Qwen3VL placeholder: `<t.t seconds>` + one pad run PER temporal group
4207                for group in &vid.groups {
4208                    out.push_str(&format!("<{:.1} seconds>", group.timestamp));
4209                    out.push_str("<|vision_start|>");
4210                    for _ in 0..memra_engine::vision_pre::n_tokens_for_grid(group.gh, group.gw) {
4211                        out.push_str("<|video_pad|>");
4212                    }
4213                    out.push_str("<|vision_end|>");
4214                }
4215                // Only metadata is retained in the plan; frame pixels are decoded after budget,
4216                // memory, and request-slot admission in `decode_pending_vision`.
4217                images.push(PendingVisionUnit::Video {
4218                    bytes,
4219                    groups: vid.groups,
4220                    video: vidx,
4221                });
4222            }
4223            Some(other) => {
4224                return Err(format!("unsupported content part type {other:?}"));
4225            }
4226        }
4227    }
4228    Ok(out)
4229}
4230
4231/// Render a JSON value the way the reference template's `tojson` does (python json.dumps:
4232/// `", "` / `": "` separators, insertion-order keys — serde_json preserve_order — non-ASCII
4233/// left raw). The tools block is prompt bytes, so the training-time convention is the law.
4234fn pyjson(v: &serde_json::Value, out: &mut String) {
4235    match v {
4236        serde_json::Value::Object(m) => {
4237            out.push('{');
4238            for (i, (k, val)) in m.iter().enumerate() {
4239                if i > 0 {
4240                    out.push_str(", ");
4241                }
4242                out.push_str(&serde_json::Value::String(k.clone()).to_string());
4243                out.push_str(": ");
4244                pyjson(val, out);
4245            }
4246            out.push('}');
4247        }
4248        serde_json::Value::Array(a) => {
4249            out.push('[');
4250            for (i, val) in a.iter().enumerate() {
4251                if i > 0 {
4252                    out.push_str(", ");
4253                }
4254                pyjson(val, out);
4255            }
4256            out.push(']');
4257        }
4258        scalar => out.push_str(&scalar.to_string()),
4259    }
4260}
4261
4262fn pyjson_str(v: &serde_json::Value) -> String {
4263    let mut s = String::new();
4264    pyjson(v, &mut s);
4265    s
4266}
4267
4268/// Sampler wiring shared by both bodies (gap-scan F3): the penalties existed in
4269/// SamplerConfig end-to-end (host sampler + spec rejection-sampling verify) — this is
4270/// pure request-struct plumbing. Every serving path uses the same bounded history window:
4271/// speculative sampling already caps its O(n²) history form at `PEN_WINDOW_MAX`, so the host
4272/// and sparse-device paths must use that exact bound too. Otherwise a spec-to-plain demotion
4273/// changes penalty logits mid-request (Hermes `da99e50ec4750599`).
4274#[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
4275fn sampler_config(
4276    temperature: f32,
4277    top_k: usize,
4278    top_p: f32,
4279    min_p: f32,
4280    frequency_penalty: f32,
4281    presence_penalty: f32,
4282    repetition_penalty: f32,
4283    seed: Option<u64>,
4284) -> SamplerConfig {
4285    let penalties_on =
4286        frequency_penalty != 0.0 || presence_penalty != 0.0 || repetition_penalty != 1.0;
4287    SamplerConfig {
4288        temperature,
4289        top_k,
4290        top_p,
4291        min_p,
4292        penalty_last_n: if penalties_on {
4293            memra_engine::spec::PEN_WINDOW_MAX
4294        } else {
4295            0
4296        },
4297        penalty_repeat: repetition_penalty,
4298        penalty_freq: frequency_penalty,
4299        penalty_present: presence_penalty,
4300        // Omitted seed => fresh entropy per request (dogfood F4). An explicit seed — including
4301        // an explicit 0 — is honored exactly, so every determinism gate keeps its behavior.
4302        seed: seed.unwrap_or_else(fresh_seed),
4303    }
4304}
4305
4306/// Non-zero per-request entropy for seed-omitting clients. Nanosecond clock mixed with a
4307/// process-lifetime counter through SplitMix64's finalizer: two requests in the same
4308/// nanosecond tick (batched arrivals) still get distinct streams, which a bare clock read
4309/// would not guarantee. Not crypto — this only has to avoid replaying one stream forever.
4310fn fresh_seed() -> u64 {
4311    use std::sync::atomic::{AtomicU64, Ordering};
4312    static COUNTER: AtomicU64 = AtomicU64::new(0);
4313    let n = COUNTER.fetch_add(1, Ordering::Relaxed);
4314    let nanos = std::time::SystemTime::now()
4315        .duration_since(std::time::UNIX_EPOCH)
4316        .map(|d| d.as_nanos() as u64)
4317        .unwrap_or(0);
4318    let mut z = nanos
4319        .wrapping_add(n.wrapping_mul(0x9E3779B97F4A7C15))
4320        .wrapping_add(0x9E3779B97F4A7C15);
4321    z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
4322    z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
4323    z ^= z >> 31;
4324    // seed 0 is a legal explicit value but a poor accidental one; keep it reachable only
4325    // when the caller asks for it.
4326    if z == 0 { 0x9E3779B97F4A7C15 } else { z }
4327}
4328
4329/// Honesty gate (gap-scan F4): semantic params we cannot honor are explicit 400s with the
4330/// offending param named — never silent downgrades (a client sending response_format:
4331/// json_object would get unvalidated free text and no error). Cosmetic fields (`user`,
4332/// `stream_options`) stay accept-and-ignore.
4333fn reject_unsupported(fields: &[(&str, bool, &str)]) -> Result<(), (String, String)> {
4334    for (param, present, why) in fields {
4335        if *present {
4336            return Err((format!("{param} is not supported{why}"), param.to_string()));
4337        }
4338    }
4339    Ok(())
4340}
4341
4342#[derive(PartialEq)]
4343enum ToolChoice {
4344    Auto,
4345    None,
4346}
4347
4348fn parse_tool_choice(v: &Option<serde_json::Value>) -> Result<ToolChoice, String> {
4349    match v {
4350        None | Some(serde_json::Value::Null) => Ok(ToolChoice::Auto),
4351        Some(serde_json::Value::String(s)) => match s.as_str() {
4352            "auto" => Ok(ToolChoice::Auto),
4353            "none" => Ok(ToolChoice::None),
4354            "required" => Err("tool_choice \"required\" is not supported (no constrained \
4355                               decoding); use \"auto\""
4356                .into()),
4357            other => Err(format!("bad tool_choice {other:?} (auto|none)")),
4358        },
4359        Some(serde_json::Value::Object(_)) => {
4360            Err("named-function tool_choice is not supported; use \"auto\"".into())
4361        }
4362        Some(other) => Err(format!("bad tool_choice: {other}")),
4363    }
4364}
4365
4366/// Map OpenAI `reasoning_effort` / OpenRouter `reasoning` onto the model's native thinking
4367/// control — ONE serve surface, per-arch mechanism (owner directive 2026-08-07: every
4368/// supported model is a thinking model).
4369///
4370/// The OpenAI/OpenRouter convention for reasoning-capable models: `low|medium|high` all mean
4371/// reasoning ON at that budget; `none|minimal` request (near-)zero reasoning; OpenRouter's
4372/// `reasoning: {enabled: false}` is the explicit off. Absent means the MODEL'S OWN default —
4373/// unless the operator declared `default_reasoning_effort` for the model in
4374/// MEMRA_MODEL_METADATA (`default_effort` here), in which case the UNSET case — and only
4375/// the unset case — resolves as if the client had sent that value (same match arms below,
4376/// so the downstream Request is byte-identical to the explicit request). Any explicit
4377/// client reasoning field wins over the deployment default:
4378///
4379/// | field value        | ThinkMode | effort level | qwen class      | gemma4        | hy3        | step35            |
4380/// |--------------------|-----------|--------------|-----------------|---------------|------------|-------------------|
4381/// | (absent)           | Default   | None         | think ON (tmpl) | think OFF     | no_think   | tail always open  |
4382/// | none / minimal     | NoThink   | "low"        | closed <think>  | closed channel| no_think   | Reasoning: low    |
4383/// | low                | Think     | "low"        | open <think>    | <\|think\|> ON| low        | Reasoning: low    |
4384/// | medium             | Think     | "medium"     | open <think>    | <\|think\|> ON| low (clamp)| Reasoning: medium |
4385/// | high               | Think     | "high"       | open <think>    | <\|think\|> ON| high       | Reasoning: high   |
4386/// | xhigh/max/ultra    | Think     | "high"       | open <think>    | <\|think\|> ON| high       | Reasoning: high   |
4387/// | {enabled: false}   | NoThink   | "low"        | closed <think>  | closed channel| no_think   | Reasoning: low    |
4388/// | {enabled: true}    | Think     | None         | open <think>    | <\|think\|> ON| low        | (tmpl default)    |
4389///
4390/// Returns `(think, effort_level, client_explicit)`. `effort_level` rides `Request::reasoning_effort` only
4391/// for templates that consume a level string (`ModelCaps::effort_levels`: step35, hy3;
4392/// `ModelCaps::dsv4`: the encoding_dsv4 effort ladder — on the 0731 encoding low = default
4393/// no prefix, high = a real prompt prefix, medium renders as the default level, and the
4394/// native "max" rung IS reachable: dsv4 is the one loaded template that distinguishes a
4395/// tier above "high" (0731: high -> ABSOLUTE_MAX, max -> BEYOND_MAX prefixes), so the
4396/// above-high aliases canonicalize to "max" for it instead of clamping — see
4397/// `canonical_effort_for` (hermes 2026-08-23: the unconditional clamp silently lost the
4398/// BEYOND_MAX tier for dsv4 clients); binary-switch templates are carried by `ThinkMode`
4399/// alone, so their prompts cannot be perturbed by a level they never read.
4400///
4401/// PRECEDENCE (issue #31, standard-surface law): an EXPLICIT boolean switch — OpenRouter
4402/// `reasoning.enabled`, or Anthropic `thinking.type` which `anthropic::translate` maps
4403/// onto it — wins the on/off decision over the switch an effort level implies; the effort
4404/// value is STILL validated against the one table (an invalid value is a 400 on every
4405/// surface, never a silent accept) and still supplies the level for level-consuming
4406/// templates. `vllm_switch` is the same kind of explicit boolean, arriving under the
4407/// vLLM/HF names (`enable_thinking`, `chat_template_kwargs.enable_thinking`); two explicit
4408/// switches that DISAGREE are a 400 rather than a coin-flip.
4409///
4410/// `client_explicit` (third return) says the CLIENT expressed a reasoning control itself —
4411/// false when the mode came only from the operator's `default_reasoning_effort`. Callers
4412/// use it to decide whether an unhonourable request is the client's 400 or the operator's
4413/// problem: refusing every request on a switchless template because of a deployment
4414/// default would take a model offline for a config choice the caller never made.
4415fn parse_think(
4416    reasoning_effort: &Option<String>,
4417    reasoning: &Option<serde_json::Value>,
4418    vllm_switch: Option<bool>,
4419    suppress_switch: Option<bool>,
4420    default_effort: Option<&str>,
4421    max_tier: bool,
4422) -> Result<(ThinkMode, Option<String>, bool), String> {
4423    let mut effort = reasoning_effort.clone();
4424    let ReasoningObject {
4425        mut enabled,
4426        effort: object_effort,
4427        exclude,
4428    } = parse_reasoning_object(reasoning)?;
4429    if let Some(e) = object_effort {
4430        effort = Some(e);
4431    }
4432    // vLLM-idiom switch (`enable_thinking` / `chat_template_kwargs.enable_thinking`) is the
4433    // same kind of explicit boolean as `reasoning.enabled`. Two explicit switches that
4434    // disagree get a 400: picking one silently would make the ignored one exactly the
4435    // accepted-and-ignored parameter this lane exists to remove.
4436    match (enabled, vllm_switch) {
4437        (Some(a), Some(b)) if a != b => {
4438            return Err(format!(
4439                "contradictory reasoning switches: reasoning.enabled={a} and \
4440                 enable_thinking={b} — send one"
4441            ));
4442        }
4443        (None, Some(b)) => enabled = Some(b),
4444        _ => {}
4445    }
4446    // SUPPRESSION IS OFF (owner ruling 2026-08-23, "we have to actually reason or not reason").
4447    // `include_reasoning:false` and `reasoning.exclude:true` used to hide the reasoning text
4448    // while the model still generated and we still billed it. They are now spellings of the
4449    // off-switch, folded onto the SAME boolean axis as `reasoning.enabled` — so they inherit
4450    // its precedence, its contradiction rule, and its named refusal on templates that cannot
4451    // honour an off-request. `include_reasoning:true` / `exclude:false` say "deliver it", which
4452    // is now the only behaviour, so they express no switch at all rather than pinning ON.
4453    //
4454    // Runs AFTER the vLLM fold on purpose: `enable_thinking:true` + `include_reasoning:false` is
4455    // a contradiction, and reaching it here means the refusal below NAMES include_reasoning
4456    // instead of blaming a `reasoning.enabled` the caller never sent.
4457    let suppress = match (exclude, suppress_switch) {
4458        (Some(true), _) | (_, Some(false)) => Some(false),
4459        _ => None,
4460    };
4461    match (enabled, suppress) {
4462        (Some(true), Some(false)) => {
4463            return Err(
4464                "contradictory reasoning switches: reasoning is enabled but \
4465                 include_reasoning:false / reasoning.exclude:true asks for no reasoning — \
4466                 on this server not delivering reasoning means not generating it, so send one"
4467                    .into(),
4468            );
4469        }
4470        (None, Some(b)) => enabled = Some(b),
4471        _ => {}
4472    }
4473    // Did the CLIENT itself ask for a reasoning mode? Recorded before the deployment
4474    // default is substituted, so the operator's default can never be mistaken for a
4475    // caller's explicit request.
4476    let client_explicit = effort.is_some() || enabled.is_some();
4477    // Deployment default: ONLY when the client expressed nothing at all — no effort on
4478    // either surface AND no `reasoning.enabled` in either direction. Substituting into
4479    // `effort` before the match keeps one mapping table: the resolved request cannot
4480    // diverge from an explicit request carrying the same value.
4481    if effort.is_none() && enabled.is_none() {
4482        effort = default_effort.map(str::to_string);
4483    }
4484    // Validate BEFORE the switch precedence below, so an out-of-table value is rejected
4485    // even when it arrives next to an explicit enabled/disabled (issue #31: /v1/messages
4486    // accepted every string because its value never reached this table; the old
4487    // `enabled == false` early-return here skipped validation the same way).
4488    let effort_arm = match effort.as_deref() {
4489        None => None,
4490        Some(raw) => {
4491            let level = canonical_effort_for(raw, max_tier).ok_or_else(|| {
4492                format!(
4493                    "bad reasoning_effort {raw:?} \
4494                     (none|minimal|low|medium|high; xhigh/max/ultra clamp to the \
4495                     highest level this model's template distinguishes)"
4496                )
4497            })?;
4498            Some(match level {
4499                "none" | "minimal" => (ThinkMode::NoThink, "low"),
4500                "low" => (ThinkMode::Think, "low"),
4501                "medium" => (ThinkMode::Think, "medium"),
4502                "max" => (ThinkMode::Think, "max"),
4503                _ => (ThinkMode::Think, "high"),
4504            })
4505        }
4506    };
4507    let (think, level) = match (enabled, effort_arm) {
4508        // OpenRouter "thinking off" / Anthropic thinking.type "disabled": the strongest
4509        // off-request any surface can express — it wins over a coexisting effort level.
4510        (Some(false), _) => (ThinkMode::NoThink, Some("low".to_string())),
4511        (Some(true), arm) => (ThinkMode::Think, arm.map(|(_, level)| level.to_string())),
4512        (None, Some((think, level))) => (think, Some(level.to_string())),
4513        (None, None) => (ThinkMode::Default, None),
4514    };
4515    Ok((think, level, client_explicit))
4516}
4517
4518/// The three keys of the OpenRouter `reasoning` object this server understands.
4519struct ReasoningObject {
4520    enabled: Option<bool>,
4521    effort: Option<String>,
4522    exclude: Option<bool>,
4523}
4524
4525/// Parse the OpenRouter `reasoning` object STRICTLY — every key named, every unknown key a 400.
4526///
4527/// THE DEFECT THIS CLOSES (lane/reasoning-schema-20260823): `reasoning` is typed
4528/// `Option<serde_json::Value>`, so serde structurally cannot reject a key, and only `enabled`,
4529/// `effort` and `exclude` were ever read. Anything else — most importantly OpenRouter's real
4530/// `reasoning.max_tokens` — was accepted with 200 and changed nothing. That is the same
4531/// accepted-and-ignored class PR #33 closed one level up for `chat_template_kwargs`, and the
4532/// same law applies: a key this server cannot act on is a named refusal, not a silent drop.
4533///
4534/// The wrong-TYPE cases are refusals too, and that also removes a cross-surface divergence:
4535/// `reasoning.effort: 3` used to fall through `as_str()` to `None` and be silently ignored on
4536/// chat, while the Anthropic surface's `output_config.effort` 400'd on exactly the same
4537/// mistake. One schema means one answer to the same malformed request on every surface.
4538///
4539/// `reasoning.max_tokens` gets its own message rather than the generic unknown-key one: it is
4540/// a real field a real client sends, so the refusal has to say WHY we will not pretend to
4541/// honour it (owner ruling: reasoning is output, `max_tokens` is the single output budget
4542/// covering it, and there is no separate reasoning budget on this server).
4543fn parse_reasoning_object(
4544    reasoning: &Option<serde_json::Value>,
4545) -> Result<ReasoningObject, String> {
4546    let mut out = ReasoningObject {
4547        enabled: None,
4548        effort: None,
4549        exclude: None,
4550    };
4551    let Some(v) = reasoning else { return Ok(out) };
4552    let obj = match v {
4553        serde_json::Value::Null => return Ok(out),
4554        serde_json::Value::Object(obj) => obj,
4555        _ => return Err("reasoning must be an object".into()),
4556    };
4557    for (key, value) in obj {
4558        // An explicit JSON null means "not set" for a KEY exactly as it already does for the whole
4559        // object — that is how several SDKs serialise an unset optional field, and `{"effort":
4560        // null}` used to be a 400 here while `/v1/responses` and `/v1/messages` both read it as
4561        // unset. The skip is scoped to the keys we IMPLEMENT, per arm: a first cut applied it
4562        // before this match, which meant `{"max_tokens": null}` and `{"banana": null}` returned
4563        // 200 — smuggling an unhonourable key past its own refusal by nulling the value, which is
4564        // the very class this function exists to close.
4565        match key.as_str() {
4566            "enabled" => {
4567                if !value.is_null() {
4568                    out.enabled = Some(
4569                        value
4570                            .as_bool()
4571                            .ok_or("reasoning.enabled must be true or false")?,
4572                    );
4573                }
4574            }
4575            "exclude" => {
4576                if !value.is_null() {
4577                    out.exclude = Some(
4578                        value
4579                            .as_bool()
4580                            .ok_or("reasoning.exclude must be true or false")?,
4581                    );
4582                }
4583            }
4584            "effort" => {
4585                if !value.is_null() {
4586                    out.effort = Some(
4587                        value
4588                            .as_str()
4589                            .ok_or("reasoning.effort must be a string")?
4590                            .to_string(),
4591                    );
4592                }
4593            }
4594            "max_tokens" => {
4595                return Err(
4596                    "reasoning.max_tokens is not supported by this server: reasoning tokens \
4597                     are output tokens here, and max_tokens is the ONE output budget covering \
4598                     reasoning and content together — there is no separate reasoning budget to \
4599                     spend against, so honouring this field is impossible rather than merely \
4600                     unimplemented. Use max_tokens for the budget, and reasoning.effort (or \
4601                     reasoning.enabled:false) to spend less of it on reasoning"
4602                        .into(),
4603                );
4604            }
4605            other => {
4606                return Err(format!(
4607                    "reasoning.{other} is not a field this server implements (it would change \
4608                     nothing about the request); the supported keys are enabled, effort and \
4609                     exclude"
4610                ));
4611            }
4612        }
4613    }
4614    Ok(out)
4615}
4616
4617/// vLLM `chat_template_kwargs` -> the kwargs this renderer can honour.
4618///
4619/// The renderer is Rust, not jinja, so a kwarg it does not implement changes NOTHING about
4620/// the prompt. Accepting such a kwarg with 200 is the accepted-and-ignored defect one level
4621/// down from `enable_thinking`, so every unknown key is a 400 that names the key. Returns
4622/// the `enable_thinking` value when present.
4623///
4624/// `preserve_thinking` is Qwen3.8's THIRD official thinking kwarg (Qwen/Qwen3.8-27B card;
4625/// Qwen's own quickstart sends `{"enable_thinking": True, "preserve_thinking": True}`). It
4626/// governs whether PRIOR assistant turns replay their `<think>` block into the prompt.
4627///
4628/// The renderer's ladder arm now implements the vendor DEFAULT (lane/dflash2-session-reuse):
4629/// the template's replay condition is `preserve_thinking is undefined or preserve_thinking is
4630/// true or …`, so the absent default is replay — every prior assistant turn renders
4631/// `<think>\n{reasoning_content|trim}\n</think>\n\n` before its content, empty when the client
4632/// sent no reasoning. `true` therefore names exactly what this server renders and is ACCEPTED.
4633///
4634/// `false` (strip the block for turns at or before the last real user query) remains
4635/// unimplemented and refused: it needs the template's `last_query_index` walk, and silently
4636/// serving the replay bytes under a strip request would be a lie about the prompt.
4637fn parse_template_kwargs(kwargs: &Option<serde_json::Value>) -> Result<Option<bool>, String> {
4638    let Some(v) = kwargs else { return Ok(None) };
4639    let obj = match v {
4640        serde_json::Value::Null => return Ok(None),
4641        serde_json::Value::Object(obj) => obj,
4642        _ => return Err("chat_template_kwargs must be an object".into()),
4643    };
4644    let mut switch = None;
4645    for (key, value) in obj {
4646        match key.as_str() {
4647            "enable_thinking" => {
4648                switch = Some(
4649                    value
4650                        .as_bool()
4651                        .ok_or("chat_template_kwargs.enable_thinking must be true or false")?,
4652                );
4653            }
4654            "preserve_thinking" => {
4655                let preserve = value
4656                    .as_bool()
4657                    .ok_or("chat_template_kwargs.preserve_thinking must be true or false")?;
4658                if !preserve {
4659                    return Err(
4660                        "chat_template_kwargs.preserve_thinking:false is not supported by this \
4661                         server: the renderer implements the vendor DEFAULT (replay every prior \
4662                         assistant turn's <think> block, empty when no reasoning was sent) but \
4663                         not the strip arm — serving replay bytes under a strip request would \
4664                         misdescribe the prompt. Omit the flag or send true"
4665                            .into(),
4666                    );
4667                }
4668                // true == the vendor default the renderer implements; nothing to carry.
4669            }
4670            other => {
4671                return Err(format!(
4672                    "chat_template_kwargs.{other} is not supported by this server's \
4673                     template renderer (it would change nothing about the prompt); the only \
4674                     supported key is enable_thinking (preserve_thinking is RECOGNISED but \
4675                     refuses in both directions — see its own message)"
4676                ));
4677            }
4678        }
4679    }
4680    Ok(switch)
4681}
4682
4683/// Reconcile the two vLLM spellings of the thinking switch: top-level `enable_thinking` and
4684/// `chat_template_kwargs.enable_thinking`. Both present and disagreeing is a 400 — see
4685/// `parse_think`'s contradiction rule, same reason.
4686fn resolve_vllm_think_switch(
4687    enable_thinking: Option<bool>,
4688    kwargs: &Option<serde_json::Value>,
4689) -> Result<Option<bool>, String> {
4690    let from_kwargs = parse_template_kwargs(kwargs)?;
4691    match (enable_thinking, from_kwargs) {
4692        (Some(a), Some(b)) if a != b => Err(format!(
4693            "contradictory reasoning switches: enable_thinking={a} and \
4694             chat_template_kwargs.enable_thinking={b} — send one"
4695        )),
4696        (Some(a), _) => Ok(Some(a)),
4697        (None, b) => Ok(b),
4698    }
4699}
4700
4701/// Canonical reasoning-effort table — the ONE allowlist every surface consults: chat
4702/// `reasoning_effort`, OpenRouter/`/v1/responses` `reasoning.effort`, Anthropic
4703/// `/v1/messages` `output_config.effort`. Returns the canonical level, or None for a
4704/// value outside the set (the caller's 400). `xhigh`/`max`/`ultra` clamp to the highest
4705/// level the model's template distinguishes — because real default-config clients send
4706/// them (codex sends `xhigh` on /v1/responses; Claude Code sends `xhigh` on /v1/messages
4707/// on current models): rejecting them refuses stock CLI sessions, and accepting them on
4708/// SOME surfaces only was issue #31's divergence.
4709///
4710/// `dsv4_max`: deepseek-v4 is the ONE loaded template with a rung ABOVE "high" (0731
4711/// encoding: "high" -> DS_EFFORT_ABSOLUTE_MAX, "max" -> DS_EFFORT_BEYOND_MAX prefixes;
4712/// preview: "high" no-op, "max" -> ABSOLUTE_MAX — `dsv4_effort_prefix`). For it the
4713/// above-high aliases canonicalize to "max"; clamping them to "high" silently discarded
4714/// a real tier (hermes finding, fixed 2026-08-23). Every other template's highest rung
4715/// is "high", so the clamp there stays correct and byte-identical to before.
4716///
4717/// `minimal` = OFF here, and that is a deliberate divergence from Qwen's hosted API (which
4718/// maps minimal to low with reasoning on briefly): this server's schema promises that its
4719/// no-reasoning side is real. See the mapping table in SERVING.md.
4720pub(crate) fn canonical_effort_for(value: &str, max_tier: bool) -> Option<&'static str> {
4721    match value {
4722        "none" => Some("none"),
4723        "minimal" => Some("minimal"),
4724        "low" => Some("low"),
4725        "medium" => Some("medium"),
4726        "high" => Some("high"),
4727        // `max_tier` = this model's template distinguishes a rung ABOVE `high`, so the
4728        // above-high aliases canonicalize to "max" instead of clamping into "high" and losing
4729        // the tier. True for deepseek-v4 0731 (high -> ABSOLUTE_MAX, max -> BEYOND_MAX) and for
4730        // GLM-5.3-Flash (low|high|max, `max` its own default). Every binary-switch and
4731        // three-rung template keeps the clamp — it cannot render a level it does not define.
4732        "xhigh" | "max" | "ultra" => Some(if max_tier { "max" } else { "high" }),
4733        _ => None,
4734    }
4735}
4736
4737/// Membership + non-dsv4 canonicalization (the pre-exemption table; see
4738/// `canonical_effort_for` for the dsv4 "max" rung).
4739pub(crate) fn canonical_effort(value: &str) -> Option<&'static str> {
4740    canonical_effort_for(value, false)
4741}
4742
4743/// serde_json::Value -> chat::Val (serde-free tree for the gemma4 tooluse arm). `Num` keeps
4744/// the value's exact numeric text so the rendered bytes match jinja's `{{ number }}`.
4745fn json_to_val(v: &serde_json::Value) -> chat::Val {
4746    match v {
4747        serde_json::Value::Null => chat::Val::Null,
4748        serde_json::Value::Bool(b) => chat::Val::Bool(*b),
4749        serde_json::Value::Number(n) => chat::Val::Num(n.to_string()),
4750        serde_json::Value::String(s) => chat::Val::Str(s.clone()),
4751        serde_json::Value::Array(a) => chat::Val::Arr(a.iter().map(json_to_val).collect()),
4752        // preserve_order is on (Cargo.toml): the object iterates in client key order, which
4753        // the gemma dialect then dictsorts — ties keep this order, matching jinja.
4754        serde_json::Value::Object(o) => chat::Val::Obj(
4755            o.iter()
4756                .map(|(k, val)| (k.clone(), json_to_val(val)))
4757                .collect(),
4758        ),
4759    }
4760}
4761
4762/// Validate tool schemas and pre-serialize them for the template's <tools> block; also produce
4763/// the gemma4 tooluse dialect's typed `function` objects, and extract declared parameter types
4764/// (function -> parameter -> type) for argument coercion.
4765#[allow(clippy::type_complexity)]
4766fn prepare_tools(
4767    tools: &[serde_json::Value],
4768) -> Result<
4769    (
4770        Vec<String>,
4771        Vec<chat::Val>,
4772        HashMap<String, HashMap<String, String>>,
4773    ),
4774    String,
4775> {
4776    let mut tools_json = Vec::with_capacity(tools.len());
4777    let mut tools_struct = Vec::with_capacity(tools.len());
4778    let mut schemas: HashMap<String, HashMap<String, String>> = HashMap::new();
4779    for t in tools {
4780        let f = t
4781            .get("function")
4782            .ok_or("each tool needs a function object")?;
4783        let name = f
4784            .get("name")
4785            .and_then(|n| n.as_str())
4786            .ok_or("each tool needs function.name")?;
4787        let mut params: HashMap<String, String> = HashMap::new();
4788        if let Some(props) = f
4789            .get("parameters")
4790            .and_then(|p| p.get("properties"))
4791            .and_then(|p| p.as_object())
4792        {
4793            for (p, def) in props {
4794                if let Some(ty) = def.get("type").and_then(|t| t.as_str()) {
4795                    params.insert(p.clone(), ty.to_string());
4796                }
4797            }
4798        }
4799        schemas.insert(name.to_string(), params);
4800        tools_json.push(pyjson_str(t));
4801        // gemma4 arm reads the FUNCTION object (name/description/parameters/response).
4802        tools_struct.push(json_to_val(f));
4803    }
4804    Ok((tools_json, tools_struct, schemas))
4805}
4806
4807/// Re-render an assistant-history tool call for the template. Value law mirrors the
4808/// template's `args_value | tojson if mapping/sequence else | string`: strings raw,
4809/// objects/arrays python-style JSON; scalars use their JSON text (`true`/`3`/`null` —
4810/// JSON spelling, not python's, so a parse round-trip stays self-consistent).
4811fn render_req_tool_call(tc: &ReqToolCall) -> Result<TmplToolCall, String> {
4812    let parsed: serde_json::Value = match &tc.function.arguments {
4813        serde_json::Value::Null => json!({}),
4814        serde_json::Value::String(s) if s.trim().is_empty() => json!({}),
4815        serde_json::Value::String(s) => serde_json::from_str(s)
4816            .map_err(|e| format!("tool_calls arguments is not valid JSON: {e}"))?,
4817        v @ serde_json::Value::Object(_) => v.clone(),
4818        _ => return Err("tool_calls arguments must be a JSON object".into()),
4819    };
4820    let obj = parsed
4821        .as_object()
4822        .ok_or("tool_calls arguments must decode to a JSON object")?;
4823    let params = obj
4824        .iter()
4825        .map(|(k, v)| {
4826            let rendered = match v {
4827                serde_json::Value::String(s) => s.clone(),
4828                v @ (serde_json::Value::Object(_) | serde_json::Value::Array(_)) => pyjson_str(v),
4829                scalar => scalar.to_string(),
4830            };
4831            (k.clone(), rendered)
4832        })
4833        .collect();
4834    // gemma4 tooluse dialect: typed args (dictsorted + dialect-rendered by the renderer) and
4835    // the call id (matched to a following tool turn's tool_call_id to name the response).
4836    let args = obj
4837        .iter()
4838        .map(|(k, v)| (k.clone(), json_to_val(v)))
4839        .collect();
4840    Ok(TmplToolCall {
4841        name: tc.function.name.clone(),
4842        params,
4843        args,
4844        id: tc.id.clone(),
4845    })
4846}
4847
4848/// OpenAI response entry for one parsed call.
4849fn tool_call_json(c: &ParsedToolCall) -> serde_json::Value {
4850    json!({ "id": c.id, "type": "function",
4851            "function": { "name": c.name, "arguments": c.arguments } })
4852}
4853
4854/// The whole server as a library entry point (BASE-4 stays: this crate is the
4855/// async-only seam; the bin in `src/main.rs` is one line deep). Public so a
4856/// deployment-owned binary can wrap the same server with its own wiring.
4857async fn serve_bounded_http_with_limits<F>(
4858    listener: tokio::net::TcpListener,
4859    app: Router,
4860    shutdown: F,
4861    header_read_timeout: std::time::Duration,
4862    max_connections: usize,
4863    connection_max_lifetime: std::time::Duration,
4864) -> std::io::Result<()>
4865where
4866    F: std::future::Future<Output = ()> + Send,
4867{
4868    let connections = Arc::new(tokio::sync::Semaphore::new(max_connections));
4869    let (connection_shutdown, _) = tokio::sync::watch::channel(false);
4870    let mut connection_tasks = tokio::task::JoinSet::new();
4871    let mut shutdown = Box::pin(shutdown);
4872
4873    loop {
4874        tokio::select! {
4875            _ = &mut shutdown => break,
4876            joined = connection_tasks.join_next(), if !connection_tasks.is_empty() => {
4877                if let Some(Err(error)) = joined {
4878                    eprintln!("[server] connection task failed: {error}");
4879                }
4880            }
4881            accepted = listener.accept() => {
4882                let (stream, _) = match accepted {
4883                    Ok(connection) => connection,
4884                    Err(error) => {
4885                        eprintln!("[server] accept failed: {error}");
4886                        tokio::time::sleep(std::time::Duration::from_millis(100)).await;
4887                        continue;
4888                    }
4889                };
4890                let permit = match connections.clone().try_acquire_owned() {
4891                    Ok(permit) => permit,
4892                    Err(_) => {
4893                        drop(stream);
4894                        continue;
4895                    }
4896                };
4897                let service = app.clone().map_request(
4898                    |request: hyper::Request<hyper::body::Incoming>| request.map(Body::new),
4899                );
4900                let service = hyper_util::service::TowerToHyperService::new(service);
4901                let io = hyper_util::rt::TokioIo::new(stream);
4902                let mut builder = hyper_util::server::conn::auto::Builder::new(
4903                    hyper_util::rt::TokioExecutor::new(),
4904                );
4905                builder
4906                    .http1()
4907                    .timer(hyper_util::rt::TokioTimer::new())
4908                    .header_read_timeout(header_read_timeout)
4909                    .max_headers(64);
4910                builder
4911                    .http2()
4912                    .timer(hyper_util::rt::TokioTimer::new())
4913                    .max_concurrent_streams(MAX_HTTP2_STREAMS_PER_CONNECTION)
4914                    .keep_alive_interval(Some(std::time::Duration::from_secs(30)))
4915                    .keep_alive_timeout(std::time::Duration::from_secs(10));
4916                let mut connection = Box::pin(builder
4917                    .serve_connection_with_upgrades(io, service)
4918                    .into_owned());
4919                let mut shutdown_rx = connection_shutdown.subscribe();
4920                connection_tasks.spawn(async move {
4921                    let _permit = permit;
4922                    tokio::select! {
4923                        result = connection.as_mut() => {
4924                            let _ = result;
4925                        }
4926                        _ = tokio::time::sleep(connection_max_lifetime) => {
4927                            // Stop accepting new requests at the age boundary, but let every
4928                            // active response (including long SSE) finish. A hard timeout here
4929                            // truncated valid generations and made connection age part of the
4930                            // response contract.
4931                            connection.as_mut().graceful_shutdown();
4932                            let _ = connection.await;
4933                        }
4934                        _ = shutdown_rx.changed() => {
4935                            connection.as_mut().graceful_shutdown();
4936                            let _ = connection.await;
4937                        }
4938                    }
4939                });
4940            }
4941        }
4942    }
4943    drop(listener);
4944    let _ = connection_shutdown.send(true);
4945    let drained = tokio::time::timeout(std::time::Duration::from_secs(5), async {
4946        while connection_tasks.join_next().await.is_some() {}
4947    })
4948    .await;
4949    if drained.is_err() {
4950        connection_tasks.abort_all();
4951        eprintln!("[server] WARN: HTTP connections exceeded the 5s graceful close deadline");
4952    }
4953    Ok(())
4954}
4955
4956async fn serve_bounded_http<F>(
4957    listener: tokio::net::TcpListener,
4958    app: Router,
4959    shutdown: F,
4960) -> std::io::Result<()>
4961where
4962    F: std::future::Future<Output = ()> + Send,
4963{
4964    serve_bounded_http_with_limits(
4965        listener,
4966        app,
4967        shutdown,
4968        HTTP1_HEADER_READ_TIMEOUT,
4969        MAX_HTTP_CONNECTIONS,
4970        HTTP_CONNECTION_MAX_LIFETIME,
4971    )
4972    .await
4973}
4974
4975#[tokio::main]
4976pub async fn serve_main() -> Result<(), Box<dyn std::error::Error>> {
4977    serve_with(ServerWiring::stock()).await
4978}
4979
4980/// How a metering implementation reaches the server.
4981enum MeteringWiring {
4982    /// No accounting: every request is admitted (auth still applies), nothing is
4983    /// counted or billed. Only the engine is open; admission policy, billing,
4984    /// capture, and provisioning are the deployment binary's business.
4985    Stock,
4986    /// Deployment-supplied factory, plus whatever surfaces the deployment runs
4987    /// beside the engine. It CLAIMS the env vars it consumes itself
4988    /// (`ServerWiring::claiming`); any deployment-surface var left unclaimed is a
4989    /// startup FATAL, because set-but-unread configuration must not fail open.
4990    Custom(metering::MeteringFactory),
4991}
4992
4993/// Deployment wiring for a custom binary. `serve_main` is exactly
4994/// `serve_with(ServerWiring::reference())`; a deployment-owned binary substitutes
4995/// its own metering and hooks the runtime handles it needs.
4996pub struct ServerWiring {
4997    metering: MeteringWiring,
4998    /// Called once, when the worker is live (models loaded, commands accepted),
4999    /// with the runtime handles a deployment-side surface needs. Not awaited.
5000    on_ready: Option<Box<dyn FnOnce(RuntimeHandles) + Send>>,
5001    /// Reference-only env vars this deployment consumes ITSELF (its own admin, its
5002    /// own capture). Anything on the fatal list and not claimed is a startup FATAL
5003    /// under custom wiring — set-but-unread configuration never fails open.
5004    claimed_env: Vec<&'static str>,
5005}
5006
5007impl ServerWiring {
5008    /// The stock open-engine server: no accounting, no admin listener, no capture.
5009    pub fn stock() -> Self {
5010        ServerWiring {
5011            metering: MeteringWiring::Stock,
5012            on_ready: None,
5013            claimed_env: Vec::new(),
5014        }
5015    }
5016
5017    /// A server whose admission/accounting is the factory's. See
5018    /// [`MeteringWiring::Custom`] for what this deliberately turns off.
5019    pub fn with_metering(factory: metering::MeteringFactory) -> Self {
5020        ServerWiring {
5021            metering: MeteringWiring::Custom(factory),
5022            on_ready: None,
5023            claimed_env: Vec::new(),
5024        }
5025    }
5026
5027    /// Declare that the deployment consumes this reference-only env var itself
5028    /// (e.g. its own admin listener reads `MEMRA_ADMIN_ADDR`), disarming the
5029    /// custom-wiring startup FATAL for exactly that var.
5030    pub fn claiming(mut self, var: &'static str) -> Self {
5031        self.claimed_env.push(var);
5032        self
5033    }
5034
5035    pub fn on_ready(mut self, hook: impl FnOnce(RuntimeHandles) + Send + 'static) -> Self {
5036        self.on_ready = Some(Box::new(hook));
5037        self
5038    }
5039}
5040
5041/// Runtime handles handed to [`ServerWiring::on_ready`] — the narrow set of
5042/// engine-runtime operations a deployment-side admin surface needs.
5043pub struct RuntimeHandles {
5044    pub trim: TrimHandle,
5045    /// Tenant lifecycle purge (lane/kv-tenancy-compaction-20260831): the deployment
5046    /// admin surface calls this from its key-revocation and tenant-deletion paths.
5047    pub purge: PurgeHandle,
5048    /// Host-tier deploy handoff (lane/host-tier-deploy-warmth-20260901): the deployment
5049    /// admin surface exposes these as `POST /admin/kv-host/export` (called by
5050    /// serve-deploy on the DRAINED old slot after the edge flip) and
5051    /// `POST /admin/kv-host/import` (called on the promoted slot right after). Both are
5052    /// inert unless MEMRA_KV_HOST_HANDOFF names a path on the slot.
5053    pub kv_handoff: HostHandoffHandle,
5054    /// Flips to `true` when the graceful drain completes (the moment the in-tree
5055    /// admin listener stops). A deployment-side surface MUST end and drop its
5056    /// [`TrimHandle`] AND [`PurgeHandle`] on this signal: each handle wraps a worker
5057    /// command sender, and the GPU worker only exits when every sender is dropped.
5058    pub shutdown: tokio::sync::watch::Receiver<bool>,
5059}
5060
5061/// Ask the worker to trim its pools (the engine half of `/admin/trim`). Cloneable;
5062/// answers with the worker's own trim report.
5063#[derive(Clone)]
5064pub struct TrimHandle {
5065    cmd_tx: Sender<Cmd>,
5066}
5067
5068impl TrimHandle {
5069    /// 503-shaped errors as strings: worker down, or no answer within 30s.
5070    pub async fn trim(&self) -> Result<serde_json::Value, String> {
5071        let (tx, rx) = tokio::sync::oneshot::channel();
5072        if self.cmd_tx.send(Cmd::TrimPools(tx)).is_err() {
5073            return Err("worker is down".into());
5074        }
5075        match tokio::time::timeout(std::time::Duration::from_secs(30), rx).await {
5076            Ok(Ok(report)) => Ok(json!(report)),
5077            _ => Err("worker did not answer the trim within 30s".into()),
5078        }
5079    }
5080}
5081
5082/// Purge one tenant's parked KV state (the engine half of a deployment admin
5083/// `/admin/tenants/{tenant}/purge`; lane/kv-tenancy-compaction-20260831, tiering spec
5084/// §0.5). Contract notes for the deployment surface: the path parameter is `{tenant}`
5085/// (the keyring tenant id, the same string `--gen-key <tenant>` took), never
5086/// `{tenant_id}`; fire it from key revocation AND tenant deletion; a report with
5087/// `device_pinned_left > 0` means in-flight sessions still lease device entries in the
5088/// tenant's namespaces, so re-fire after the drain. Cloneable, same lifetime contract
5089/// as [`TrimHandle`]: drop it on the shutdown signal.
5090#[derive(Clone)]
5091pub struct PurgeHandle {
5092    cmd_tx: Sender<Cmd>,
5093}
5094
5095impl PurgeHandle {
5096    /// 503-shaped errors as strings: worker down, or no answer within 30s.
5097    pub async fn purge_tenant(&self, tenant: &str) -> Result<serde_json::Value, String> {
5098        let (tx, rx) = tokio::sync::oneshot::channel();
5099        let cmd = Cmd::PurgeTenantHost {
5100            tenant: tenant.to_string(),
5101            tx,
5102        };
5103        if self.cmd_tx.send(cmd).is_err() {
5104            return Err("worker is down".into());
5105        }
5106        match tokio::time::timeout(std::time::Duration::from_secs(30), rx).await {
5107            Ok(Ok(report)) => Ok(json!(report)),
5108            _ => Err("worker did not answer the purge within 30s".into()),
5109        }
5110    }
5111}
5112
5113/// Host-tier deploy handoff (lane/host-tier-deploy-warmth-20260901): the engine half of a
5114/// deployment admin `POST /admin/kv-host/export` / `POST /admin/kv-host/import` pair.
5115/// Contract notes for the deployment surface: export is called ONLY on the drained old
5116/// slot (it refuses under traffic unless `force`, and the write stalls that slot's ticks
5117/// for its duration, expected and harmless when drained); import answers as soon as the
5118/// file header validates, then re-materializes entries one per tick in the background
5119/// (watch `prefix_host_handoff_*` in /metrics for completion). Same lifetime contract as
5120/// [`TrimHandle`]: drop it on the shutdown signal.
5121#[derive(Clone)]
5122pub struct HostHandoffHandle {
5123    cmd_tx: Sender<Cmd>,
5124}
5125
5126impl HostHandoffHandle {
5127    /// Errors as strings: worker down, refused, or no answer. The timeout is generous by
5128    /// design: tens of GB of drain-demote + NVMe write happen inside the reply.
5129    pub async fn export(&self, force: bool) -> Result<serde_json::Value, String> {
5130        let (tx, rx) = tokio::sync::oneshot::channel();
5131        if self
5132            .cmd_tx
5133            .send(Cmd::ExportHostHandoff { force, tx })
5134            .is_err()
5135        {
5136            return Err("worker is down".into());
5137        }
5138        match tokio::time::timeout(std::time::Duration::from_secs(900), rx).await {
5139            Ok(Ok(Ok(report))) => Ok(json!(report)),
5140            Ok(Ok(Err(refused))) => Err(refused),
5141            _ => Err("worker did not answer the export within 900s".into()),
5142        }
5143    }
5144
5145    /// Begin the drip import; answers with the validated header (fast: no entry bytes are
5146    /// read yet) or the refusal reason.
5147    pub async fn import(&self) -> Result<serde_json::Value, String> {
5148        let (tx, rx) = tokio::sync::oneshot::channel();
5149        if self.cmd_tx.send(Cmd::ImportHostHandoff { tx }).is_err() {
5150            return Err("worker is down".into());
5151        }
5152        match tokio::time::timeout(std::time::Duration::from_secs(30), rx).await {
5153            Ok(Ok(Ok(start))) => Ok(json!(start)),
5154            Ok(Ok(Err(refused))) => Err(refused),
5155            _ => Err("worker did not answer the import within 30s".into()),
5156        }
5157    }
5158}
5159
5160pub async fn serve_with(wiring: ServerWiring) -> Result<(), Box<dyn std::error::Error>> {
5161    // Key lifecycle CLI (lane/api-keys): `--gen-key <tenant>` / `--revoke-key <prefix>`
5162    // manage the keyring and exit — no engine, no GPU, no model load.
5163    let args: Vec<String> = std::env::args().skip(1).collect();
5164    // `--version` prints the build identity and exits: no engine, no GPU, no model load. So
5165    // the fingerprint of a DEPLOYED artifact is checkable on any box, and in the release
5166    // container that produced it, without touching a serving stack. That check is the one
5167    // that would have caught `memra-unknown` before it reached a customer.
5168    if args.iter().any(|a| a == "--version" || a == "-V") {
5169        println!("memra-server {}", env!("CARGO_PKG_VERSION"));
5170        println!("system_fingerprint {SYSTEM_FINGERPRINT}");
5171        println!("build_id_src {BUILD_ID_SRC}");
5172        println!("git_sha {BUILD_GIT_SHA}");
5173        if !BUILD_ID_NOTE.is_empty() {
5174            println!("degraded {BUILD_ID_NOTE}");
5175        }
5176        return Ok(());
5177    }
5178    if let Some(code) = auth::run_cli(&args) {
5179        std::process::exit(code);
5180    }
5181    // Build provenance is the FIRST line of every boot. An unknown fingerprint is how this
5182    // defect hid: a build with a meaningless identity looked exactly like a good one, on
5183    // both sides of the deploy.
5184    eprintln!("{}", build_identity_line());
5185    if BUILD_ID_SRC != build_id::BUILD_ID_SRC_TREE {
5186        eprintln!(
5187            "[server] WARNING: build identity is DEGRADED: {BUILD_ID_NOTE}. \
5188             system_fingerprint {SYSTEM_FINGERPRINT} carries a version-only id, so it does \
5189             NOT identify the source this binary was compiled from and published \
5190             performance pins cannot be verified against it (darklanes \
5191             tools/check-claim-builds.mjs --live). Rebuild where the workspace source tree \
5192             is readable."
5193        );
5194    }
5195    // Keyring (MEMRA_API_KEYS): parsed once here so a bad config is a startup FATAL,
5196    // not a per-request surprise. Absent = single-key/open behavior, unchanged.
5197    auth::init_from_env();
5198    let api_auth = match ApiAuth::from_env() {
5199        Ok(auth) => auth,
5200        Err(err) => {
5201            eprintln!("[server] FATAL: {err}");
5202            std::process::exit(1);
5203        }
5204    };
5205    let addr = std::env::var("MEMRA_ADDR").unwrap_or_else(|_| "127.0.0.1:8080".into());
5206    let allow_open_bind = std::env::var("MEMRA_ALLOW_OPEN_BIND").as_deref() == Ok("1");
5207    let (bind_addr, bind_loopback) = match resolve_bind_addr(&addr) {
5208        Ok(resolved) => resolved,
5209        Err(err) => {
5210            eprintln!("[server] FATAL: {err}");
5211            std::process::exit(1);
5212        }
5213    };
5214    // The refusal goes through validate_bind_security — the SAME function the
5215    // exposed_open_bind_is_refused_before_server_start test exercises. It used to be
5216    // duplicated inline here, so the test was pinning a copy of the gate rather than
5217    // the gate itself (dead_code exposed the split).
5218    if let Err(message) = validate_bind_security(&addr, api_auth.configured(), allow_open_bind) {
5219        eprintln!("[server] FATAL: {message}");
5220        std::process::exit(1);
5221    }
5222    if !bind_loopback && !api_auth.configured() {
5223        eprintln!(
5224            "[server] WARNING: MEMRA_ALLOW_OPEN_BIND=1 permits open completion routes on {addr}; \
5225             metrics remain bearer-protected"
5226        );
5227    }
5228    let metrics_token = match std::env::var("MEMRA_METRICS_TOKEN") {
5229        Ok(token) if token.is_empty() => {
5230            eprintln!("[server] FATAL: MEMRA_METRICS_TOKEN must not be empty");
5231            std::process::exit(1);
5232        }
5233        Ok(token) => Some(token),
5234        Err(std::env::VarError::NotPresent) => None,
5235        Err(std::env::VarError::NotUnicode(_)) => {
5236            eprintln!("[server] FATAL: MEMRA_METRICS_TOKEN must be valid UTF-8");
5237            std::process::exit(1);
5238        }
5239    };
5240    let metrics_auth = MetricsAuth::new(bind_loopback, api_auth.configured(), metrics_token);
5241
5242    let models = parse_models_config();
5243    let (openrouter_metadata, provider_metadata) = match load_openrouter_metadata(&models) {
5244        Ok(loaded) => loaded,
5245        Err(err) => {
5246            eprintln!("[server] FATAL: {err}");
5247            std::process::exit(1);
5248        }
5249    };
5250    // The metering seam splits here. The STOCK server ships no accounting: only the
5251    // engine is open, and admission policy / billing / capture / the provisioning
5252    // surface are the deployment binary's business (owner razor 2026-08-29). Their
5253    // env vars are startup FATALs unless the wiring CLAIMS them — set-but-unread
5254    // configuration never fails open.
5255    let metering_obj: Option<Arc<dyn metering::Metering>> = {
5256        let factory = match wiring.metering {
5257            MeteringWiring::Stock => None,
5258            MeteringWiring::Custom(factory) => Some(factory),
5259        };
5260        for deployment_only in [
5261            "MEMRA_REQUEST_LEDGER",
5262            "MEMRA_TENANT_BUDGETS",
5263            "MEMRA_ADMIN_ADDR",
5264            "MEMRA_ADMIN_TOKEN_FILE",
5265            "MEMRA_CAPTURE_DIR",
5266        ] {
5267            if std::env::var_os(deployment_only).is_some()
5268                && !wiring.claimed_env.contains(&deployment_only)
5269            {
5270                eprintln!(
5271                    "[server] FATAL: {deployment_only} is a deployment-binary surface; this \
5272                     build ships no accounting/admin/capture. Wire a Metering implementation \
5273                     through ServerWiring and claim the vars it consumes."
5274                );
5275                std::process::exit(1);
5276            }
5277        }
5278        match factory {
5279            None => None,
5280            Some(factory) => {
5281                let model_ids: Vec<String> =
5282                    models.iter().map(|(name, _, _)| name.clone()).collect();
5283                match factory(&metering::MeteringInit { models: &model_ids }) {
5284                    Ok(metering_obj) => metering_obj,
5285                    Err(err) => {
5286                        eprintln!("[server] FATAL: metering wiring: {err}");
5287                        std::process::exit(1);
5288                    }
5289                }
5290            }
5291        }
5292    };
5293    let budget_tokenizers = if metering_obj
5294        .as_ref()
5295        .is_some_and(|manager| manager.enforces_limits())
5296    {
5297        match load_budget_tokenizers(&models) {
5298            Ok(tokenizers) => Some(tokenizers),
5299            Err(err) => {
5300                eprintln!("[server] FATAL: prepaid reservation tokenizers: {err}");
5301                std::process::exit(1);
5302            }
5303        }
5304    } else {
5305        None
5306    };
5307    eprintln!("[server] starting; models config = {models:?}");
5308
5309    // Inference-liveness state (G5). Created BEFORE the worker so the whole weight load is
5310    // observable as PHASE_LOADING rather than as a gap: /livez and /readyz answer honestly
5311    // from the first accepted connection, which is what a supervisor's Type=notify +
5312    // WatchdogSec contract and a load balancer's readiness probe both need.
5313    let health_state = health::WorkerHealth::new();
5314    // GPU-fault watchers (G24) start before the load too: an Xid that fires DURING a 120 s
5315    // weight load is exactly the case a post-load watcher misses. spawn_gpu_watch owns the
5316    // Xid tail as well (one call, two threads).
5317    health::spawn_gpu_watch(health_state.clone());
5318    health::spawn_sd_watchdog(health_state.clone());
5319
5320    // Spawn the GPU worker thread and block until every model is loaded (or it fails).
5321    let (cmd_tx, model_names, caps, metrics, worker_thread) =
5322        match worker::spawn(models, health_state.clone()) {
5323            Ok(v) => v,
5324            Err(err) => {
5325                eprintln!("[server] FATAL: worker init failed: {err}");
5326                health_state.mark_dead(format!("worker init failed: {err}"));
5327                health::sd_notify(&format!("STATUS=worker init failed: {err}"));
5328                std::process::exit(1);
5329            }
5330        };
5331    eprintln!("[server] worker ready; serving models: {model_names:?}");
5332
5333    // Deployment hook: the worker is live, hand over the runtime handles — INCLUDING
5334    // the drain shutdown signal. The TrimHandle wraps a worker command sender, and the
5335    // worker's exit condition is "all senders dropped": a deployment surface that
5336    // holds its handle past the shutdown signal recreates the v0.116.0 38-minute
5337    // worker-join hang (the billing parity battery caught exactly that on the first
5338    // deployment-binary arm, 2026-08-29).
5339    let (drain_shutdown_tx, drain_shutdown_rx) = tokio::sync::watch::channel(false);
5340    if let Some(on_ready) = wiring.on_ready {
5341        on_ready(RuntimeHandles {
5342            trim: TrimHandle {
5343                cmd_tx: cmd_tx.clone(),
5344            },
5345            purge: PurgeHandle {
5346                cmd_tx: cmd_tx.clone(),
5347            },
5348            kv_handoff: HostHandoffHandle {
5349                cmd_tx: cmd_tx.clone(),
5350            },
5351            shutdown: drain_shutdown_rx.clone(),
5352        });
5353    }
5354
5355    // Dead-darklane background job runner (MEMRA_BG_JOB; lane/darklane-training): armed
5356    // only after the worker is ready — a weight load is PHASE_LOADING, never a valley.
5357    let bg_handle = darklane::spawn_from_env(health_state.clone());
5358    let bg_state = bg_handle.as_ref().map(|h| {
5359        let mode = darklane::BgConfig::from_env()
5360            .map(|c| c.yield_mode.as_str())
5361            .unwrap_or("stop");
5362        (h.state.clone(), mode)
5363    });
5364
5365    let state = AppState {
5366        cmd_tx,
5367        models: model_names,
5368        caps,
5369        openrouter_metadata: Arc::new(openrouter_metadata),
5370        provider_metadata: Arc::new(provider_metadata),
5371        metering: metering_obj,
5372        budget_tokenizers,
5373        api_auth,
5374        metrics_auth,
5375        metrics,
5376        inflight: Arc::new(Default::default()),
5377        tenant_inflight: Arc::new(Default::default()),
5378        health: health_state.clone(),
5379        bg: bg_state,
5380    };
5381    let inflight_handle = state.inflight.clone();
5382    // For the drain-kill fault-attribution latch: the drain future outlives the
5383    // router that consumes `state`.
5384    let drain_metering = state.metering.clone();
5385    // LOAD-GUARD DEMAND SEAM (lane/sampled-restore-load-guard). The worker cannot see a request
5386    // that has passed this boundary but not yet reached its channel — which is exactly the head
5387    // of an arriving fan-out, the one row a tick-top reading of `active + queue` cannot refuse.
5388    // Registering the gauge (not a copy of it) keeps one source of truth.
5389    worker::register_http_inflight(state.inflight.clone());
5390    let app = Router::new()
5391        // /health is the historical name (every memra script polls it) and stays the
5392        // LIVENESS probe; /livez + /readyz are the k8s-doctrine split (healthz deprecated
5393        // upstream at v1.16). Readiness ≠ liveness: draining or a not-yet-loaded model
5394        // takes the box out of ROTATION without asking a supervisor to kill it.
5395        .route("/health", get(health_live))
5396        .route("/livez", get(health_live))
5397        .route("/readyz", get(health_ready))
5398        .route("/models", get(list_models))
5399        .route("/v1/models", get(list_models_v1))
5400        .route("/v1/auth/check", get(auth_check))
5401        .route("/v1/completions", post(completions_admitted))
5402        .route("/v1/embeddings", post(embed_api::embeddings_admitted))
5403        .route("/v1/rerank", post(embed_api::rerank_admitted))
5404        .route("/v1/chat/completions", post(chat_completions_admitted))
5405        // Translation surfaces (lane/api-surfaces): Anthropic Messages + OpenAI
5406        // Responses over the same core. Axum matches the PATH only, so the
5407        // `?beta=true` query some clients append arrives here too.
5408        .route("/v1/messages", post(anthropic::messages_admitted))
5409        .route("/v1/responses", post(responses_api::responses_admitted))
5410        .route("/metrics", get(get_metrics))
5411        .route("/yield/metrics", get(yield_metrics))
5412        .with_state(state.clone());
5413    // Body-size policy (hermes finding): explicit ceiling sized to the advertised
5414    // 262k-token + vision surface, with 413s reshaped to the standard error object.
5415    let app = apply_body_limit(app);
5416    // Header-only auth runs outside the body-limit/extractor stack. Invalid callers therefore
5417    // cannot spend the 192 MiB parser budget, while valid callers retain the advertised 413.
5418    let app = app.layer(middleware::from_fn_with_state(
5419        state,
5420        authenticate_inference_before_body,
5421    ));
5422    let app = if ttft::enabled() {
5423        app.layer(middleware::from_fn(ttft_request_start))
5424    } else {
5425        app
5426    };
5427
5428    let listener = tokio::net::TcpListener::bind(bind_addr).await?;
5429    eprintln!("[server] listening on http://{bind_addr}");
5430    drop(drain_shutdown_rx);
5431    // READY=1 only AFTER the models are resident and the socket is bound — the whole point of
5432    // Type=notify is that "started" means "can serve". A no-op when NOTIFY_SOCKET is unset
5433    // (i.e. every non-systemd run), so it costs nothing outside a unit.
5434    health::sd_notify("READY=1\nSTATUS=serving");
5435    // GRACEFUL DRAIN (gap-scan F11): SIGTERM flips the drain flag (new completion
5436    // requests 503 immediately; /health reports "draining"), then the shutdown future
5437    // resolves once every in-flight request finished (the HTTP-layer gauge — streams
5438    // hold their slot until fully written) or the MEMRA_DRAIN_S deadline (default 30s)
5439    // passed. axum's graceful shutdown stops accepting, lets tracked connections finish
5440    // their current response, and returns — exit 0 (in-flight loss only past deadline).
5441    let inflight = inflight_handle;
5442    let signal_admin_shutdown = drain_shutdown_tx.clone();
5443    let serve_result = serve_bounded_http(listener, app, async move {
5444        let mut sigterm =
5445            match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {
5446                Ok(s) => s,
5447                Err(err) => {
5448                    eprintln!("[server] WARN: no SIGTERM handler ({err}); drain disabled");
5449                    std::future::pending::<()>().await;
5450                    unreachable!()
5451                }
5452            };
5453        sigterm.recv().await;
5454        DRAINING.store(true, std::sync::atomic::Ordering::SeqCst);
5455        let _ = signal_admin_shutdown.send(true);
5456        // STOPPING=1 + EXTEND_TIMEOUT_USEC: tell systemd the stop is deliberate and how
5457        // long the drain may legitimately take, so TimeoutStopSec does not SIGKILL a
5458        // healthy drain mid-stream (audit's systemd section).
5459        health::sd_notify(&format!(
5460            "STOPPING=1\nSTATUS=draining\nEXTEND_TIMEOUT_USEC={}",
5461            (drain_deadline_s() + 5) * 1_000_000
5462        ));
5463        let n: usize = inflight
5464            .iter()
5465            .map(|c| c.load(std::sync::atomic::Ordering::SeqCst))
5466            .sum();
5467        eprintln!(
5468            "[server] SIGTERM: draining ({n} in flight, deadline {}s)",
5469            drain_deadline_s()
5470        );
5471        let deadline = std::time::Duration::from_secs(drain_deadline_s());
5472        let t0 = std::time::Instant::now();
5473        loop {
5474            let n: usize = inflight
5475                .iter()
5476                .map(|c| c.load(std::sync::atomic::Ordering::SeqCst))
5477                .sum();
5478            if n == 0 {
5479                eprintln!(
5480                    "[server] drain complete in {:.1}s; exiting",
5481                    t0.elapsed().as_secs_f64()
5482                );
5483                break;
5484            }
5485            if t0.elapsed() >= deadline {
5486                eprintln!(
5487                    "[server] drain deadline ({}s) hit with {n} in flight; exiting",
5488                    drain_deadline_s()
5489                );
5490                // Fault attribution (owner ruling 2026-08-23): everything still in
5491                // flight past this point is killed by OUR shutdown. Latch the
5492                // classification so their receipts settle `drain_killed` (debit
5493                // ZERO) instead of `abandoned` (partial-billed client walk-away).
5494                // Through the seam: a custom implementation that never heard this
5495                // would partial-bill every drain-killed request.
5496                if let Some(metering) = drain_metering.as_ref() {
5497                    metering.drain_kill();
5498                }
5499                break;
5500            }
5501            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
5502        }
5503    })
5504    .await;
5505    // Drain complete: tell every deployment-side surface to end and drop its
5506    // TrimHandle (see the worker-join note below).
5507    let _ = drain_shutdown_tx.send(true);
5508    serve_result?;
5509    // Background job cleanup on the graceful path: SIGCONT+SIGTERM(+KILL past grace) the
5510    // job's process group — a SIGSTOPped orphan would stay frozen forever. The ungraceful
5511    // path (server SIGKILL) is covered by PDEATHSIG on the child.
5512    if let Some(h) = bg_handle {
5513        h.shutdown();
5514    }
5515    // The Router owned the last command sender in the stock build; a deployment
5516    // surface's TrimHandle clone must die on the drain signal above, or the worker's
5517    // "all senders dropped" exit condition never fires and the join below hangs
5518    // forever on graceful SIGTERM (v0.116.0 admin_cmd_tx incident; re-caught by the
5519    // billing parity battery 2026-08-29). Once serve returns it is gone, so the GPU
5520    // worker retires any sessions that finished concurrently with the HTTP drain. Keep main
5521    // alive until that cleanup completes: returning first lets CUDA deinitialize underneath a
5522    // pending-token flush (observed with paired speculative sessions on graceful SIGTERM).
5523    worker_thread.join().map_err(|_| {
5524        std::io::Error::other("GPU worker thread panicked during graceful shutdown")
5525    })?;
5526    eprintln!("[server] GPU worker shutdown complete");
5527    Ok(())
5528}
5529
5530/// Validate a resolved model-plan path BEFORE the worker thread spins up: a FILE loads as
5531/// GGUF; a DIRECTORY must be an HF safetensors checkpoint (`config.json` +
5532/// `model.safetensors` or `model.safetensors.index.json` — the run-safetensors load path)
5533/// or a memra repack dir (`manifest.json`). A clear error at parse time beats a worker
5534/// load failure after the Engine is already up.
5535fn validate_model_path(path: &str) -> Result<(), String> {
5536    let p = std::path::Path::new(path);
5537    if !p.exists() {
5538        return Err(format!("model path {path:?} does not exist"));
5539    }
5540    if p.is_file() {
5541        return Ok(()); // GGUF file (the worker's file branch)
5542    }
5543    if p.join("manifest.json").exists() {
5544        return Ok(()); // memra repack/overlay dir
5545    }
5546    let has_st =
5547        p.join("model.safetensors").exists() || p.join("model.safetensors.index.json").exists();
5548    if !has_st {
5549        return Err(format!(
5550            "model dir {path:?} is not a servable checkpoint: want model.safetensors or \
5551             model.safetensors.index.json + config.json (HF safetensors dir), or \
5552             manifest.json (memra repack dir)"
5553        ));
5554    }
5555    if !p.join("config.json").exists() {
5556        return Err(format!(
5557            "model dir {path:?} has safetensors weights but no config.json"
5558        ));
5559    }
5560    Ok(())
5561}
5562
5563/// MEMRA_MODELS="name=/path.gguf[+/draft.gguf],name2=hf:owner/repo,name3=/hf_ckpt_dir".
5564/// Falls back to the BASE-4 test pair. `+<draft.gguf>` after a model path attaches that
5565/// model's regime draft (docs/DRAFT-REGIME.md) — per model, not the global MEMRA_MTP_DRAFT
5566/// env, so a multi-model server gives each model its own draft. Both parts accept hf: specs.
5567/// A model path may also be an HF safetensors checkpoint DIRECTORY (serve-st lane,
5568/// 2026-08-04) — validated by `validate_model_path`, loaded through the same
5569/// SafetensorsSource seam as run-safetensors/run-gen.
5570fn parse_models_config() -> Vec<(String, String, Option<String>)> {
5571    if let Ok(spec) = std::env::var("MEMRA_MODELS") {
5572        let mut out = Vec::new();
5573        for entry in spec.split(',').filter(|s| !s.trim().is_empty()) {
5574            if let Some((name, path)) = entry.split_once('=') {
5575                // Paths accept hf:owner/repo[:file] specs — resolved (downloaded on first
5576                // use) before the worker sees them.
5577                let (mpath, dpath) = match path.trim().split_once('+') {
5578                    Some((m, d)) => (m.trim(), Some(d.trim())),
5579                    None => (path.trim(), None),
5580                };
5581                let resolve = |p: &str| {
5582                    memra_gguf::hf::resolve_arg(p).unwrap_or_else(|err| {
5583                        eprintln!("[server] FATAL: model {name:?}: {err}");
5584                        std::process::exit(1);
5585                    })
5586                };
5587                let mpath = resolve(mpath);
5588                if let Err(err) = validate_model_path(&mpath) {
5589                    eprintln!("[server] FATAL: model {name:?}: {err}");
5590                    std::process::exit(1);
5591                }
5592                // The DRAFT path gets the same parse-time existence check as the model path
5593                // (lane/step-draft, 2026-08-07). It did not, and the asymmetry cost a class of
5594                // late failure: a typo'd or unmounted drafter path survived parse, survived the
5595                // hf resolve, and only failed after the worker had already spent the whole
5596                // trunk load on the GPU — so on a busy card the operator got
5597                // `CUDA_ERROR_OUT_OF_MEMORY` on the TRUNK and never learned the drafter path
5598                // was wrong at all. Found by this lane's own gate arm D. A drafter must be a
5599                // FILE: `load_draft` opens it as a GGUF, so the dir forms `validate_model_path`
5600                // admits are not valid here.
5601                let dpath = dpath.map(|d| {
5602                    let d = resolve(d);
5603                    let p = std::path::Path::new(&d);
5604                    if !p.exists() {
5605                        eprintln!(
5606                            "[server] FATAL: model {name:?}: drafter path {d:?} does not \
5607                                   exist (MEMRA_MODELS '+draft' attach). Refusing to start \
5608                                   rather than serving plain decode under a config that asked \
5609                                   for speculative decoding."
5610                        );
5611                        std::process::exit(1);
5612                    }
5613                    if !p.is_file() {
5614                        eprintln!(
5615                            "[server] FATAL: model {name:?}: drafter path {d:?} is not a \
5616                                   file — a '+draft' attach must be a NextN/MTP GGUF file."
5617                        );
5618                        std::process::exit(1);
5619                    }
5620                    d
5621                });
5622                out.push((name.trim().to_string(), mpath, dpath));
5623            } else {
5624                eprintln!(
5625                    "[server] WARN: bad MEMRA_MODELS entry {entry:?} (want name=/path[+/draft]); skipping"
5626                );
5627            }
5628        }
5629        if !out.is_empty() {
5630            return out;
5631        }
5632    }
5633    // Default: the BASE-4 test pair (main=27B, judge=9B).
5634    vec![
5635        (
5636            "main".into(),
5637            "/data/ai-ml/hf-models/qwen36-27b-nvfp4-mtp/Qwen3.6-27B-NVFP4-Q4_K_M-mtp.gguf".into(),
5638            None,
5639        ),
5640        (
5641            "judge".into(),
5642            "/data/ai-ml/hf-models/qwen35-9b-nvfp4-gguf/Qwen3.5-9B-NVFP4-MTP-GGUF.gguf".into(),
5643            None,
5644        ),
5645    ]
5646}
5647
5648fn load_budget_tokenizers(
5649    models: &[(String, String, Option<String>)],
5650) -> Result<Arc<HashMap<String, Arc<Tokenizer>>>, String> {
5651    let mut tokenizers = HashMap::new();
5652    for (alias, path, _) in models {
5653        let path = std::path::Path::new(path);
5654        let tokenizer = if path.is_dir() {
5655            let tokenizer_dir = if path.join("manifest.json").exists() {
5656                let repack = memra_gguf::source::Hy3RepackSource::open(path).map_err(|err| {
5657                    format!("model {alias:?}: open repack tokenizer source: {err}")
5658                })?;
5659                repack
5660                    .source_dir()
5661                    .filter(|source| source.join("tokenizer.json").exists())
5662                    .unwrap_or(path)
5663                    .to_path_buf()
5664            } else {
5665                path.to_path_buf()
5666            };
5667            Tokenizer::from_hf_dir(&tokenizer_dir)
5668                .map_err(|err| format!("model {alias:?}: reservation tokenizer: {err}"))?
5669        } else {
5670            let gguf = memra_gguf::GgufFile::open(path)
5671                .map_err(|err| format!("model {alias:?}: open reservation tokenizer: {err}"))?;
5672            Tokenizer::from_gguf(&gguf)
5673                .map_err(|err| format!("model {alias:?}: reservation tokenizer: {err}"))?
5674        };
5675        tokenizers.insert(alias.clone(), Arc::new(tokenizer));
5676    }
5677    Ok(Arc::new(tokenizers))
5678}
5679
5680/// Shared body for both probes: the honest state, plus the numbers that explain it.
5681fn health_payload(st: &AppState, status: &str, detail: Option<&str>) -> serde_json::Value {
5682    let s = st.health.snapshot();
5683    let mut v = json!({
5684        "status": status,
5685        "models": *st.models,
5686        "worker": {
5687            "phase": health::phase_name(s.phase),
5688            "beat_age_ms": s.beat_age_ms,
5689            "tick_max_ms": s.tick_max_ms,
5690            "stall_threshold_ms": s.stall_threshold_ms,
5691            "generation": s.generation,
5692            "xid_warnings": s.xid_warns,
5693        },
5694    });
5695    if let Some(d) = detail {
5696        v["detail"] = json!(d);
5697    }
5698    v
5699}
5700
5701/// `/readyz` adds peer-integrity coverage as an advisory. Even `degraded` stays HTTP 200 while
5702/// the worker is otherwise ready: new speculative sessions are held on the safe plain path, so
5703/// draining all traffic would discard usable plain capacity instead of helping self-recovery.
5704fn readiness_payload(st: &AppState, status: &str, detail: Option<&str>) -> serde_json::Value {
5705    let mut v = health_payload(st, status, detail);
5706    v["peer_probe_integrity"] = json!(st.health.peer_probe_integrity().detail());
5707    v
5708}
5709
5710/// Header-only credential preflight for the edge router. It deliberately has no
5711/// body extractor: a router can prove a bearer is known before deciding whether
5712/// to buffer a large model-selection request.
5713async fn auth_check() -> impl IntoResponse {
5714    StatusCode::NO_CONTENT
5715}
5716
5717/// LIVENESS (`/health`, `/livez`) — INFERENCE liveness, not process liveness (G5).
5718///
5719/// WHAT CHANGED AND WHY. The old handler returned 200 whenever the HTTP task was scheduled:
5720/// a panicked GPU worker, a wedged GPU, a poisoned CUDA context — all reported "ok" forever,
5721/// on a box that answered nothing. Now the answer is derived ONLY from worker state: a
5722/// heartbeat the scheduler loop stamps every iteration, the panic/GPU fault latches, and the
5723/// load phase.
5724///
5725/// 503 (dead / GPU-faulted / stalled / still loading) is deliberately a
5726/// SUPERVISOR-ACTIONABLE signal — the only recovery for a sticky CUDA fault is restarting the
5727/// process, so this endpoint is what makes `Restart=on-failure` + a liveness probe work.
5728///
5729/// DRAINING stays **200**: a drain is a healthy, deliberate shutdown, and answering 503 here
5730/// would invite a supervisor to kill the process in the middle of finishing in-flight
5731/// streams. Rotation is `/readyz`'s job — that is the whole reason the two are separate.
5732async fn health_live(State(st): State<AppState>) -> impl IntoResponse {
5733    if draining() {
5734        // "draining" = the LB/orchestrator not-ready signal (gap-scan F11): the process is
5735        // finishing in-flight work and will exit; route new traffic elsewhere.
5736        return (StatusCode::OK, Json(health_payload(&st, "draining", None))).into_response();
5737    }
5738    match st.health.live() {
5739        Ok(()) => (StatusCode::OK, Json(health_payload(&st, "ok", None))).into_response(),
5740        Err(why) => retry_contract_response(
5741            (
5742                StatusCode::SERVICE_UNAVAILABLE,
5743                Json(health_payload(&st, "unhealthy", Some(&why))),
5744            )
5745                .into_response(),
5746            Some(worker::WORKER_RESPAWN_BACKOFF_BASE_S),
5747        ),
5748    }
5749}
5750
5751/// READINESS (`/readyz`) — "should this instance receive traffic right now?"
5752///
5753/// Ready = model loaded AND worker alive AND not draining. Unready is NOT a request for a
5754/// restart: draining and still-loading are both perfectly healthy states that simply must not
5755/// be routed to. k8s doctrine (`/livez` + `/readyz`; `healthz` deprecated at v1.16), and ahead
5756/// of both vLLM (no readiness endpoint) and TGI (single `/health`).
5757///
5758/// Queue pressure deliberately does NOT flip readiness: memra's interactive lane queues FIFO
5759/// and never sheds, so a deep queue is work in progress, not unreadiness. Capacity backpressure
5760/// belongs on the request path as 429/503 (G6), where a client can act on it.
5761async fn health_ready(State(st): State<AppState>) -> impl IntoResponse {
5762    let is_draining = draining();
5763    match st.health.ready(is_draining) {
5764        Ok(()) => (StatusCode::OK, Json(readiness_payload(&st, "ready", None))).into_response(),
5765        Err(why) => retry_contract_response(
5766            (
5767                StatusCode::SERVICE_UNAVAILABLE,
5768                Json(readiness_payload(&st, "not_ready", Some(&why))),
5769            )
5770                .into_response(),
5771            Some(if is_draining {
5772                drain_deadline_s()
5773            } else {
5774                worker::WORKER_RESPAWN_BACKOFF_BASE_S
5775            }),
5776        ),
5777    }
5778}
5779
5780#[derive(Clone, Copy)]
5781struct DualPpMetricsSnapshot {
5782    stage_ns: [u64; 4],
5783    stage_samples: [usize; 4],
5784    dropped_timing_samples: usize,
5785    overlaps: usize,
5786    slot_pairs: usize,
5787    slot_uses: [usize; 2],
5788    slot_collisions: usize,
5789}
5790
5791impl DualPpMetricsSnapshot {
5792    fn current() -> Self {
5793        let (stage_ns, stage_samples) = memra_engine::pp::dual_pp_timing_snapshot();
5794        let (slot_pairs, slot_uses, slot_collisions) = memra_engine::pp::dual_pp_slot_snapshot();
5795        Self {
5796            stage_ns,
5797            stage_samples,
5798            dropped_timing_samples: memra_engine::pp::dual_pp_timing_dropped(),
5799            overlaps: memra_engine::pp::dual_pp_overlaps(),
5800            slot_pairs,
5801            slot_uses,
5802            slot_collisions,
5803        }
5804    }
5805
5806    fn populated(self) -> bool {
5807        self.stage_samples.iter().any(|&n| n > 0)
5808            || self.dropped_timing_samples > 0
5809            || self.slot_pairs > 0
5810            || self.slot_collisions > 0
5811    }
5812}
5813
5814fn insert_dual_pp_metrics(
5815    body: &mut serde_json::Value,
5816    metrics_scope: &MetricsScope,
5817    snapshot: impl FnOnce() -> DualPpMetricsSnapshot,
5818) {
5819    // Dual wave/slot counts reveal live capacity and the two-device topology. Completion
5820    // credentials never evaluate the snapshot closure, even when the process is dual-active.
5821    if !metrics_scope.operator() {
5822        return;
5823    }
5824    let snapshot = snapshot();
5825    if !snapshot.populated() {
5826        return;
5827    }
5828    let timings: serde_json::Map<String, serde_json::Value> = memra_engine::pp::DUAL_PP_STAGE_NAMES
5829        .iter()
5830        .enumerate()
5831        .map(|(i, name)| {
5832            let total_ms = snapshot.stage_ns[i] as f64 / 1_000_000.0;
5833            (
5834                name.to_string(),
5835                json!({
5836                    "samples": snapshot.stage_samples[i],
5837                    "total_ms": total_ms,
5838                    "mean_ms": if snapshot.stage_samples[i] > 0 {
5839                        total_ms / snapshot.stage_samples[i] as f64
5840                    } else { 0.0 },
5841                }),
5842            )
5843        })
5844        .collect();
5845    body["dual_pp"] = json!({
5846        "overlaps": snapshot.overlaps,
5847        "slot_pairs": snapshot.slot_pairs,
5848        "slot_uses": snapshot.slot_uses,
5849        "slot_collisions": snapshot.slot_collisions,
5850        "cuda_event_spans": timings,
5851        "dropped_timing_samples": snapshot.dropped_timing_samples,
5852    });
5853}
5854
5855#[derive(Clone, Copy)]
5856struct PpWaveMetricsSnapshot {
5857    ticks: usize,
5858    cells: usize,
5859    overlaps: usize,
5860}
5861
5862impl PpWaveMetricsSnapshot {
5863    fn current() -> Self {
5864        let (ticks, cells, overlaps) = memra_engine::pp::pp_wave_snapshot();
5865        Self {
5866            ticks,
5867            cells,
5868            overlaps,
5869        }
5870    }
5871}
5872
5873fn insert_pp_wave_metrics(
5874    body: &mut serde_json::Value,
5875    metrics_scope: &MetricsScope,
5876    snapshot: impl FnOnce() -> PpWaveMetricsSnapshot,
5877) {
5878    if !metrics_scope.operator() {
5879        return;
5880    }
5881    let snapshot = snapshot();
5882    if snapshot.ticks == 0 && snapshot.cells == 0 {
5883        return;
5884    }
5885    body["pp_wave"] = json!({
5886        "ticks": snapshot.ticks,
5887        "cells": snapshot.cells,
5888        "overlaps": snapshot.overlaps,
5889    });
5890}
5891
5892fn insert_spec_acceptance_metrics(
5893    body: &mut serde_json::Value,
5894    metrics_scope: &MetricsScope,
5895    snapshot: impl FnOnce() -> HashMap<String, memra_engine::spec::SpecTelemetry>,
5896) {
5897    // Acceptance shape is process-wide model telemetry. As with dual_pp, tenant credentials
5898    // return before evaluating the snapshot closure so they cannot observe other workloads.
5899    if !metrics_scope.operator() {
5900        return;
5901    }
5902    let snapshot = snapshot();
5903    if snapshot.is_empty() {
5904        return;
5905    }
5906
5907    let mut tau = serde_json::Map::new();
5908    let mut by_position = serde_json::Map::new();
5909    for (model, telemetry) in snapshot {
5910        if telemetry.rounds == 0 {
5911            continue;
5912        }
5913        let n_pos = telemetry
5914            .pos_drafted
5915            .iter()
5916            .rposition(|&n| n > 0)
5917            .map_or(0, |position| position + 1);
5918        tau.insert(model.clone(), json!(telemetry.tau()));
5919        by_position.insert(
5920            model,
5921            json!({
5922                "window_seconds": worker::SPEC_METRICS_WINDOW_S,
5923                "rounds": telemetry.rounds,
5924                "offered": telemetry.pos_drafted[..n_pos].to_vec(),
5925                "accepted": telemetry.pos_accepted[..n_pos].to_vec(),
5926                "accept_rate": (0..n_pos).map(|position| {
5927                    let offered = telemetry.pos_drafted[position];
5928                    if offered > 0 {
5929                        telemetry.pos_accepted[position] as f64 / offered as f64
5930                    } else {
5931                        0.0
5932                    }
5933                }).collect::<Vec<f64>>(),
5934            }),
5935        );
5936    }
5937    if !tau.is_empty() {
5938        body["spec_tau"] = serde_json::Value::Object(tau);
5939        body["spec_accept_by_position"] = serde_json::Value::Object(by_position);
5940    }
5941}
5942
5943fn insert_peer_probe_metrics(
5944    body: &mut serde_json::Value,
5945    metrics_scope: &MetricsScope,
5946    snapshot: impl FnOnce() -> memra_engine::pp::PeerProbeMetrics,
5947) {
5948    // Probe bypass/failure state and boundary traffic are process-wide safety telemetry.
5949    // Completion credentials must not learn cross-tenant traffic or device topology.
5950    if !metrics_scope.operator() {
5951        return;
5952    }
5953    let snapshot = snapshot();
5954    body["peer_probe_bypassed"] = json!(snapshot.bypassed);
5955    body["peer_probe_boundary_copies"] = json!(snapshot.boundary_copies);
5956    body["peer_probe_runtime_reprobes"] = json!(snapshot.runtime_probes);
5957    body["peer_probe_runtime_failures"] = json!(snapshot.runtime_failures);
5958    body["peer_probe_deferred_total"] = json!(snapshot.deferred_total);
5959    body["peer_probe_integrity_degraded"] = json!(snapshot.integrity_degraded);
5960    body["peer_probe_degraded_to_host_bounce"] = json!(snapshot.degraded_to_host_bounce);
5961}
5962
5963/// Flat serving counters + engine-truth step latency percentiles.
5964async fn get_metrics(State(st): State<AppState>, headers: HeaderMap) -> Response {
5965    let metrics_scope = match authorize_metrics(&st.api_auth, &st.metrics_auth, &headers) {
5966        Ok(scope) => scope,
5967        Err(response) => return response,
5968    };
5969    let m = st.metrics.lock().map(|m| m.clone()).unwrap_or_default();
5970    // These counters describe the whole process, not the authenticated tenant. Preserve them for
5971    // the legacy single-key completion domain, but fail closed when a multi-tenant keyring caller
5972    // has no explicit operator scrape token.
5973    let mut body = if metrics_scope.process_wide() {
5974        json!({
5975            "admitted": m.admitted,
5976            "completed": m.completed,
5977            "tokens_out": m.tokens_out,
5978            "step_p50_ms": m.step_p50_ms,
5979            "step_p99_ms": m.step_p99_ms,
5980            // worker-truth prompt caching split (cached = resumed from any KV cache tier).
5981            "prompt_tokens_in": m.prompt_tokens_in,
5982            "cached_tokens_in": m.cached_tokens_in,
5983            // computed = actually primed; the denominator of the revenue multiplier
5984            // (billed prompt tokens / computed prompt tokens — tools/cache_economics.py).
5985            "computed_tokens_in": m.prompt_tokens_in.saturating_sub(m.cached_tokens_in),
5986            // Whole-session cache and admission observability (lane/cx-cachespec): cumulative
5987            // counters locate a latency slope; gauges show whether retired state is accumulating.
5988            "admission_session_defers": m.admission_session_defers,
5989            "admission_vram_defers": m.admission_vram_defers,
5990            "step_oom_parks": m.step_oom_parks,
5991            "continuation_pool_hits": m.continuation_pool_hits,
5992            "continuation_pool_evictions": m.continuation_pool_evictions,
5993            "plain_affinity_rewinds": m.plain_affinity_rewinds,
5994            "served_dspark": m.served_dspark,
5995            "served_spec": m.served_spec,
5996            "served_plain": m.served_plain,
5997            "spec_pool_hits": m.spec_pool_hits,
5998            "spec_pool_misses": m.spec_pool_misses,
5999            "spec_pool_affinity_rewinds": m.spec_pool_affinity_rewinds,
6000            "spec_pool_evictions": m.spec_pool_evictions,
6001            // lane/session-resume-sampler-predicate-20260820: the production answer to "does real
6002            // multi-turn traffic change sampler mid-session". Subset of spec_pool_misses.
6003            "spec_pool_sampler_refusals": m.spec_pool_sampler_refusals,
6004        })
6005    } else {
6006        json!({})
6007    };
6008    // Global prefix shape/volume and current capacity/VRAM are operator-only surfaces. The legacy
6009    // single-key domain retains its cumulative counters, while keyring completion credentials get
6010    // only their permitted tenant rows, including that tenant's own cache-hit ratio.
6011    if metrics_scope.operator() {
6012        if let Some(budget_health) = st.metering.as_ref().and_then(|m| m.limits_health()) {
6013            body["budget_source_reload_failed"] = json!(budget_health.source_reload_failed);
6014            body["budget_source_reload_consecutive"] =
6015                json!(budget_health.source_reload_consecutive);
6016            body["budget_source_available"] = json!(budget_health.source_available);
6017        }
6018        // Token-weighted global hit ratio + full prefix-cache probe/churn counters.
6019        body["cache_hit_token_ratio"] = json!(if m.prompt_tokens_in > 0 {
6020            m.cached_tokens_in as f64 / m.prompt_tokens_in as f64
6021        } else {
6022            0.0
6023        });
6024        body["prefix_cache_hits"] = json!(m.prefix_hits);
6025        body["prefix_cache_misses"] = json!(m.prefix_misses);
6026        body["prefix_cache_inserts"] = json!(m.prefix_inserts);
6027        body["prefix_cache_evictions"] = json!(m.prefix_evictions);
6028        body["prefix_cache_skips_budget"] = json!(m.prefix_skips_budget);
6029        body["prefix_cache_skips_pinned"] = json!(m.prefix_skips_pinned);
6030        body["prefix_cache_hit_tokens"] = json!(m.prefix_hit_tokens);
6031        // Pinned-host spill tier behind the prefix cache (lane/kv-host-spill-20260830;
6032        // MEMRA_KV_HOST_MB, default 0 = off). *_ms are cumulative copy wall-time: the
6033        // tick-stall receipt for the pod battery.
6034        body["prefix_host_entries"] = json!(m.prefix_host_entries);
6035        body["prefix_host_bytes"] = json!(m.prefix_host_bytes);
6036        body["prefix_host_demotions"] = json!(m.prefix_host_demotions);
6037        body["prefix_host_promotions"] = json!(m.prefix_host_promotions);
6038        body["prefix_host_demote_ms"] = json!(m.prefix_host_demote_ms);
6039        body["prefix_host_promote_ms"] = json!(m.prefix_host_promote_ms);
6040        body["prefix_host_rejected_allocs"] = json!(m.prefix_host_rejected_allocs);
6041        body["prefix_host_purges"] = json!(m.prefix_host_purges);
6042        body["prefix_host_purged_entries"] = json!(m.prefix_host_purged_entries);
6043        body["prefix_host_purged_bytes"] = json!(m.prefix_host_purged_bytes);
6044        body["prefix_host_tenant_rejects"] = json!(m.prefix_host_tenant_rejects);
6045        // Agent-pause demotion (MEMRA_KV_PAUSE_DEMOTE, lane/kv-pause-demote-20260831):
6046        // pause_demotes is a subset of prefix_host_demotions; pause_cancels counts armed
6047        // candidates whose session returned before the timer (or left nothing demotable).
6048        body["prefix_host_pause_demotes"] = json!(m.prefix_host_pause_demotes);
6049        body["prefix_host_pause_cancels"] = json!(m.prefix_host_pause_cancels);
6050        body["prefix_host_handoff_exports"] = json!(m.prefix_host_handoff_exports);
6051        body["prefix_host_handoff_imported_entries"] =
6052            json!(m.prefix_host_handoff_imported_entries);
6053        body["prefix_host_handoff_imported_bytes"] = json!(m.prefix_host_handoff_imported_bytes);
6054        body["prefix_host_handoff_skips"] = json!(m.prefix_host_handoff_skips);
6055        // KV budget flex (MEMRA_KV_FLEX, lane/kv-flex-20260831, tiering spec Arc G):
6056        // borrowed_bytes = current device prefix-cache residency above its configured
6057        // floor; sheds/shed_ms = borrowed-slice reclaims and their CUMULATIVE wall-time
6058        // (ms per shed = shed_ms / sheds, the capture-arrival zero-tax receipt).
6059        body["kv_flex_borrowed_bytes"] = json!(m.kv_flex_borrowed_bytes);
6060        body["kv_flex_sheds"] = json!(m.kv_flex_sheds);
6061        body["kv_flex_shed_ms"] = json!(m.kv_flex_shed_ms);
6062        // One sample per prefix-cache probe: served length on a hit, best LCP on a miss.
6063        // `edges` are lower bounds; the last bucket is unbounded.
6064        body["lcp_histogram"] = json!({
6065            "edges": worker::LCP_HIST_EDGES.to_vec(),
6066            "counts": m.lcp_hist.to_vec(),
6067        });
6068        // Valley signal (lane/darklane-training): seconds the worker has been COMPLETELY idle
6069        // (no active sessions, no queued admissions, no pending HTTP handoffs) — worker truth
6070        // via health phase + beat age + the PENDING_ADMITS gauge, no new hot-path cost.
6071        let idle_s = darklane::ValleySignal::new(st.health.clone()).idle_seconds();
6072        body["prefix_cache_entries"] = json!(m.prefix_entries);
6073        body["prefix_cache_bytes"] = json!(m.prefix_bytes);
6074        body["active_sessions"] = json!(m.active_sessions);
6075        body["queued_requests"] = json!(m.queued_requests);
6076        // Predictive-admission book (D2 gap G2, lane/d2-engine-gaps-20260831): per-model
6077        // in-flight sessions and the sum of their engine admission charges. Operator
6078        // scope: per-model load shape is cross-tenant information.
6079        body["admission_inflight"] = json!(m.admission_inflight);
6080        body["admission_booked_bytes"] = json!(m.admission_booked_bytes);
6081        body["continuation_pool_entries"] = json!(m.continuation_pool_entries);
6082        body["spec_pool_entries"] = json!(m.spec_pool_entries);
6083        body["cuda_driver_free_bytes"] = json!(m.cuda_driver_free_bytes);
6084        body["cuda_pool_reserved_bytes"] = json!(m.cuda_pool_reserved_bytes);
6085        body["cuda_pool_used_bytes"] = json!(m.cuda_pool_used_bytes);
6086        body["cuda_pool_cached_bytes"] = json!(m.cuda_pool_cached_bytes);
6087        if !m.constraint_compiler_fail_closed.is_empty() {
6088            body["constraint_compiler_fail_closed"] = serde_json::Value::Object(
6089                m.constraint_compiler_fail_closed
6090                    .iter()
6091                    .map(|(model, gauge)| {
6092                        let value = u8::from(gauge.load(std::sync::atomic::Ordering::Acquire));
6093                        (model.clone(), json!(value))
6094                    })
6095                    .collect(),
6096            );
6097        }
6098        body["serve_idle_seconds"] = json!((idle_s * 1000.0).round() / 1000.0);
6099    }
6100    // Per-tenant prompt/cached breakdown (composes with PC-ISO tenancy): keyring
6101    // deployments key rows by tenant (`t:<tenant>`), no-keyring by raw cache_salt
6102    // ("" = the default namespace). ABSENT until the first admit, so a fresh server's
6103    // /metrics is otherwise unchanged. Bounded rows; overflow aggregates in "(other)".
6104    if !m.ns_tokens.is_empty() {
6105        let tenants: serde_json::Map<String, serde_json::Value> = m
6106            .ns_tokens
6107            .iter()
6108            .filter(|(ns, _)| metrics_scope.includes(ns))
6109            .map(|(ns, [p, c])| {
6110                (
6111                    ns.clone(),
6112                    json!({
6113                        "prompt_tokens_in": p,
6114                        "cached_tokens_in": c,
6115                        "cache_hit_token_ratio": if *p > 0 { *c as f64 / *p as f64 } else { 0.0 },
6116                    }),
6117                )
6118            })
6119            .collect();
6120        if !tenants.is_empty() {
6121            body["tenants"] = serde_json::Value::Object(tenants);
6122        }
6123    }
6124    let adsd_suspect_total: serde_json::Map<String, serde_json::Value> = m
6125        .adsd_suspect_total
6126        .iter()
6127        .filter(|(tenant, _)| metrics_scope.includes(tenant))
6128        .map(|(tenant, total)| (tenant.clone(), json!(total)))
6129        .collect();
6130    if !adsd_suspect_total.is_empty() {
6131        body["adsd_suspect_total"] = serde_json::Value::Object(adsd_suspect_total);
6132    }
6133    // Background-job state is operator-only and absent unless MEMRA_BG_JOB armed the runner.
6134    if metrics_scope.operator()
6135        && let Some((bg, mode)) = &st.bg
6136    {
6137        body["bg"] = bg.to_json(mode);
6138    }
6139    // Spec-decode acceptance telemetry (lane/accept-telemetry — the llama.cpp #26389 /
6140    // vLLM per-draft-position counter schema). Per model, cumulative since model load
6141    // (models load once per process — counters reset on restart, never mid-run). The
6142    // block is ABSENT until a spec burst runs: spec-off deployments see the exact
6143    // pre-lane payload. accept_rate_per_pos[j] = P(position j accepted | round offered
6144    // position j) — sane spec decode decays monotonically from pos 0.
6145    if metrics_scope.operator() {
6146        let spec: serde_json::Map<String, serde_json::Value> = m
6147            .spec
6148            .iter()
6149            .map(|(model, t)| {
6150                let n_pos = t
6151                    .pos_drafted
6152                    .iter()
6153                    .rposition(|&d| d > 0)
6154                    .map_or(0, |p| p + 1);
6155                (
6156                    model.clone(),
6157                    json!({
6158                        "rounds": t.rounds,
6159                        "drafted": t.drafted,
6160                        "accepted": t.accepted,
6161                        "acceptance_rate": if t.drafted > 0 {
6162                            t.accepted as f64 / t.drafted as f64 } else { 0.0 },
6163                        "tokens_per_round": if t.rounds > 0 {
6164                            (t.accepted + t.rounds) as f64 / t.rounds as f64 } else { 0.0 },
6165                        "pos_drafted": t.pos_drafted[..n_pos].to_vec(),
6166                        "pos_accepted": t.pos_accepted[..n_pos].to_vec(),
6167                        "accept_rate_per_pos": (0..n_pos).map(|j| if t.pos_drafted[j] > 0 {
6168                            t.pos_accepted[j] as f64 / t.pos_drafted[j] as f64 } else { 0.0 })
6169                            .collect::<Vec<f64>>(),
6170                    }),
6171                )
6172            })
6173            .collect();
6174        if !spec.is_empty() {
6175            body["spec"] = serde_json::Value::Object(spec);
6176        }
6177    }
6178    insert_spec_acceptance_metrics(&mut body, &metrics_scope, || m.spec_window.clone());
6179    insert_dual_pp_metrics(&mut body, &metrics_scope, DualPpMetricsSnapshot::current);
6180    insert_pp_wave_metrics(&mut body, &metrics_scope, PpWaveMetricsSnapshot::current);
6181    insert_peer_probe_metrics(
6182        &mut body,
6183        &metrics_scope,
6184        memra_engine::pp::peer_probe_metrics,
6185    );
6186    Json(body).into_response()
6187}
6188
6189#[derive(Debug, Default, Deserialize)]
6190struct ModelsQuery {
6191    #[serde(default)]
6192    schema: Option<String>,
6193}
6194
6195fn models_openai_body(models: &[String]) -> serde_json::Value {
6196    let data: Vec<_> = models
6197        .iter()
6198        .map(|m| json!({ "id": m, "object": "model" }))
6199        .collect();
6200    json!({ "object": "list", "data": data })
6201}
6202
6203/// The surface a model actually serves, defaulting to chat. All THREE catalog
6204/// feeds (`/v1/models`, `/models?schema=openrouter`, `/models?schema=openmodels`)
6205/// resolve it through here so they can never disagree about the same model — the
6206/// disagreement being exactly what a split fix would have created.
6207fn declared_surface(metadata: Option<&OpenRouterModelMetadata>) -> &'static str {
6208    match metadata.and_then(|m| m.surface.as_deref()) {
6209        Some("embedding") => "embedding",
6210        Some("rerank") => "rerank",
6211        _ => "chat",
6212    }
6213}
6214
6215fn openrouter_supported_parameters(
6216    caps: Option<&ModelCaps>,
6217    max_output_length: Option<u64>,
6218    is_chat: bool,
6219) -> serde_json::Value {
6220    let mut parameters = serde_json::Map::new();
6221    // EVERY parameter below is a completion-request field. /v1/embeddings takes
6222    // {input, dimensions, encoding_format} and /v1/rerank takes {query, documents,
6223    // top_n} — neither accepts sampling, stop, seed, max_tokens, json_mode or
6224    // structured_outputs. Publishing them off the chat surface would repeat, on this
6225    // feed, the contradiction this change exists to remove: /v1/models declaring
6226    // structured_output=false for an embedder while this feed advertises
6227    // structured_outputs as an accepted boolean for the same model.
6228    if !is_chat {
6229        return serde_json::Value::Object(parameters);
6230    }
6231    for name in [
6232        "temperature",
6233        "top_p",
6234        "min_p",
6235        "frequency_penalty",
6236        "presence_penalty",
6237        "repetition_penalty",
6238        "stop",
6239    ] {
6240        parameters.insert(name.into(), json!({ "type": "unknown" }));
6241    }
6242    parameters.insert("top_k".into(), json!({ "type": "integer", "min": 0 }));
6243    parameters.insert(
6244        "seed".into(),
6245        json!({ "type": "integer", "min": 0, "max": JSON_SAFE_INTEGER_MAX }),
6246    );
6247    let mut max_tokens = json!({ "type": "integer", "min": 1, "unit": "token" });
6248    if let Some(max) = max_output_length {
6249        max_tokens["max"] = json!(max);
6250    }
6251    parameters.insert("max_tokens".into(), max_tokens);
6252    // Constrained decoding is NOT universal, and this catalog used to say it was. The dsv4
6253    // route refuses `response_format` by name. A template whose `<think>` tail opens
6254    // unconditionally with no `enable_thinking` switch is refused ONLY when its think-close
6255    // token contract is unknown (`ModelCaps::think_close` empty — GLM-5.3-Flash): with a known
6256    // close sequence, POST-THINK constrained decoding serves it (think runs unconstrained, the
6257    // grammar engages at the close token — lane/step37-postthink-grammar). This predicate
6258    // mirrors the ACTUAL refusal in `build_chat_request`, not a template heuristic: v0.123.0
6259    // shipped the heuristic form and advertised `structured_output: false` for step37 while the
6260    // server was serving schema-valid `response_format` on it (found by the 2026-09-01 claim
6261    // re-seal; live-verified both ways). Same predicate as the contract-v2 row's
6262    // `structured_output`, so the two catalogs cannot disagree about one model. Off the chat
6263    // surface (embedders, rerankers) nothing chat-shaped is advertised at all.
6264    if is_chat
6265        && caps.is_some_and(|c| {
6266            !c.dsv4 && !(c.qwen_think && !c.think_switch && c.think_close.is_empty())
6267        })
6268    {
6269        parameters.insert("json_mode".into(), json!({ "type": "boolean" }));
6270        parameters.insert("structured_outputs".into(), json!({ "type": "boolean" }));
6271    }
6272    if is_chat && caps.is_some_and(|c| c.tools_branch) {
6273        parameters.insert("tools".into(), json!({ "type": "boolean" }));
6274        parameters.insert(
6275            "tool_choice".into(),
6276            json!({ "type": "enum", "values": ["auto", "none"] }),
6277        );
6278    }
6279    if is_chat && caps.is_some_and(|c| c.qwen_think || c.effort_levels || c.gemma_think) {
6280        parameters.insert("reasoning".into(), json!({ "type": "boolean" }));
6281    }
6282    serde_json::Value::Object(parameters)
6283}
6284
6285/// The context window a catalog row is allowed to CLAIM: the checkpoint's trained
6286/// `context_length` capped by the deployment's operational envelope
6287/// (`max_prompt_length + max_output_length`) when the metadata pins both.
6288///
6289/// The trained figure is a training fact, not a serving claim. Admission already refuses a
6290/// `max_ctx` beyond the pinned envelope (`apply_model_request_limits`: "a tiny request could
6291/// reserve the model's full trained context and bypass the production shape's VRAM admission
6292/// contract"), but until 2026-08-30 every catalog body still advertised the raw trained value —
6293/// so a deployment whose shape cannot serve that window published it anyway. The receipt that
6294/// forced this: GLM-5.3-Flash declares 1,048,576 trained, and the 3-card resident serving shape
6295/// cannot prime it — the 1M deep prime died `layer 31: DSA k-pool selection failed:
6296/// DriverError(CUDA_ERROR_OUT_OF_MEMORY)` at a 97,242 MiB per-card peak
6297/// (`research/glm5-prefix-latent-20260830/box-window/WINDOW-STATUS.md`). A row must never
6298/// advertise a window the deployment has not pinned as admissible; with no envelope pinned the
6299/// trained value stands (a bare dev boot is not a customer catalog).
6300fn published_context_length(
6301    caps: Option<&ModelCaps>,
6302    metadata: Option<&OpenRouterModelMetadata>,
6303) -> Option<u64> {
6304    let trained = caps
6305        .map(|c| c.context_length as u64)
6306        .filter(|&value| value > 0)?;
6307    let envelope = metadata.and_then(|m| {
6308        let prompt = m.max_prompt_length?;
6309        let output = m.max_output_length?;
6310        prompt.checked_add(output)
6311    });
6312    Some(envelope.map_or(trained, |envelope| trained.min(envelope)))
6313}
6314
6315fn model_entry_openrouter(
6316    name: &str,
6317    caps: Option<&ModelCaps>,
6318    metadata: Option<&OpenRouterModelMetadata>,
6319) -> serde_json::Value {
6320    let empty = OpenRouterModelMetadata::default();
6321    let metadata = metadata.unwrap_or(&empty);
6322    let context_length =
6323        published_context_length(caps, Some(metadata)).filter(|&v| v <= JSON_SAFE_INTEGER_MAX);
6324    let tokenizer = caps
6325        .map(|c| c.tokenizer.as_str())
6326        .filter(|tokenizer| !tokenizer.is_empty());
6327
6328    let mut input = serde_json::Map::new();
6329    input.insert("type".into(), json!("text"));
6330    let mut supported_inputs = serde_json::Map::new();
6331    if let Some(value) = context_length {
6332        supported_inputs.insert(
6333            "max_context_length".into(),
6334            json!({ "value": value, "unit": "token" }),
6335        );
6336    }
6337    if let Some(value) = metadata.max_prompt_length {
6338        supported_inputs.insert(
6339            "max_prompt_length".into(),
6340            json!({ "value": value, "unit": "token" }),
6341        );
6342    }
6343    if !supported_inputs.is_empty() {
6344        input.insert(
6345            "supported_inputs".into(),
6346            serde_json::Value::Object(supported_inputs),
6347        );
6348    }
6349    let mut input_pricing = Vec::new();
6350    for (kind, cost) in [
6351        ("prompt", metadata.pricing.prompt.as_deref()),
6352        ("cached_prompt", metadata.pricing.cached_prompt.as_deref()),
6353        ("cache_write", metadata.pricing.cache_write.as_deref()),
6354    ] {
6355        if let Some(cost) = cost {
6356            input_pricing.push(json!({
6357                "type": kind,
6358                "unit": "token",
6359                "cost_usd": cost,
6360            }));
6361        }
6362    }
6363    if !input_pricing.is_empty() {
6364        input.insert("pricing".into(), serde_json::Value::Array(input_pricing));
6365    }
6366    let mut input_capacity = Vec::new();
6367    for (kind, value) in [
6368        ("prompt", metadata.capacity.prompt_tpm),
6369        ("cached_prompt", metadata.capacity.cached_prompt_tpm),
6370    ] {
6371        if let Some(value) = value {
6372            input_capacity.push(json!({
6373                "type": kind,
6374                "unit": "token",
6375                "per": "minute",
6376                "value": value,
6377            }));
6378        }
6379    }
6380    if !input_capacity.is_empty() {
6381        input.insert("capacity".into(), serde_json::Value::Array(input_capacity));
6382    }
6383
6384    let or_surface = declared_surface(Some(metadata));
6385    let or_is_chat = or_surface == "chat";
6386    let mut output = serde_json::Map::new();
6387    // These strings come from the vendored Provider Monitor 2.4 schema this feed
6388    // stamps itself with — research/gateway-20260812/raw/sources/
6389    // openrouter-provider-schema-v2.4-20260812.json, `OutputModality`, a closed
6390    // oneOf whose branches enum `type` to text|image|video|speech|transcription|
6391    // embeddings|rerank|audio. They are NOT ours to choose: the wire enum is PLURAL
6392    // `embeddings` while the models.toml key is singular `embedding`, and there is no
6393    // `score` modality at all. A row matching no branch fails the whole document.
6394    output.insert(
6395        "type".into(),
6396        json!(match or_surface {
6397            "embedding" => "embeddings",
6398            "rerank" => "rerank",
6399            _ => "text",
6400        }),
6401    );
6402    output.insert(
6403        "supported_parameters".into(),
6404        openrouter_supported_parameters(caps, metadata.max_output_length, or_is_chat),
6405    );
6406    // The embeddings and rerank branches declare NO `streaming` property and are
6407    // additionalProperties:false, so the key must be ABSENT there — `false` is as
6408    // invalid as `true`. Chat keeps the byte-identical `true`.
6409    if or_is_chat {
6410        output.insert("streaming".into(), json!(true));
6411    }
6412    // Same rule as /v1/models' max_output_tokens: a surface that emits no completion
6413    // tokens advertises no ceiling, or a client reads it as a max_tokens to send.
6414    if let Some(value) = metadata.max_output_length
6415        && or_is_chat
6416    {
6417        output.insert(
6418            "max_length".into(),
6419            json!({ "value": value, "unit": "token" }),
6420        );
6421    }
6422    let mut output_pricing = Vec::new();
6423    for (kind, cost) in [
6424        ("completion", metadata.pricing.completion.as_deref()),
6425        (
6426            "internal_reasoning",
6427            metadata.pricing.internal_reasoning.as_deref(),
6428        ),
6429    ] {
6430        if let Some(cost) = cost {
6431            output_pricing.push(json!({
6432                "type": kind,
6433                "unit": "token",
6434                "cost_usd": cost,
6435            }));
6436        }
6437    }
6438    if !output_pricing.is_empty() {
6439        output.insert("pricing".into(), serde_json::Value::Array(output_pricing));
6440    }
6441    let mut output_capacity = Vec::new();
6442    if let Some(value) = metadata.capacity.completion_tpm {
6443        output_capacity.push(json!({
6444            "type": "completion",
6445            "unit": "token",
6446            "per": "minute",
6447            "value": value,
6448        }));
6449    }
6450    if let Some(value) = metadata.capacity.concurrency {
6451        output_capacity.push(json!({
6452            "type": "concurrency",
6453            "unit": "request",
6454            "value": value,
6455        }));
6456    }
6457    if !output_capacity.is_empty() {
6458        output.insert("capacity".into(), serde_json::Value::Array(output_capacity));
6459    }
6460
6461    let mut entry = serde_json::Map::new();
6462    entry.insert("schema_version".into(), json!(OPENROUTER_SCHEMA_VERSION));
6463    entry.insert("id".into(), json!(name));
6464    entry.insert("name".into(), json!(name));
6465    if let Some(value) = metadata.hugging_face_id.as_deref() {
6466        entry.insert("hugging_face_id".into(), json!(value));
6467    }
6468    if let Some(value) = metadata.created {
6469        entry.insert("created".into(), json!(value));
6470    }
6471    if let Some(value) = metadata.quantization.as_deref() {
6472        entry.insert("quantization".into(), json!(value));
6473    }
6474    if let Some(value) = tokenizer {
6475        entry.insert("tokenizer".into(), json!(value));
6476    }
6477    if let Some(value) = metadata.description.as_deref() {
6478        entry.insert("description".into(), json!(value));
6479    }
6480    let mut input_modalities = vec![serde_json::Value::Object(input)];
6481    for m in &metadata.input_modalities {
6482        let mut extra = serde_json::Map::new();
6483        extra.insert("type".into(), json!(m));
6484        if let Some(cost) = metadata.pricing.prompt.as_deref() {
6485            // image content bills as ordinary prompt tokens (the pad run IS the prompt)
6486            extra.insert(
6487                "pricing".into(),
6488                json!([{ "type": "prompt", "unit": "token", "cost_usd": cost }]),
6489            );
6490        }
6491        input_modalities.push(serde_json::Value::Object(extra));
6492    }
6493    entry.insert(
6494        "input_modalities".into(),
6495        serde_json::Value::Array(input_modalities),
6496    );
6497    entry.insert(
6498        "output_modalities".into(),
6499        serde_json::Value::Array(vec![serde_json::Value::Object(output)]),
6500    );
6501    if let Some(cost) = metadata.pricing.request.as_deref() {
6502        entry.insert(
6503            "pricing".into(),
6504            json!([{ "type": "request", "unit": "request", "cost_usd": cost }]),
6505        );
6506    }
6507    if let Some(value) = metadata.capacity.request_rpm {
6508        entry.insert(
6509            "capacity".into(),
6510            json!([{
6511                "type": "request",
6512                "unit": "request",
6513                "per": "minute",
6514                "value": value,
6515            }]),
6516        );
6517    }
6518    if let Some(value) = metadata.is_ready {
6519        entry.insert("is_ready".into(), json!(value));
6520    }
6521    if let Some(value) = metadata.is_free {
6522        entry.insert("is_free".into(), json!(value));
6523    }
6524    if let Some(value) = metadata.discount_to_user {
6525        entry.insert("discount_to_user".into(), json!(value));
6526    }
6527    if let Some(value) = metadata.openrouter_slug.as_deref() {
6528        entry.insert("openrouter".into(), json!({ "slug": value }));
6529    }
6530    if !metadata.datacenters.is_empty() {
6531        entry.insert("datacenters".into(), json!(metadata.datacenters));
6532    }
6533    let mut compliance = serde_json::Map::new();
6534    if let Some(value) = metadata.zdr {
6535        compliance.insert("zdr".into(), json!(value));
6536    }
6537    if let Some(value) = metadata.hipaa {
6538        compliance.insert("hipaa".into(), json!(value));
6539    }
6540    if !compliance.is_empty() {
6541        entry.insert("compliance".into(), serde_json::Value::Object(compliance));
6542    }
6543    serde_json::Value::Object(entry)
6544}
6545
6546fn models_openrouter_body(st: &AppState) -> serde_json::Value {
6547    let data: Vec<_> = st
6548        .models
6549        .iter()
6550        .map(|model| {
6551            model_entry_openrouter(model, st.caps.get(model), st.openrouter_metadata.get(model))
6552        })
6553        .collect();
6554    json!({ "data": data })
6555}
6556
6557fn model_entry_openmodels(
6558    name: &str,
6559    caps: Option<&ModelCaps>,
6560    metadata: Option<&OpenRouterModelMetadata>,
6561) -> Result<serde_json::Value, String> {
6562    let metadata = metadata.ok_or_else(|| {
6563        format!("OpenModels feed requires MEMRA_MODEL_METADATA for model {name:?}")
6564    })?;
6565    let context_length = published_context_length(caps, Some(metadata))
6566        .filter(|&value| value <= JSON_SAFE_INTEGER_MAX)
6567        .ok_or_else(|| format!("OpenModels feed requires context_length for model {name:?}"))?;
6568    let created = metadata
6569        .created
6570        .ok_or_else(|| format!("OpenModels feed requires created for model {name:?}"))?;
6571    let max_output_length = metadata
6572        .max_output_length
6573        .ok_or_else(|| format!("OpenModels feed requires max_output_length for model {name:?}"))?;
6574    let prompt = metadata
6575        .pricing
6576        .prompt
6577        .as_deref()
6578        .ok_or_else(|| format!("OpenModels feed requires pricing.prompt for model {name:?}"))?;
6579    let completion =
6580        metadata.pricing.completion.as_deref().ok_or_else(|| {
6581            format!("OpenModels feed requires pricing.completion for model {name:?}")
6582        })?;
6583    let input_cache_read = metadata.pricing.cached_prompt.as_deref().ok_or_else(|| {
6584        format!("OpenModels feed requires pricing.cached_prompt for model {name:?}")
6585    })?;
6586    let is_ready = metadata
6587        .is_ready
6588        .ok_or_else(|| format!("OpenModels feed requires is_ready for model {name:?}"))?;
6589    let is_free = metadata
6590        .is_free
6591        .ok_or_else(|| format!("OpenModels feed requires is_free for model {name:?}"))?;
6592    let discount_to_user = metadata
6593        .discount_to_user
6594        .ok_or_else(|| format!("OpenModels feed requires discount_to_user for model {name:?}"))?;
6595
6596    let mut pricing = serde_json::Map::new();
6597    pricing.insert("prompt".into(), json!(prompt));
6598    pricing.insert("completion".into(), json!(completion));
6599    pricing.insert("input_cache_read".into(), json!(input_cache_read));
6600    if let Some(value) = metadata.pricing.request.as_deref() {
6601        pricing.insert("request".into(), json!(value));
6602    }
6603
6604    let om_surface = declared_surface(Some(metadata));
6605    let om_is_chat = om_surface == "chat";
6606    let mut supported_features = Vec::new();
6607    if om_is_chat && caps.is_some_and(|c| c.tools_branch) {
6608        supported_features.push("tool_calling");
6609    }
6610    if om_is_chat && caps.is_some_and(|c| c.qwen_think || c.effort_levels || c.gemma_think) {
6611        supported_features.push("reasoning");
6612    }
6613
6614    let mut entry = serde_json::Map::new();
6615    entry.insert("id".into(), json!(name));
6616    entry.insert("name".into(), json!(name));
6617    entry.insert("created".into(), json!(created));
6618    entry.insert("input_modalities".into(), json!(["text"]));
6619    entry.insert(
6620        "output_modalities".into(),
6621        json!(match om_surface {
6622            "embedding" => ["embeddings"],
6623            "rerank" => ["rerank"],
6624            _ => ["text"],
6625        }),
6626    );
6627    entry.insert("context_length".into(), json!(context_length));
6628    entry.insert("max_output_length".into(), json!(max_output_length));
6629    // OpenModels' current snapshot importer defaults an omitted currency to CNY.
6630    // Declare the USD unit used by every pricing string so it cannot apply FX conversion.
6631    entry.insert("currency".into(), json!("USD"));
6632    entry.insert("pricing".into(), serde_json::Value::Object(pricing));
6633    entry.insert("supported_features".into(), json!(supported_features));
6634    entry.insert("is_ready".into(), json!(is_ready));
6635    entry.insert("is_free".into(), json!(is_free));
6636    entry.insert("discount_to_user".into(), json!(discount_to_user));
6637    Ok(serde_json::Value::Object(entry))
6638}
6639
6640fn models_openmodels_body(st: &AppState) -> Result<serde_json::Value, String> {
6641    let data: Result<Vec<_>, _> = st
6642        .models
6643        .iter()
6644        .map(|model| {
6645            model_entry_openmodels(model, st.caps.get(model), st.openrouter_metadata.get(model))
6646        })
6647        .collect();
6648    Ok(json!({ "data": data? }))
6649}
6650
6651async fn list_models(State(st): State<AppState>, Query(query): Query<ModelsQuery>) -> Response {
6652    match query.schema.as_deref() {
6653        None | Some("openai") => Json(models_openai_body(st.models.as_ref())).into_response(),
6654        Some("openrouter") => Json(models_openrouter_body(&st)).into_response(),
6655        Some("openmodels") => match models_openmodels_body(&st) {
6656            Ok(body) => Json(body).into_response(),
6657            Err(error) => bad_request(&error, Some("schema")),
6658        },
6659        Some(schema) => bad_request(
6660            &format!(
6661                "unsupported models schema {schema:?}; expected openai, openrouter, or openmodels"
6662            ),
6663            Some("schema"),
6664        ),
6665    }
6666}
6667
6668/// One /v1/models entry in EXACTLY the router-marketplace contract-v2 shape — no extra
6669/// keys ("Do not design a custom catalog or pricing format"; the checker rejects
6670/// unknown fields). The richer OpenRouter/OpenModels shapes stay on /models?schema=.
6671/// Values are worker truth from the loaded plan (ModelCaps probed at spawn) plus the
6672/// model's MEMRA_MODEL_METADATA entry — the same source the request ledger bills from,
6673/// so the advertised price can never drift from the charged one. Prices render as
6674/// per-1M-token decimal STRINGS via exact decimal shift; null when a rate does not apply.
6675fn model_entry_v1(
6676    name: &str,
6677    caps: Option<&ModelCaps>,
6678    metadata: Option<&OpenRouterModelMetadata>,
6679) -> serde_json::Value {
6680    let ctx = published_context_length(caps, metadata);
6681    // Same thinking-capability predicate as the OpenRouter catalog body: any of the
6682    // three template dialects (qwen think tail, level-consuming effort string, gemma
6683    // thought channel) means the model reasons and the reasoning knobs are live.
6684    let thinking = caps.is_some_and(|c| c.qwen_think || c.effort_levels || c.gemma_think || c.dsv4);
6685    // rung-3 model-row honesty: the dsv4 route refuses response_format by name and
6686    // serves no prefix cache (n_cached honestly 0) — its row must not claim either.
6687    let is_dsv4 = caps.is_some_and(|c| c.dsv4);
6688    let per_1m = |v: Option<&str>| match v.and_then(per_million_price) {
6689        Some(p) => json!(p),
6690        None => serde_json::Value::Null,
6691    };
6692    let owned_by = metadata
6693        .and_then(|m| m.owned_by.as_deref())
6694        .unwrap_or_else(|| name.split('/').next().unwrap_or(name));
6695    let mut input_modalities = vec!["text"];
6696    if let Some(meta) = metadata {
6697        input_modalities.extend(meta.input_modalities.iter().map(String::as_str));
6698    }
6699    let lifecycle = metadata.and_then(|m| m.lifecycle.as_ref());
6700    let reliability = metadata.and_then(|m| m.reliability.as_ref());
6701    // The row a client SDK reads to decide HOW to call this model. A non-chat model
6702    // advertised as chat sends the caller to the wrong endpoint with the wrong body,
6703    // so type/endpoints/output_modalities/capabilities all follow the declared surface
6704    // rather than a hardcoded chat literal (2026-08-28: qwen3-embedding-8b and
6705    // qwen3-reranker-8b were published as chat models with tools+streaming).
6706    let surface = declared_surface(metadata);
6707    let (model_type, endpoints, output_modalities) = match surface {
6708        // `type` mirrors the models.toml vocabulary (singular, like `surface`);
6709        // output modalities use the SAME wire enum the 2.4 schema pins, because
6710        // inventing a second vocabulary is what produced `score` in the first place.
6711        "embedding" => ("embedding", vec!["embeddings"], vec!["embeddings"]),
6712        "rerank" => ("rerank", vec!["rerank"], vec!["rerank"]),
6713        _ => ("chat", vec!["chat/completions"], vec!["text"]),
6714    };
6715    let is_chat = surface == "chat";
6716    json!({
6717        "id": name,
6718        "name": name,
6719        "object": "model",
6720        "owned_by": owned_by,
6721        "type": model_type,
6722        "context_length": ctx,
6723        // A non-chat surface emits no completion tokens; advertising an output ceiling
6724        // for it invites a max_tokens the endpoint will never honour.
6725        "max_output_tokens": if is_chat { metadata.and_then(|m| m.max_output_length) } else { None },
6726        "endpoints": endpoints,
6727        "input_modalities": input_modalities,
6728        "output_modalities": output_modalities,
6729        "capabilities": {
6730            // Every chat-shaped capability is FALSE off the chat surface: an embedder
6731            // does not stream, does not call tools, and does not reason.
6732            "streaming": is_chat,
6733            "tools": is_chat && caps.is_some_and(|c| c.tools_branch),
6734            // A switchless force-open `<think>` tail refuses `response_format` ONLY when
6735            // its think-close contract is unknown (`think_close` empty — GLM-5.3-Flash);
6736            // with a known close sequence POST-THINK constrained decoding serves it
6737            // (lane/step37-postthink-grammar), so the advertisement mirrors the actual
6738            // `build_chat_request` refusal. The heuristic form of this predicate shipped in
6739            // v0.123.0 and advertised false for step37 while the server served schema-valid
6740            // constrained output on it.
6741            "structured_output": is_chat
6742                && !is_dsv4
6743                && !caps.is_some_and(|c| c.qwen_think && !c.think_switch && c.think_close.is_empty()),
6744            "reasoning": is_chat && thinking,
6745            "prompt_caching": is_chat && !is_dsv4,
6746        },
6747        "pricing": {
6748            "currency": "USD",
6749            "unit": "per_1m_tokens",
6750            "input": per_1m(metadata.and_then(|m| m.pricing.prompt.as_deref())),
6751            "output": per_1m(metadata.and_then(|m| m.pricing.completion.as_deref())),
6752            "cached_input": per_1m(metadata.and_then(|m| m.pricing.cached_prompt.as_deref())),
6753            "cache_write": per_1m(metadata.and_then(|m| m.pricing.cache_write.as_deref())),
6754            // Per-REQUEST minimum in USD (not a token rate): our request price, "0" default.
6755            "minimum_request": metadata
6756                .and_then(|m| m.pricing.request.as_deref())
6757                .unwrap_or("0"),
6758        },
6759        "lifecycle": {
6760            "status": lifecycle.and_then(|l| l.status.as_deref()).unwrap_or("active"),
6761            "deprecation_at": lifecycle.and_then(|l| l.deprecation_at.as_deref()),
6762            "retirement_at": lifecycle.and_then(|l| l.retirement_at.as_deref()),
6763            "replacement_model_id": lifecycle.and_then(|l| l.replacement_model_id.as_deref()),
6764        },
6765        "reliability": {
6766            "first_token_timeout_seconds":
6767                reliability.and_then(|r| r.first_token_timeout_seconds).unwrap_or(120),
6768            "completion_timeout_seconds":
6769                reliability.and_then(|r| r.completion_timeout_seconds).unwrap_or(900),
6770            "stream_idle_timeout_seconds":
6771                reliability.and_then(|r| r.stream_idle_timeout_seconds).unwrap_or(60),
6772            "capacity_scope":
6773                reliability.and_then(|r| r.capacity_scope.as_deref()).unwrap_or("model_region"),
6774        },
6775    })
6776}
6777
6778/// GET /v1/models — the existing OpenAI/OpenRouter catalog listing, enriched with per-model
6779/// metadata from the loaded plan (context length, tokenizer, instruct family).
6780async fn list_models_v1(State(st): State<AppState>) -> impl IntoResponse {
6781    let data: Vec<_> = st
6782        .models
6783        .iter()
6784        .map(|m| model_entry_v1(m, st.caps.get(m), st.openrouter_metadata.get(m)))
6785        .collect();
6786    let mut body = json!({
6787        "object": "list",
6788        "contract_version": "2.0",
6789        "data": data,
6790    });
6791    // Provider block (contract v2): operator identity from the metadata file, error
6792    // contract from server truth — 429 rate limits and 503 overload both carry
6793    // Retry-After (+ the retry-after-ms twin), quota exhaustion is the stable
6794    // insufficient_balance code on 402, and every response echoes x-request-id.
6795    if let Some(provider) = st.provider_metadata.as_ref() {
6796        body["provider"] = json!({
6797            "id": provider.id,
6798            "status_url": provider.status_url,
6799            "support_contact": provider.support_contact,
6800            "incident_contact": provider.incident_contact,
6801            "regions": provider.regions,
6802            "request_id_header": "x-request-id",
6803            "error_contract": {
6804                "rate_limit_status": 429,
6805                "overload_status": 503,
6806                "retry_after_header": "Retry-After",
6807                "account_quota_error_codes": ["insufficient_balance"],
6808            },
6809        });
6810    }
6811    Json(body)
6812}
6813
6814/// Per-lane counters + engine-truth interactive step latency (sidecar-compatible shape —
6815/// the x-lane QoS gate's receipts endpoint).
6816async fn yield_metrics(State(st): State<AppState>, headers: HeaderMap) -> Response {
6817    let metrics_scope = match authorize_metrics(&st.api_auth, &st.metrics_auth, &headers) {
6818        Ok(scope) => scope,
6819        Err(response) => return response,
6820    };
6821    if !metrics_scope.process_wide() {
6822        return error_response(
6823            StatusCode::FORBIDDEN,
6824            "completion api keys do not authorize process-wide yield metrics; configure \
6825             MEMRA_METRICS_TOKEN",
6826            "authentication_error",
6827            None,
6828        );
6829    }
6830    let m = st.metrics.lock().map(|m| m.clone()).unwrap_or_default();
6831    let lane = |i: usize| {
6832        json!({
6833            "admitted": m.lane_admitted[i], "shed": m.lane_shed[i],
6834            "completed": m.lane_completed[i], "tokens_out": m.lane_tokens[i],
6835        })
6836    };
6837    let mut body = json!({
6838        "lanes": {
6839            "interactive": lane(0), "judge": lane(1), "harvest": lane(2),
6840        },
6841        "interactive_step_ms": { "p50": m.step_p50_ms, "p99": m.step_p99_ms },
6842    });
6843    if metrics_scope.operator() {
6844        body["batch_size_last"] = json!(m.batch_size_last);
6845    }
6846    Json(body).into_response()
6847}
6848
6849/// Wait for the worker's admission verdict before committing a streaming response. Successful
6850/// admission publishes `PromptUsage` immediately, so this does not wait for a potentially slow
6851/// first token. Queueing intentionally keeps the request pre-header until capacity is available.
6852///
6853/// WHY THE PEEK MATTERS MORE THAN IT LOOKS (audit §OpenRouter uptime): once the first byte of
6854/// a 200 is written, the response is COMMITTED — a router cannot fail over, and a mid-stream
6855/// death counts against uptime. Catching an admission refusal here converts a would-be
6856/// mid-stream failure into a clean pre-header 429/503 that the client's own retry handles.
6857///
6858/// The 429 body now goes through `engine_error_body` (G6). It used to be
6859/// `{"error": "<string>"}` — a BARE STRING where every OpenAI SDK expects an object, which
6860/// made shed errors render as a blank message in every client that parses the standard shape.
6861async fn peek_admission(
6862    mut rx: worker::EventReceiver,
6863) -> Result<worker::EventReceiver, (Response, &'static str)> {
6864    match rx.recv().await {
6865        // Any pre-admission failure — a shed, a rejected allocation, a load fault — is
6866        // answered as a normal HTTP error with its own class instead of being smuggled into a
6867        // stream. Classification is the producer's (worker::EngineError), so this no longer
6868        // string-matches a "shed:" prefix that only ever existed as an in-band sentinel.
6869        Some(Event::Error(e)) => {
6870            let error_code = engine_error_code(e.class);
6871            Err((engine_error_response(&e), error_code))
6872        }
6873        first => {
6874            let (tx2, rx2) = worker::event_channel();
6875            if let Some(ev) = first {
6876                let _ = tx2.send(ev);
6877            }
6878            tokio::spawn(forward_events(rx, tx2));
6879            Ok(rx2)
6880        }
6881    }
6882}
6883
6884/// Pump worker events to the response side, and — the part that is load-bearing for
6885/// cancellation — drop the worker-side receiver AS SOON AS the consumer goes away, not at
6886/// the next event.
6887///
6888/// A plain `while let Some(ev) = rx.recv().await { tx2.send(ev) }` loop only discovers a
6889/// dropped consumer when the NEXT event arrives, so a request producing nothing yet (a
6890/// long prefill) kept its worker channel open indefinitely: the abort the worker looks for
6891/// (`req.tx.is_closed()`) never appeared, and neither a client disconnect nor a deadline
6892/// miss could actually cancel it. Selecting on `tx2.closed()` closes that gap for every
6893/// consumer-side exit — client hang-up, deadline, or handler return.
6894async fn forward_events(mut rx: worker::EventReceiver, tx2: worker::EventSender) {
6895    loop {
6896        tokio::select! {
6897            biased;
6898            () = tx2.closed() => break,
6899            ev = rx.recv() => match ev {
6900                Some(ev) => {
6901                    if tx2.send(ev).is_err() {
6902                        break;
6903                    }
6904                }
6905                None => break,
6906            },
6907        }
6908    }
6909}
6910
6911/// STREAMING TTFT DEADLINE (lane/deadline-billing-20260823): hold the response PRE-HEADER
6912/// until the first generated event (token, done, or fault) or the deadline, whichever is
6913/// first. A deadline miss can then be an honest, retryable 408 — once the first byte of a
6914/// 200 is written the response is COMMITTED (see `peek_admission`), and a mid-stream error
6915/// chunk is neither a status a router can act on nor a promise-keeping "you don't pay"
6916/// signal. This extends the existing pre-header posture (queueing already holds
6917/// pre-header until admission) through prefill: headers now commit at first token, which
6918/// is bounded by the deadline (<= 90 s), inside the fronting proxy's ~100 s
6919/// time-to-headers ceiling.
6920///
6921/// Pre-token events (PromptUsage) are buffered and re-injected in order, so the stream
6922/// consumer's receipt discipline is unchanged. On a miss the receiver — and with it the
6923/// worker-side event channel — is dropped, which IS the cancel signal: the worker retires
6924/// closed-channel requests queued or active at the next tick.
6925async fn peek_first_token(
6926    mut rx: worker::EventReceiver,
6927    deadline: RequestDeadline,
6928) -> Result<worker::EventReceiver, ()> {
6929    let mut buffered: Vec<Event> = Vec::new();
6930    loop {
6931        match tokio::time::timeout_at(deadline.at, rx.recv()).await {
6932            Err(_) => return Err(()), // deadline elapsed; dropping rx cancels generation
6933            Ok(None) => break,        // worker gone: the stream's closed-channel law handles it
6934            Ok(Some(ev)) => {
6935                let first_delivery = matches!(
6936                    ev,
6937                    Event::Token { .. } | Event::Done { .. } | Event::Error(_)
6938                );
6939                buffered.push(ev);
6940                if first_delivery {
6941                    break;
6942                }
6943            }
6944        }
6945    }
6946    let (tx2, rx2) = worker::event_channel();
6947    for ev in buffered {
6948        let _ = tx2.send(ev);
6949    }
6950    tokio::spawn(forward_events(rx, tx2));
6951    Ok(rx2)
6952}
6953
6954/// Build the (GenParams, SamplerConfig, stop, prompt) from a request body.
6955#[cfg(test)]
6956/// Test helper: the raw-prompt build with NO per-model vendor defaults declared, i.e. the
6957/// API-standard fallback path. Tests that exercise the vendor-default substitution pass their
6958/// own `SamplingDefaults` to `build_request_with_trace` directly.
6959fn build_request(
6960    req: &CompletionReq,
6961    tx: worker::EventSender,
6962    lane: lanes::Lane,
6963    affinity: Option<String>,
6964) -> Request {
6965    build_request_with_trace(req, tx, lane, affinity, None, &SamplingDefaults::default())
6966}
6967
6968fn build_request_with_trace(
6969    req: &CompletionReq,
6970    tx: worker::EventSender,
6971    lane: lanes::Lane,
6972    affinity: Option<String>,
6973    ttft: Option<Arc<ttft::Trace>>,
6974    sampling_defaults: &SamplingDefaults,
6975) -> Request {
6976    let params = GenParams {
6977        max_new: req.max_tokens.unwrap_or(worker::MAX_NEW_CTX_BOUNDED),
6978        max_ctx: req.max_ctx,
6979        eos: Vec::new(), // worker adds the model's own eos id
6980    };
6981    // Same resolver the chat/messages/responses surfaces use — the raw-prompt surface gets the
6982    // model's vendor-recommended sampling for omitted fields too (standard-surface law). Before
6983    // this lane it could not: its fields were bare `f32`s, so "omitted" was indistinguishable
6984    // from "1.0" and the per-model default was silently unreachable here.
6985    let sampler_cfg = resolve_sampler_config(req.into(), sampling_defaults);
6986    Request {
6987        model: req.model.clone(),
6988        prompt_ids: req.prompt_ids.clone(),
6989        prompt_text: req.prompt.clone(),
6990        chat: req.chat,
6991        chat_turns: Vec::new(),
6992        tools_json: Vec::new(),
6993        tools_struct: Vec::new(),
6994        think: ThinkMode::Default,
6995        reasoning_effort: None, // /v1/completions is a raw-prompt surface (no template render)
6996        params,
6997        sampler_cfg,
6998        stop_strings: req.stop.clone().into_vec(),
6999        trace_id: req.trace_id.clone(),
7000        // Stamped with the envelope id by the handler before submission (the builder
7001        // does not see the envelope).
7002        request_id: String::new(),
7003        admit_predict_logged: false,
7004        max_prompt_tokens: None,
7005        cache_ns: cache_namespace(&req.cache_salt),
7006        affinity,
7007        lane,
7008        grammar: None, // /v1/completions carries no response_format (chat surface only)
7009        prepared_constraint: None,
7010        constraint_ready: None,
7011        oom_retries: 0, // step-OOM park budget: fresh from the HTTP layer (lane/admit-oom)
7012        spec_k_replay: None,
7013        prepared_prompt: None,
7014        capture: None,      // set only by the embeddings/rerank routes
7015        images: Vec::new(), // /v1/completions is a raw-text surface
7016        gemma_images: Vec::new(),
7017        glm5_images: Vec::new(),
7018        step_images: Vec::new(),
7019        vision_memory: None,
7020        wire_deadline: None, // stamped by the handler at submission (with request_id)
7021        ttft,
7022        tx,
7023    }
7024}
7025
7026/// Everything the chat handler derives from the request body before submitting to the
7027/// worker: the worker Request plus the parser arming state for the response side.
7028struct ChatPlan {
7029    request: Request,
7030    /// Some(parser) when a <tools> block was rendered — the ONLY case the emission parser
7031    /// runs (non-tools traffic keeps byte-identical streams, chunk boundaries included).
7032    parser: Option<ToolStreamParser>,
7033    /// Header-planned vision units awaiting their post-admission pixel decode
7034    /// (`decode_pending_vision`) — see the hermes decode-bomb fix, 2026-08-23.
7035    pending_images: Vec<PendingVisionUnit>,
7036    pending_gemma: Vec<PendingGemmaImage>,
7037    pending_glm5: Vec<PendingGlm5Image>,
7038    pending_step: Vec<PendingStepImage>,
7039    /// Process-wide patch-memory reservation carried into the worker request. It is released when
7040    /// the worker drops the request after completion or cancellation, so streaming responses do
7041    /// not reopen the pre-admission memory window.
7042    vision_memory: Option<VisionMemoryPermit>,
7043}
7044
7045pub(crate) fn request_has_vision(req: &ChatCompletionReq) -> bool {
7046    req.messages.iter().any(|message| {
7047        message.content.as_array().is_some_and(|parts| {
7048            parts.iter().any(|part| {
7049                matches!(
7050                    part.get("type").and_then(serde_json::Value::as_str),
7051                    Some("image_url" | "video_url")
7052                )
7053            })
7054        })
7055    })
7056}
7057
7058fn planned_vision_bytes(plan: &ChatPlan) -> Result<usize, String> {
7059    let mut total = 0usize;
7060    let mut add = |bytes: usize| {
7061        total = total.checked_add(bytes).ok_or_else(|| {
7062            "vision patch memory reservation overflowed while planning".to_string()
7063        })?;
7064        Ok::<(), String>(())
7065    };
7066    for unit in &plan.pending_images {
7067        let bytes = match unit {
7068            PendingVisionUnit::Still { gh, gw, .. } => gh
7069                .checked_mul(*gw)
7070                .and_then(|n| n.checked_mul(memra_engine::vision::V_PATCH_IN))
7071                .and_then(|n| n.checked_mul(std::mem::size_of::<f32>()))
7072                .ok_or_else(|| "vision patch memory reservation overflowed".to_string())?,
7073            PendingVisionUnit::Video { groups, .. } => {
7074                groups.iter().try_fold(0usize, |total, group| {
7075                    let bytes = group
7076                        .gh
7077                        .checked_mul(group.gw)
7078                        .and_then(|n| n.checked_mul(memra_engine::vision::V_PATCH_IN))
7079                        .and_then(|n| n.checked_mul(std::mem::size_of::<f32>()))
7080                        .ok_or_else(|| "vision patch memory reservation overflowed".to_string())?;
7081                    total.checked_add(bytes).ok_or_else(|| {
7082                        "vision patch memory reservation overflowed while planning".to_string()
7083                    })
7084                })?
7085            }
7086        };
7087        add(bytes)?;
7088    }
7089    for unit in &plan.pending_gemma {
7090        let bytes = unit
7091            .gw
7092            .checked_mul(unit.gh)
7093            .and_then(|n| n.checked_mul(memra_engine::vision_gemma::GV_PATCH_IN))
7094            .and_then(|n| n.checked_mul(std::mem::size_of::<f32>()))
7095            .ok_or_else(|| "vision patch memory reservation overflowed".to_string())?;
7096        add(bytes)?;
7097    }
7098    for unit in &plan.pending_glm5 {
7099        let bytes = unit
7100            .gh
7101            .checked_mul(unit.gw)
7102            .and_then(|n| n.checked_mul(memra_engine::vision_glm5::G5V_PATCH_IN))
7103            .and_then(|n| n.checked_mul(std::mem::size_of::<f32>()))
7104            .ok_or_else(|| "vision patch memory reservation overflowed".to_string())?;
7105        add(bytes)?;
7106    }
7107    for unit in &plan.pending_step {
7108        use memra_engine::vision_step::{SV_GRID_MAIN, SV_GRID_TILE, SV_PATCH_IN};
7109        // one 52x52 main view + n_tiles 36x36 crops, 588 f32 per patch row
7110        let patches = unit
7111            .plan
7112            .n_tiles
7113            .checked_mul(SV_GRID_TILE * SV_GRID_TILE)
7114            .and_then(|n| n.checked_add(SV_GRID_MAIN * SV_GRID_MAIN))
7115            .ok_or_else(|| "vision patch memory reservation overflowed".to_string())?;
7116        let bytes = patches
7117            .checked_mul(SV_PATCH_IN)
7118            .and_then(|n| n.checked_mul(std::mem::size_of::<f32>()))
7119            .ok_or_else(|| "vision patch memory reservation overflowed".to_string())?;
7120        add(bytes)?;
7121    }
7122    Ok(total)
7123}
7124
7125pub(crate) fn reserve_vision_memory(
7126    plan: &ChatPlan,
7127) -> Result<Option<VisionMemoryPermit>, VisionMemoryError> {
7128    let bytes = planned_vision_bytes(plan).map_err(VisionMemoryError::Request)?;
7129    try_reserve_vision_memory(bytes)
7130}
7131
7132#[cfg(test)]
7133fn build_chat_request(
7134    req: ChatCompletionReq,
7135    caps: Option<&ModelCaps>,
7136    tx: worker::EventSender,
7137    lane: lanes::Lane,
7138    affinity: Option<String>,
7139) -> Result<ChatPlan, String> {
7140    // Test helper: no operator metadata, so the arch caps are the only default source — the
7141    // pre-lane behavior. Vendor-default tests pass their own `ModelSamplingDefaults`.
7142    let defaults = ModelSamplingDefaults::resolve(None, caps);
7143    build_chat_request_with_trace(req, caps, tx, lane, affinity, None, None, &defaults)
7144}
7145
7146/// `default_effort` is the model's operator-declared `default_reasoning_effort`
7147/// (MEMRA_MODEL_METADATA) — the serve callers pass it from the metadata map; None keeps
7148/// the model template's own default for the unset case (every model without the knob is
7149/// byte-identical to before the knob existed).
7150///
7151/// `sampling_defaults` is the same idea for the sampling fields (lane/vendor-default-sampling,
7152/// 2026-08-19): the model vendor's recommendation, substituted only into fields the client left
7153/// out. Built by `ModelSamplingDefaults::resolve` from the operator metadata block plus the
7154/// arch caps, and passed rather than computed here so the raw-prompt surface can share the
7155/// exact same resolver. It carries BOTH vendor arms (lane/per-mode-sampling, 2026-08-24);
7156/// the request's RESOLVED thinking mode picks the arm below, AFTER `parse_think` and the
7157/// constraint gate have settled it — so the arm always matches the mode the model actually
7158/// runs in, on every surface that funnels through this builder.
7159#[allow(clippy::too_many_arguments)]
7160fn build_chat_request_with_trace(
7161    req: ChatCompletionReq,
7162    caps: Option<&ModelCaps>,
7163    tx: worker::EventSender,
7164    lane: lanes::Lane,
7165    affinity: Option<String>,
7166    ttft: Option<Arc<ttft::Trace>>,
7167    default_effort: Option<&str>,
7168    sampling_defaults: &ModelSamplingDefaults,
7169) -> Result<ChatPlan, String> {
7170    req.stop.validate()?;
7171    // The client's own expression is snapshotted here; the omitted fields resolve to a
7172    // vendor arm only once the thinking mode is final (see `sampler_cfg` below).
7173    let client_sampling: ClientSampling = (&req).into();
7174    let tool_choice = parse_tool_choice(&req.tool_choice)?;
7175    // Template honesty gate (serve-st lane, 2026-08-04): a directory checkpoint
7176    // (safetensors/repack) with NO chat template cannot honestly serve chat — 400 with a
7177    // clear message instead of silently rendering fallback ChatML the model never saw.
7178    // GGUF models keep the historical fallback (chat_ok=true there regardless).
7179    if let Some(c) = caps
7180        && !c.chat_ok
7181    {
7182        return Err(format!(
7183            "model {:?} has no chat template (checkpoint carries neither \
7184                 tokenizer_config.json chat_template nor chat_template.jinja) — \
7185                 /v1/chat/completions unavailable; use /v1/completions with a raw prompt",
7186            req.model
7187        ));
7188    }
7189    let vllm_switch = resolve_vllm_think_switch(req.enable_thinking, &req.chat_template_kwargs)?;
7190    let (mut think, effort_level, think_client_explicit) = parse_think(
7191        &req.reasoning_effort,
7192        &req.reasoning,
7193        vllm_switch,
7194        req.include_reasoning,
7195        default_effort,
7196        // Templates with a real rung ABOVE `high`: deepseek-v4's BEYOND_MAX prefix and
7197        // GLM-5.3-Flash's `Reasoning Effort: Max` (its own default). Clamping xhigh/max/ultra
7198        // into `high` on these silently drops the tier the client asked for.
7199        caps.is_some_and(|c| c.dsv4 || c.glm5),
7200    )?;
7201    // Does this model's template express a reasoning DEPTH at all, and can it be turned off?
7202    // Both are template-probed capabilities, never inferred from the family name (house law:
7203    // a control is never assumed from a shared loader, format or lineage).
7204    let level_template = caps
7205        .map(|c| c.effort_levels || c.dsv4 || c.qwen_effort || c.glm5)
7206        .unwrap_or(false);
7207    // SILENT-IGNORE GATE (lane/reasoning-control-20260823, corrected here). A client that
7208    // explicitly asked for reasoning OFF, on a model whose template opens a `<think>` tail it
7209    // cannot close, cannot be served that request: the prompt would render think-open anyway
7210    // and the reply would stream a full reasoning block behind a 200. That is the owner's named
7211    // unacceptable case — asking for non-reasoning and getting reasoning — so it is a named 400.
7212    // Scoped to a CLIENT-explicit off-request (`think_client_explicit`): a deployment
7213    // `default_reasoning_effort` must never 400 a caller who sent nothing.
7214    //
7215    // TWO DIALECTS ARE EXEMPT, and both were false positives of the marker pair as PR #33 shipped
7216    // it (found by review before release, no customer ever saw them):
7217    //   - `dsv4`: the deepseek-v4 renderer honours NoThink through its own `chat` thinking mode
7218    //     (a closed `</think>`), so it needs no `enable_thinking` marker to turn reasoning off.
7219    //     Latent rather than live today only because encoding-keyed artifacts carry no template
7220    //     string; keyed here explicitly so it cannot become live by accident.
7221    //   - a template with NO think tail at all (`!qwen_think`) — gemma4's thought channel and
7222    //     hy3's `no_think` header both close cleanly and never matched this gate.
7223    // step35 is deliberately NOT exempt even though it consumes effort levels: its `<think>` tail
7224    // is unconditional, so its documented `none|minimal -> "Reasoning: low"` clamp answered an
7225    // off-request WITH reasoning at the lowest rung. That is the unacceptable case wearing a
7226    // clamp, and the 400 replaces it.
7227    if think_client_explicit
7228        && think == ThinkMode::NoThink
7229        && let Some(c) = caps
7230        && c.qwen_think
7231        && !c.think_switch
7232        && !c.dsv4
7233    {
7234        return Err(format!(
7235            "model {:?} cannot disable reasoning: its chat template opens a think \
7236                     tail unconditionally and carries no enable_thinking switch, so \
7237                     reasoning_effort/enable_thinking cannot turn it off on this model",
7238            req.model
7239        ));
7240    }
7241    // GRADATION ON A BINARY MODEL: TRANSLATE, never refuse (coordinator ruling 2026-08-23,
7242    // resolving two owner rulings that pulled against each other). A first cut of this lane
7243    // REFUSED a graded level on a model whose template has no depth input — the construction
7244    // proof being that low/medium/high render bytes identical to an unset request there. The
7245    // refusal was correct arithmetic and the wrong law: the owner explicitly authorised
7246    // normalisation ("it can be translated into one schema that we use"), the standard-surface
7247    // law makes real-CLI round-trips a launch gate, and stock codex (`reasoning.effort:"xhigh"`)
7248    // and stock Claude Code (`output_config.effort:"xhigh"`) send a graded level on EVERY
7249    // request — the 400 broke default-config agent sessions against ornith, the exact model we
7250    // serve to agents.
7251    //
7252    // The owner's unacceptable case is asking for NON-reasoning and getting reasoning. A caller
7253    // sending `xhigh` asked for reasoning and gets reasoning — the translation keeps the
7254    // promise. So the mapping, documented here and in SERVING.md rather than implied:
7255    //
7256    //   graded level (low|medium|high|xhigh) on a binary-switch model  =>  reasoning ON.
7257    //
7258    // No code runs here to do it: `parse_think` already resolved every ON rung to
7259    // `ThinkMode::Think`, and the `level_template` delivery gate below drops the rung string for
7260    // templates with no ladder — so the rendered prompt is byte-identical to an explicit
7261    // `reasoning:{"enabled":true}` by construction (pinned by
7262    // `a_graded_level_on_a_binary_model_translates_to_reasoning_on`). The named 400s stay for
7263    // what is genuinely unhonourable: unknown keys, wrong types, contradictions, and the
7264    // off-request a template cannot honour (the gate above).
7265    // Effort-level templates: the client's reasoning_effort is a RENDER input, not a think
7266    // switch — step35/hy3 (`effort_levels`: "Reasoning: {level}\n\n" / header level), qwen3.8
7267    // (`qwen_effort`: the `xhigh|medium|low` instruction sentence at the head of the system
7268    // turn) and deepseek-v4 (`dsv4`: the encoding's effort-prompt prefix, resolved against the
7269    // artifact's detected encoding revision — 0731 ladder low/high/max where "high" is a
7270    // REAL prefix; the preview treats "high" as its documented no-op and "medium" renders
7271    // as the default level under both, the never-corrupt clamp). Gate on the capability so
7272    // every other model's prompt stays byte-identical.
7273    let reasoning_effort = if level_template { effort_level } else { None };
7274    // response_format -> grammar spec (constrained decoding). None/text = unconstrained,
7275    // the exact legacy path; unknown/malformed forms are loud 400s.
7276    let grammar = constrained::parse_response_format(req.response_format.as_ref())?;
7277    // GRAMMAR x THINK (measured live 2026-08-03): the grammar masks from the FIRST
7278    // generated token, so an open <think> tail can never be closed — the forced JSON
7279    // lands in the think segment and `content` comes back empty. Constrained requests
7280    // force the template's no-think switch — that path is byte-identical to before this
7281    // lane. A think-tail template WITHOUT the switch serves POST-THINK constrained
7282    // decoding instead (lane/step37-postthink-grammar, 2026-08-30) when its think-close
7283    // token contract is derivable (`ModelCaps::think_close`): the think phase runs
7284    // unconstrained exactly as the model was trained (EOS banned, so the response cannot
7285    // end inside think), and the grammar clamps every token from the close on. The worker
7286    // arms the gate at admission from the same load-time contract; nothing else is
7287    // plumbed through the request. A think-forced template with NO derivable close
7288    // contract keeps the loud 400 (honesty gate), never a silent
7289    // constrain-from-token-1 stream.
7290    if grammar.is_some()
7291        && let Some(c) = caps
7292        && c.qwen_think
7293        && think != ThinkMode::NoThink
7294    {
7295        if c.think_switch {
7296            think = ThinkMode::NoThink;
7297        } else if c.think_close.is_empty() {
7298            return Err(
7299                "response_format requires the model's think channel to close \
7300                                before the grammar can engage, but this chat template has \
7301                                neither an enable_thinking switch nor a recognizable \
7302                                think-close token sequence"
7303                    .into(),
7304            );
7305        }
7306        // else: POST-THINK constrained decoding — think stays ON (the
7307        // template's only honest mode); the worker engages the grammar at the
7308        // close token(s).
7309    }
7310
7311    // PER-MODE VENDOR DEFAULTS (lane/per-mode-sampling, 2026-08-24): the thinking mode is
7312    // final from here on, so this is the one point where an omitted sampling field becomes
7313    // a number — the resolved mode picks the vendor arm, then the same client-wins law as
7314    // ever (`resolve_sampler_config`: client value > arm default > API-standard). A model
7315    // without a `non_thinking_sampling` table gets its single arm for every mode,
7316    // byte-identical to when this call sat at the top of the function.
7317    let sampler_cfg = resolve_sampler_config(client_sampling, sampling_defaults.for_mode(think));
7318
7319    // tool_choice "none" = OpenAI "the model will not call tools": the prompt renders
7320    // WITHOUT the tools block (byte-identical to a no-tools request) and no parser runs.
7321    let (tools_json, tools_struct, schemas) =
7322        if !req.tools.is_empty() && tool_choice == ToolChoice::Auto {
7323            prepare_tools(&req.tools)?
7324        } else {
7325            (Vec::new(), Vec::new(), HashMap::new())
7326        };
7327
7328    let mut turns: Vec<TmplTurn> = Vec::with_capacity(req.messages.len());
7329    let mut images: Vec<PendingVisionUnit> = Vec::new();
7330    let mut gemma_images: Vec<PendingGemmaImage> = Vec::new();
7331    let mut glm5_images: Vec<PendingGlm5Image> = Vec::new();
7332    let mut step_images: Vec<PendingStepImage> = Vec::new();
7333    let mut next_video = 0usize;
7334    for msg in &req.messages {
7335        let content = content_to_text_vision(
7336            &msg.content,
7337            &mut images,
7338            &mut gemma_images,
7339            &mut glm5_images,
7340            &mut step_images,
7341            &mut next_video,
7342        )
7343        .map_err(|e| format!("{} message: {e}", msg.role))?;
7344        let tool_calls = msg
7345            .tool_calls
7346            .iter()
7347            .map(render_req_tool_call)
7348            .collect::<Result<Vec<_>, _>>()?;
7349        if !tool_calls.is_empty() && msg.role != "assistant" {
7350            return Err("tool_calls are only valid on assistant messages".into());
7351        }
7352        // OpenAI's `developer` role is their o-series rename of `system`; chat templates
7353        // know only `system`, so normalize here (matches OpenAI's own equivalence).
7354        let role = if msg.role == "developer" {
7355            "system".to_string()
7356        } else {
7357            msg.role.clone()
7358        };
7359        turns.push(TmplTurn {
7360            role,
7361            content,
7362            tool_calls,
7363            // gemma4-only fields; the qwen/step dialects ignore them.
7364            reasoning: msg.reasoning.clone().filter(|r| !r.is_empty()),
7365            tool_call_id: msg.tool_call_id.clone(),
7366            tool_name: msg.name.clone(),
7367            tool_responses: Vec::new(),
7368            // dsv4-only fields: the OpenAI serve surface carries no `task` head, and dsv4
7369            // request-level tools flow via `tools_struct` (folded onto the leading system
7370            // turn by the dsv4 arm); every other dialect ignores both.
7371            task: None,
7372            tools: Vec::new(),
7373        });
7374    }
7375
7376    // Capability gate: reject tools on models whose template has no tools branch BEFORE
7377    // the request reaches the GPU worker (clean 400 instead of a mid-stream error).
7378    let has_tool_features = !tools_json.is_empty()
7379        || turns
7380            .iter()
7381            .any(|t| t.role == "tool" || !t.tool_calls.is_empty());
7382    if has_tool_features && !caps.map(|c| c.tools_branch).unwrap_or(false) {
7383        return Err(format!(
7384            "model {:?} chat template has no tools branch",
7385            req.model
7386        ));
7387    }
7388
7389    // Parser think gate: the rendered prompt ends with an OPEN think tail (template
7390    // default, not switched off by reasoning_effort on a switch-carrying template).
7391    let think_open = caps
7392        .map(|c| c.qwen_think && !(think == ThinkMode::NoThink && c.think_switch))
7393        .unwrap_or(false);
7394    // REASONING SEPARATION (gap-scan F13): think-segment text routes to the OpenRouter
7395    // `reasoning` response field on EVERY chat request against a think-open prompt —
7396    // content is post-think only. Tools requests keep the full tool-call scanner; non-tools
7397    // think-open requests get the reasoning-only splitter (post-think text unscanned).
7398    // Models without a think tail keep a byte-identical no-parser stream.
7399    //
7400    // REASONING IS ALWAYS DELIVERED (owner ruling 2026-08-23). There is no longer a
7401    // suppression path: `include_reasoning:false` and `reasoning.exclude:true` are handled far
7402    // upstream in `parse_think`, where they turn reasoning OFF instead of hiding it. Reasoning
7403    // tokens are output tokens and are billed as output, so withholding them was charging for
7404    // output we did not send; the drop capability is deleted from the parser rather than merely
7405    // left unreachable, so the third state (generate, bill, withhold) cannot be reintroduced by
7406    // wiring a flag back to it.
7407    // gemma4 tooluse dialect: tools rendered into the gemma template need the gemma call
7408    // parser (`<|tool_call>call:NAME{…}<tool_call|>` + thought channels), NOT the qwen
7409    // `<tool_call>`/`<parameter=…>` scanner. Keyed on the gemma marker so qwen/step keep
7410    // their own scanner.
7411    let gemma_tools = !tools_json.is_empty() && caps.map(|c| c.gemma_think).unwrap_or(false);
7412    // deepseek-v4 dialect: thinking mode maps to encoding_dsv4's thinking_mode (Default/Think
7413    // -> thinking, an open `<think>` tail; NoThink -> chat, a closed `</think>`). The parser
7414    // splits `</think>` reasoning + `<|DSML|tool_calls>` blocks. Armed on EVERY dsv4 chat
7415    // request (like gemma_think): tools present -> full call parser; else a reasoning splitter
7416    // that also passes content through cleanly.
7417    let is_dsv4 = caps.map(|c| c.dsv4).unwrap_or(false);
7418    let dsv4_think_open = is_dsv4 && think != ThinkMode::NoThink;
7419    let dsv4_tools = is_dsv4 && !tools_struct.is_empty();
7420    // GLM-5.3-Flash dialect: `<think>` reasoning (unconditional tail, no separator newlines
7421    // after the close) plus `<tool_call>NAME<arg_key>…` calls. Armed on EVERY glm5 chat request
7422    // like the gemma/dsv4 arms: with tools the full call parser, without them the reasoning
7423    // splitter — the qwen scanner's `<function=` body grammar never matches this wire, so
7424    // before this branch a glm5 tool call would have surfaced VERBATIM as content.
7425    let glm5 = caps.map(|c| c.glm5).unwrap_or(false);
7426    // Tencent HY3 dialect: reasoning closes with `</think:opensource>` and calls use the
7427    // suffixed `<tool_calls:opensource>` protocol. Armed on think-open or tools, like dsv4.
7428    let is_hy3 = caps.map(|c| c.hy3).unwrap_or(false);
7429    let hy3_think_open = is_hy3 && think == ThinkMode::Think;
7430    let hy3_tools = is_hy3 && !tools_json.is_empty();
7431    let parser = if glm5 {
7432        Some(ToolStreamParser::glm5(think_open, schemas))
7433    } else if is_hy3 && (hy3_tools || hy3_think_open) {
7434        Some(ToolStreamParser::hy3(schemas, hy3_think_open))
7435    } else if is_dsv4 && (dsv4_tools || dsv4_think_open) {
7436        Some(ToolStreamParser::dsv4(dsv4_think_open))
7437    } else if gemma_tools {
7438        Some(ToolStreamParser::gemma_tools())
7439    } else if !tools_json.is_empty() {
7440        Some(ToolStreamParser::new(schemas, think_open))
7441    } else if think_open {
7442        Some(ToolStreamParser::reasoning_only())
7443    } else if caps.map(|c| c.gemma_think).unwrap_or(false) {
7444        // gemma4 thought-channel dialect (lane/gemma4-serve-gaps): thought text used to
7445        // land VERBATIM in content — `<|channel>thought\n…` with thinking on, and the tags
7446        // leaked with it (think-smoke receipt, step-sku lane). Armed on EVERY gemma4 chat
7447        // request, not just thinking-on: the closed-channel prompt still leaves the model
7448        // free to open a channel mid-stream (observed live), and the template's own
7449        // strip_thinking law applies wherever the tags appear. gemma4 templates carry no
7450        // tools branch, so this arm never competes with the tool scanner.
7451        Some(ToolStreamParser::gemma_thought())
7452    } else {
7453        None
7454    };
7455
7456    Ok(ChatPlan {
7457        request: Request {
7458            model: req.model,
7459            prompt_ids: Vec::new(),
7460            prompt_text: String::new(),
7461            chat: false,
7462            chat_turns: turns,
7463            tools_json,
7464            tools_struct,
7465            think,
7466            reasoning_effort,
7467            params: GenParams {
7468                max_new: req.max_tokens.unwrap_or(worker::MAX_NEW_CTX_BOUNDED),
7469                max_ctx: req.max_ctx,
7470                eos: Vec::new(),
7471            },
7472            sampler_cfg,
7473            stop_strings: {
7474                // gemma4 tooluse: the model emits `<|tool_call>call:…<tool_call|>` and would
7475                // then run past its handoff into a hallucinated `<|tool_response>`; stop when
7476                // the call completes (scoped to gemma tool requests — never global). The stop
7477                // token stays in the stream (not a silent eos) so the parser closes the span.
7478                let mut stops = req.stop.into_vec();
7479                if gemma_tools {
7480                    stops.push("<tool_call|>".to_string());
7481                }
7482                // deepseek-v4 tool requests: stop when the DSML tool_calls block closes, so the
7483                // model does not run past its handoff into a hallucinated `<tool_result>`
7484                // (scoped to dsv4 tool requests, never global; the close stays in the stream so
7485                // the parser finishes the span — same law as gemma's `<tool_call|>`).
7486                if dsv4_tools {
7487                    stops.push("</\u{ff5c}DSML\u{ff5c}tool_calls>".to_string());
7488                }
7489                // HY3 tool requests: stop on the native suffixed tool_calls close. Keep the
7490                // marker in the stream so the parser can close and emit every call.
7491                if hy3_tools {
7492                    stops.push("</tool_calls:opensource>".to_string());
7493                }
7494                stops
7495            },
7496            trace_id: None,
7497            // Stamped with the envelope id by the handler before submission (the plan
7498            // builder does not see the envelope).
7499            request_id: String::new(),
7500            admit_predict_logged: false,
7501            max_prompt_tokens: None,
7502            cache_ns: cache_namespace(&req.cache_salt),
7503            affinity,
7504            lane,
7505            grammar,
7506            prepared_constraint: None,
7507            constraint_ready: None,
7508            oom_retries: 0, // step-OOM park budget: fresh from the HTTP layer (lane/admit-oom)
7509            spec_k_replay: None,
7510            prepared_prompt: None,
7511            // Filled by decode_pending_vision AFTER budget admission (hermes
7512            // decode-bomb finding, fixed 2026-08-23) — the pad runs above were rendered
7513            // from header-planned grids, so admission prices the full vision prompt
7514            // without a single canvas expanding.
7515            images: Vec::new(),
7516            gemma_images: Vec::new(),
7517            glm5_images: Vec::new(),
7518            step_images: Vec::new(),
7519            capture: None, // set only by the embeddings/rerank routes
7520            vision_memory: None,
7521            wire_deadline: None, // stamped by the handler at submission (with request_id)
7522            ttft,
7523            tx,
7524        },
7525        parser,
7526        pending_images: images,
7527        pending_gemma: gemma_images,
7528        pending_glm5: glm5_images,
7529        pending_step: step_images,
7530        vision_memory: None,
7531    })
7532}
7533
7534/// Phase 2 of the vision path: decode the planned stills into patch rows, AFTER budget
7535/// admission (hermes decode-bomb finding, fixed 2026-08-23). Order is preserved — the
7536/// worker aligns pad runs 1:1 with `images`. Each decoded grid must equal its planned
7537/// grid: the pad runs are already rendered from the plan, so a mismatch (a container
7538/// whose header lies about dimensions) refuses rather than desyncing runs from units.
7539fn decode_pending_vision(plan: &mut ChatPlan) -> Result<(), String> {
7540    for (i, unit) in plan.pending_images.drain(..).enumerate() {
7541        match unit {
7542            PendingVisionUnit::Still { bytes, gh, gw } => {
7543                let prep = memra_engine::vision_pre::prep_image_bytes(&bytes)
7544                    .map_err(|e| format!("image {}: {e}", i + 1))?;
7545                if (prep.gh, prep.gw) != (gh, gw) {
7546                    return Err(format!(
7547                        "image {}: decoded grid {}x{} differs from its header-planned grid {gh}x{gw} — refusing (pad runs already rendered)",
7548                        i + 1,
7549                        prep.gh,
7550                        prep.gw
7551                    ));
7552                }
7553                plan.request
7554                    .images
7555                    .push(memra_engine::vision_pre::VisionUnit { prep, video: None });
7556            }
7557            PendingVisionUnit::Video {
7558                bytes,
7559                groups,
7560                video,
7561            } => {
7562                let prepared = memra_engine::vision_pre::prep_video_gif(&bytes)
7563                    .map_err(|e| format!("video {}: {e}", i + 1))?;
7564                if prepared.groups.len() != groups.len() {
7565                    return Err(format!(
7566                        "video {}: decoded {} groups differ from its header-planned {} groups",
7567                        i + 1,
7568                        prepared.groups.len(),
7569                        groups.len()
7570                    ));
7571                }
7572                for ((group, prep), timestamp) in
7573                    groups.iter().zip(prepared.groups).zip(prepared.timestamps)
7574                {
7575                    if (prep.gh, prep.gw) != (group.gh, group.gw) {
7576                        return Err(format!(
7577                            "video {}: decoded grid {}x{} differs from its header-planned grid {}x{}",
7578                            i + 1,
7579                            prep.gh,
7580                            prep.gw,
7581                            group.gh,
7582                            group.gw
7583                        ));
7584                    }
7585                    if (timestamp - group.timestamp).abs() > 0.001 {
7586                        return Err(format!(
7587                            "video {}: decoded timestamp {timestamp:.3} differs from its header-planned timestamp {:.3}",
7588                            i + 1,
7589                            group.timestamp
7590                        ));
7591                    }
7592                    plan.request
7593                        .images
7594                        .push(memra_engine::vision_pre::VisionUnit {
7595                            prep,
7596                            video: Some(video),
7597                        });
7598                }
7599            }
7600        }
7601    }
7602    for (i, unit) in plan.pending_gemma.drain(..).enumerate() {
7603        let (patches, gw, gh) = memra_engine::vision_gemma::gemma_prep_image(&unit.bytes)
7604            .map_err(|e| format!("image {}: {e}", i + 1))?;
7605        if (gw, gh) != (unit.gw, unit.gh) {
7606            return Err(format!(
7607                "image {}: decoded grid {gw}x{gh} differs from its header-planned grid {}x{} — refusing (pad runs already rendered)",
7608                i + 1,
7609                unit.gw,
7610                unit.gh
7611            ));
7612        }
7613        plan.request
7614            .gemma_images
7615            .push(memra_engine::vision_gemma::GemmaVisionUnit { patches, gw, gh });
7616    }
7617    for (i, unit) in plan.pending_glm5.drain(..).enumerate() {
7618        let (patches, gh, gw) = memra_engine::vision_glm5::glm5_prep_image(&unit.bytes)
7619            .map_err(|e| format!("image {}: {e}", i + 1))?;
7620        if (gh, gw) != (unit.gh, unit.gw) {
7621            return Err(format!(
7622                "image {}: decoded grid {gh}x{gw} differs from its header-planned grid {}x{} — refusing (placeholder runs already rendered)",
7623                i + 1,
7624                unit.gh,
7625                unit.gw
7626            ));
7627        }
7628        plan.request
7629            .glm5_images
7630            .push(memra_engine::vision_glm5::Glm5VisionUnit { patches, gh, gw });
7631    }
7632    for (i, unit) in plan.pending_step.drain(..).enumerate() {
7633        let prepped = memra_engine::vision_step::step_prep_image(&unit.bytes)
7634            .map_err(|e| format!("image {}: {e}", i + 1))?;
7635        if prepped.tiles.len() != unit.plan.n_tiles
7636            || prepped.newline_mask != unit.plan.newline_mask
7637        {
7638            return Err(format!(
7639                "image {}: decoded tiling ({} tiles) differs from its header-planned tiling \
7640                 ({} tiles) — refusing (pad runs already rendered)",
7641                i + 1,
7642                prepped.tiles.len(),
7643                unit.plan.n_tiles
7644            ));
7645        }
7646        plan.request.step_images.push(prepped);
7647    }
7648    Ok(())
7649}
7650
7651/// Resolve the request's tenant identity (lane/api-keys, 2026-08-05). The law lives in
7652/// `auth::authenticate_with`; this wraps the startup-resolved auth sources:
7653///   MEMRA_API_KEYS keyring match -> that key's tenant/lane-class/rate-limit;
7654///   MEMRA_API_KEY single-key match -> tenant "default" (back-compat: the daily driver
7655///     and every serve script keep working unchanged, keyring configured or not);
7656///   neither configured -> open, tenant "default";
7657///   otherwise Err: Unknown -> 401 (OpenAI authentication_error), Disabled -> 403.
7658fn bearer_token(headers: &HeaderMap) -> Option<&str> {
7659    headers
7660        .get("authorization")
7661        .and_then(|value| value.to_str().ok())
7662        .and_then(|value| value.strip_prefix("Bearer "))
7663}
7664
7665fn authentication_error(why: auth::AuthDenied) -> Response {
7666    match why {
7667        auth::AuthDenied::Unknown => error_response(
7668            StatusCode::UNAUTHORIZED,
7669            "invalid api key",
7670            "authentication_error",
7671            None,
7672        ),
7673        auth::AuthDenied::Disabled => error_response(
7674            StatusCode::FORBIDDEN,
7675            "api key is disabled",
7676            "authentication_error",
7677            None,
7678        ),
7679    }
7680}
7681
7682#[allow(clippy::result_large_err)] // allow: the fat error type is the diagnostic contract here; boxing it would change the error surface
7683fn authenticate(api_auth: &ApiAuth, headers: &HeaderMap) -> Result<auth::TenantCtx, Response> {
7684    auth::authenticate_with(
7685        api_auth.keyring,
7686        api_auth.single_key.as_deref(),
7687        bearer_token(headers),
7688    )
7689    .map_err(authentication_error)
7690}
7691
7692#[derive(Debug, Clone, PartialEq, Eq)]
7693enum MetricsScope {
7694    All,
7695    CompletionDomain,
7696    Tenant(String),
7697}
7698
7699impl MetricsScope {
7700    fn operator(&self) -> bool {
7701        matches!(self, MetricsScope::All)
7702    }
7703
7704    fn process_wide(&self) -> bool {
7705        matches!(self, MetricsScope::All | MetricsScope::CompletionDomain)
7706    }
7707
7708    fn includes(&self, tenant_row: &str) -> bool {
7709        match self {
7710            MetricsScope::All | MetricsScope::CompletionDomain => true,
7711            MetricsScope::Tenant(tenant) => tenant == tenant_row,
7712        }
7713    }
7714}
7715
7716#[allow(clippy::result_large_err)] // allow: the fat error type is the diagnostic contract here; boxing it would change the error surface
7717fn authorize_metrics(
7718    api_auth: &ApiAuth,
7719    metrics_auth: &MetricsAuth,
7720    headers: &HeaderMap,
7721) -> Result<MetricsScope, Response> {
7722    if !metrics_auth.required {
7723        return Ok(MetricsScope::All);
7724    }
7725    let Some(candidate) = bearer_token(headers) else {
7726        return Err(authentication_error(auth::AuthDenied::Unknown));
7727    };
7728    if let Some(token) = metrics_auth.token.as_deref() {
7729        if auth::constant_time_secret_eq(token, candidate) {
7730            return Ok(MetricsScope::All);
7731        }
7732        if api_auth.configured() {
7733            return match auth::authenticate_with(
7734                api_auth.keyring,
7735                api_auth.single_key.as_deref(),
7736                Some(candidate),
7737            ) {
7738                Ok(_) => Err(error_response(
7739                    StatusCode::FORBIDDEN,
7740                    "completion api keys do not authorize metrics while \
7741                     MEMRA_METRICS_TOKEN is configured",
7742                    "authentication_error",
7743                    None,
7744                )),
7745                Err(why) => Err(authentication_error(why)),
7746            };
7747        }
7748        return Err(authentication_error(auth::AuthDenied::Unknown));
7749    }
7750    if api_auth.configured() {
7751        let tenant = authenticate(api_auth, headers)?;
7752        return Ok(if api_auth.keyring.is_some() {
7753            MetricsScope::Tenant(format!("t:{}", tenant.tenant))
7754        } else {
7755            // Without a keyring there is one completion tenancy domain. Its metering
7756            // rows are raw cache_salt values, so they all belong to this caller. It is
7757            // still a completion credential, not an operator scrape principal.
7758            MetricsScope::CompletionDomain
7759        });
7760    }
7761    Err(authentication_error(auth::AuthDenied::Unknown))
7762}
7763
7764/// Lane resolution with the tenant's lane class applied: interactive-class keys keep the
7765/// legacy behavior exactly (default interactive, any x-lane honored); batch-class keys
7766/// DEFAULT to harvest and are refused the protected interactive lane (403, loud — the
7767/// QoS gate exists to protect interactive from bulk traffic, so a bulk key cannot claim
7768/// the protected class by omission or by header).
7769#[allow(clippy::result_large_err)] // allow: the fat error type is the diagnostic contract here; boxing it would change the error surface
7770fn lane_for_tenant(
7771    headers: &axum::http::HeaderMap,
7772    tenant: &auth::TenantCtx,
7773) -> Result<lanes::Lane, Response> {
7774    let requested = match headers.get("x-lane").map(|v| v.to_str().unwrap_or("?")) {
7775        None => None,
7776        // A bad x-lane really is a client bug, so 400 is the right status — but the body has to
7777        // be an OpenAI-compat error OBJECT like every other refusal on this surface. It used to
7778        // be a bare `{"error":"unknown x-lane ..."}` string, which makes `e.body["error"]["type"]`
7779        // an index error in every SDK that parses the standard shape.
7780        Some(v) => Some(lanes::Lane::parse(v).ok_or_else(|| {
7781            error_response_coded(
7782                StatusCode::BAD_REQUEST,
7783                &format!("unknown x-lane {v:?}; expected one of interactive, judge, harvest"),
7784                "invalid_request_error",
7785                Some("x-lane"),
7786                Some("invalid_lane"),
7787            )
7788        })?),
7789    };
7790    match tenant.lane_class {
7791        auth::LaneClass::Interactive => Ok(requested.unwrap_or(lanes::Lane::Interactive)),
7792        auth::LaneClass::Batch => match requested {
7793            None => Ok(lanes::Lane::Harvest),
7794            Some(lanes::Lane::Interactive) => Err(error_response(
7795                StatusCode::FORBIDDEN,
7796                "this api key is batch-class: x-lane interactive is not permitted \
7797                 (use judge or harvest)",
7798                "authentication_error",
7799                Some("x-lane"),
7800            )),
7801            Some(l) => Ok(l),
7802        },
7803    }
7804}
7805
7806/// The tenant-scoped PC-ISO namespace: keyring configured -> `t:<tenant>\x1f<salt>`
7807/// (a tenant's keys share cache, different tenants never — auth::scope_namespace);
7808/// no keyring -> the validated raw salt. Invalid values fail at the HTTP boundary.
7809fn tenant_namespace(
7810    tenant: &auth::TenantCtx,
7811    cache_salt: &Option<String>,
7812) -> Result<String, &'static str> {
7813    let keyring_configured = auth::global().is_some();
7814    let raw = validate_cache_namespace(cache_salt, keyring_configured)?;
7815    if keyring_configured {
7816        Ok(auth::scope_namespace(&tenant.tenant, &raw))
7817    } else {
7818        Ok(raw)
7819    }
7820}
7821
7822/// METER SEAM (public-repo half): one flat log line per admitted request with the tenant
7823/// identity — the private fork's metering layer parses these for per-tenant usage/billing;
7824/// the public repo only emits. Completion accounting stays on the existing worker-truth
7825/// usage/abort lines; this line binds request-id -> tenant -> model/lane at admission.
7826fn meter_admit(env: &Envelope, tenant: &auth::TenantCtx, model: &str, lane: lanes::Lane) {
7827    eprintln!(
7828        "[meter] admit id={} tenant={} lane={} model={:?}",
7829        env.id,
7830        tenant.tenant,
7831        lane.as_str(),
7832        model
7833    );
7834}
7835
7836fn apply_model_request_limits(
7837    request: &mut Request,
7838    metadata: Option<&OpenRouterModelMetadata>,
7839    caps: Option<&ModelCaps>,
7840) -> Result<(), (String, &'static str)> {
7841    let Some(metadata) = metadata else {
7842        return Ok(());
7843    };
7844    let max_prompt = metadata
7845        .max_prompt_length
7846        .map(usize::try_from)
7847        .transpose()
7848        .map_err(|_| {
7849            (
7850                "configured model prompt limit does not fit this platform".into(),
7851                "model",
7852            )
7853        })?;
7854    let max_output = metadata
7855        .max_output_length
7856        .map(usize::try_from)
7857        .transpose()
7858        .map_err(|_| {
7859            (
7860                "configured model output limit does not fit this platform".into(),
7861                "model",
7862            )
7863        })?;
7864
7865    request.max_prompt_tokens = max_prompt;
7866    if let Some(max_output) = max_output {
7867        if request.params.max_new == worker::MAX_NEW_CTX_BOUNDED {
7868            request.params.max_new = metadata
7869                .default_output_length
7870                .map(usize::try_from)
7871                .transpose()
7872                .map_err(|_| {
7873                    (
7874                        "configured default output length does not fit this platform".into(),
7875                        "model",
7876                    )
7877                })?
7878                .unwrap_or(max_output);
7879        } else if request.params.max_new > max_output {
7880            return Err((
7881                format!(
7882                    "max_tokens {} exceeds configured model maximum {max_output}",
7883                    request.params.max_new
7884                ),
7885                "max_tokens",
7886            ));
7887        }
7888    }
7889
7890    // `max_ctx` is a memra extension. Refuse a client-selected allocation larger than the
7891    // advertised prompt+output envelope: otherwise a tiny request could reserve the model's
7892    // full trained context and bypass the production shape's VRAM admission contract.
7893    if let (Some(max_prompt), Some(max_output), Some(requested_ctx)) =
7894        (max_prompt, max_output, request.params.max_ctx)
7895    {
7896        let operational_ctx = max_prompt
7897            .checked_add(max_output)
7898            .and_then(|value| value.checked_add(8))
7899            .ok_or_else(|| {
7900                (
7901                    "configured model context envelope overflowed".into(),
7902                    "model",
7903                )
7904            })?;
7905        let operational_ctx = caps
7906            .map(|caps| caps.context_length)
7907            .filter(|&context| context > 0)
7908            .map_or(operational_ctx, |context| operational_ctx.min(context));
7909        if requested_ctx > operational_ctx {
7910            return Err((
7911                format!(
7912                    "max_ctx {requested_ctx} exceeds configured model envelope {operational_ctx}"
7913                ),
7914                "max_ctx",
7915            ));
7916        }
7917    }
7918    Ok(())
7919}
7920
7921/// The request's effective completion-token bound for the receipt row (D2 gap G4):
7922/// `params.max_new` after `apply_model_request_limits` resolution, `None` when it is
7923/// still the context-bounded sentinel.
7924fn effective_max_tokens(request: &worker::Request) -> Option<u64> {
7925    (request.params.max_new != worker::MAX_NEW_CTX_BOUNDED).then_some(request.params.max_new as u64)
7926}
7927
7928#[allow(clippy::too_many_arguments)]
7929fn start_request_receipt(
7930    st: &AppState,
7931    env: &Envelope,
7932    tenant: &auth::TenantCtx,
7933    model: &str,
7934    route: &'static str,
7935    lane: lanes::Lane,
7936    stream: bool,
7937    max_tokens: Option<u64>,
7938    reserved_ctx: Option<u64>,
7939    budget_permit: Option<metering::Permit>,
7940) -> Option<Box<dyn metering::Receipt>> {
7941    st.metering.as_ref().map(|accounting| {
7942        accounting.open(
7943            &metering::RequestMeta {
7944                request_id: &env.id,
7945                tenant: &tenant.tenant,
7946                principal: tenant.key_prefix.as_deref(),
7947                model,
7948                route,
7949                lane: lane.as_str(),
7950                stream,
7951                max_tokens,
7952                reserved_ctx,
7953            },
7954            budget_permit,
7955        )
7956    })
7957}
7958
7959/// Attach capture to a successful-admission receipt when the tenant is marked. The
7960/// prompt payload is built lazily — unmarked tenants (the overwhelming majority of
7961/// traffic) pay only the receipt's `wants_capture` flag, set once at open. The
7962/// settle-time re-check inside the implementation remains the authoritative
7963/// capture decision.
7964fn arm_capture(
7965    mut receipt: Option<Box<dyn metering::Receipt>>,
7966    prompt: impl FnOnce() -> serde_json::Value,
7967) -> Option<Box<dyn metering::Receipt>> {
7968    if let Some(receipt) = receipt.as_mut()
7969        && receipt.wants_capture()
7970    {
7971        receipt.arm_capture(prompt());
7972    }
7973    receipt
7974}
7975
7976/// The capture row's prompt payload: the messages array as the caller sent it
7977/// (role/content/tool_calls), rebuilt from the parsed request. Content stays the
7978/// original JSON value, so string and array-of-parts shapes round-trip unchanged.
7979fn capture_chat_messages(messages: &[ChatMessage]) -> serde_json::Value {
7980    serde_json::Value::Array(
7981        messages
7982            .iter()
7983            .map(|message| {
7984                let mut row = json!({ "role": message.role, "content": message.content });
7985                if !message.tool_calls.is_empty() {
7986                    row["tool_calls"] = serde_json::Value::Array(
7987                        message
7988                            .tool_calls
7989                            .iter()
7990                            .map(|call| {
7991                                json!({
7992                                    "id": call.id,
7993                                    "function": {
7994                                        "name": call.function.name,
7995                                        "arguments": call.function.arguments,
7996                                    },
7997                                })
7998                            })
7999                            .collect(),
8000                    );
8001                }
8002                row
8003            })
8004            .collect(),
8005    )
8006}
8007
8008enum BudgetRejection {
8009    Invalid(String),
8010    Insufficient,
8011    Unenrolled,
8012    /// The authenticated KEY's spend cap is reached (the tenant may still have
8013    /// balance). Distinct 402 code: the recovery is raising the key's cap.
8014    PrincipalCapped,
8015    Unavailable(String),
8016}
8017
8018impl BudgetRejection {
8019    fn into_response(self) -> (Response, &'static str) {
8020        match self {
8021            Self::Invalid(message) => (bad_request(&message, Some("prompt")), "invalid_request"),
8022            Self::Insufficient => (
8023                error_response_coded(
8024                    StatusCode::PAYMENT_REQUIRED,
8025                    "tenant prepaid balance is insufficient for this request",
8026                    "insufficient_balance",
8027                    None,
8028                    Some("insufficient_balance"),
8029                ),
8030                "insufficient_balance",
8031            ),
8032            Self::Unenrolled => (
8033                error_response_coded(
8034                    StatusCode::PAYMENT_REQUIRED,
8035                    "tenant is not enrolled for prepaid billing",
8036                    "tenant_not_enrolled",
8037                    None,
8038                    Some("tenant_not_enrolled"),
8039                ),
8040                "tenant_not_enrolled",
8041            ),
8042            Self::PrincipalCapped => (
8043                error_response_coded(
8044                    StatusCode::PAYMENT_REQUIRED,
8045                    "this API key's spend cap is reached; raise or clear the key's cap to continue",
8046                    "key_spend_cap_reached",
8047                    None,
8048                    Some("key_spend_cap_reached"),
8049                ),
8050                "key_spend_cap_reached",
8051            ),
8052            Self::Unavailable(err) => {
8053                eprintln!("[budget] ERROR: admission unavailable: {err}");
8054                (
8055                    error_response_coded(
8056                        StatusCode::SERVICE_UNAVAILABLE,
8057                        "tenant budget accounting is unavailable",
8058                        "server_error",
8059                        None,
8060                        Some("tenant_budget_unavailable"),
8061                    ),
8062                    "tenant_budget_unavailable",
8063                )
8064            }
8065        }
8066    }
8067}
8068
8069fn prepare_budget_prompt(
8070    request: &mut Request,
8071    tokenizer: Option<&Tokenizer>,
8072) -> Result<usize, String> {
8073    if let Some(error) = worker::prompt_source_limit_error(request) {
8074        return Err(error);
8075    }
8076    if request.prepared_prompt.is_none() {
8077        if let Some(trace) = request.ttft.as_ref() {
8078            trace.mark_tokenize_start();
8079        }
8080        let prompt = if !request.prompt_ids.is_empty() {
8081            request.prompt_ids.clone()
8082        } else if !request.chat_turns.is_empty() {
8083            let tokenizer = tokenizer.ok_or("reservation tokenizer is unavailable")?;
8084            // The SHARED fast-path predicate (worker::plain_chat_render_path) — this is the
8085            // render that actually serves: the worker's `prepare` only re-renders when
8086            // `prepared_prompt` is still None, and this budget-admission path fills it first.
8087            // v0.109.1's first cut fixed the worker copies only, and the live probe showed
8088            // why one predicate must exist ONCE: unset q38 chats still served the bare bytes
8089            // because THIS third copy kept routing them down the legacy render.
8090            let plain = worker::plain_chat_render_path(
8091                &request.tools_json,
8092                &request.think,
8093                request.reasoning_effort.as_deref(),
8094                &request.chat_turns,
8095                tokenizer.has_qwen_effort_ladder(),
8096            );
8097            let rendered = if plain {
8098                let messages: Vec<_> = request
8099                    .chat_turns
8100                    .iter()
8101                    .map(|turn| (turn.role.as_str(), turn.content.as_str()))
8102                    .collect();
8103                tokenizer.apply_chat_template(&messages, true)
8104            } else {
8105                tokenizer
8106                    .apply_chat_template_tools_ex(
8107                        &request.chat_turns,
8108                        true,
8109                        &request.tools_json,
8110                        &request.tools_struct,
8111                        request.think,
8112                        request.reasoning_effort.as_deref(),
8113                    )
8114                    .map_err(|err| format!("chat template: {err}"))?
8115            };
8116            tokenizer.encode(&rendered, true)
8117        } else if request.chat {
8118            let tokenizer = tokenizer.ok_or("reservation tokenizer is unavailable")?;
8119            let rendered =
8120                tokenizer.apply_chat_template(&[("user", request.prompt_text.as_str())], true);
8121            tokenizer.encode(&rendered, true)
8122        } else {
8123            let tokenizer = tokenizer.ok_or("reservation tokenizer is unavailable")?;
8124            tokenizer.encode(&request.prompt_text, true)
8125        };
8126        if prompt.is_empty() {
8127            return Err("empty prompt after tokenization".into());
8128        }
8129        if let Some(trace) = request.ttft.as_ref() {
8130            trace.mark_tokenize_end(prompt.len());
8131        }
8132        request.prepared_prompt = Some(prompt);
8133    }
8134    let prompt_tokens = request
8135        .prepared_prompt
8136        .as_ref()
8137        .expect("budget prompt was prepared")
8138        .len();
8139    if let Some(limit) = request.max_prompt_tokens
8140        && prompt_tokens > limit
8141    {
8142        return Err(format!(
8143            "prompt ({prompt_tokens} tok) exceeds configured model maximum ({limit})"
8144        ));
8145    }
8146    Ok(prompt_tokens)
8147}
8148
8149fn budget_completion_bound(
8150    request: &Request,
8151    prompt_tokens: usize,
8152    caps: Option<&ModelCaps>,
8153) -> Result<usize, String> {
8154    let max_new = request.params.max_new;
8155    let requested_ctx = match (request.params.max_ctx, max_new) {
8156        (Some(cap), _) => cap,
8157        (None, worker::MAX_NEW_CTX_BOUNDED) => {
8158            let server_ctx = std::env::var("MEMRA_CTX")
8159                .ok()
8160                .and_then(|value| value.parse().ok())
8161                .unwrap_or(8192usize);
8162            let mut cap = server_ctx;
8163            if prompt_tokens.saturating_add(16) > cap {
8164                cap = prompt_tokens.saturating_add(server_ctx);
8165            }
8166            cap
8167        }
8168        (None, max_new) => prompt_tokens
8169            .checked_add(max_new)
8170            .and_then(|value| value.checked_add(8))
8171            .ok_or_else(|| "request context bound overflowed".to_string())?,
8172    };
8173    let ctx_cap = caps
8174        .map(|caps| caps.context_length)
8175        .filter(|&context| context > 0)
8176        .map_or(requested_ctx, |context| requested_ctx.min(context));
8177    if prompt_tokens >= ctx_cap {
8178        return Err(format!(
8179            "prompt ({prompt_tokens} tok) >= context cap ({ctx_cap})"
8180        ));
8181    }
8182    Ok(max_new.min(ctx_cap - prompt_tokens))
8183}
8184
8185/// What budget admission produced for the receipt row: the reservation permit and the
8186/// context it charged (D2 gap G4's "reserved ctx": `prompt_tokens + completion bound`,
8187/// the same quantities handed to `Metering::reserve`). `reserved_ctx` is `None` exactly
8188/// when no reservation ran.
8189struct BudgetAdmission {
8190    permit: Option<metering::Permit>,
8191    reserved_ctx: Option<u64>,
8192}
8193
8194// Manual: `Permit` is `Box<dyn Any>`; the presence bit is the useful debug fact.
8195impl std::fmt::Debug for BudgetAdmission {
8196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8197        f.debug_struct("BudgetAdmission")
8198            .field("permit", &self.permit.is_some())
8199            .field("reserved_ctx", &self.reserved_ctx)
8200            .finish()
8201    }
8202}
8203
8204fn admit_tenant_budget(
8205    st: &AppState,
8206    tenant: &auth::TenantCtx,
8207    request: &mut Request,
8208) -> Result<BudgetAdmission, BudgetRejection> {
8209    let Some(accounting) = st.metering.as_ref().filter(|m| m.enforces_limits()) else {
8210        return Ok(BudgetAdmission {
8211            permit: None,
8212            reserved_ctx: None,
8213        });
8214    };
8215    match accounting.is_limited(&tenant.tenant) {
8216        Ok(false) => return Err(BudgetRejection::Unenrolled),
8217        Ok(true) => {}
8218        Err(metering::AdmitError::Unavailable(err)) => {
8219            return Err(BudgetRejection::Unavailable(err));
8220        }
8221        Err(other) => {
8222            return Err(BudgetRejection::Unavailable(format!(
8223                "unexpected budget enrollment result: {other:?}"
8224            )));
8225        }
8226    }
8227    let tokenizer = st
8228        .budget_tokenizers
8229        .as_ref()
8230        .and_then(|tokenizers| tokenizers.get(&request.model))
8231        .map(Arc::as_ref);
8232    if request.prompt_ids.is_empty() && tokenizer.is_none() {
8233        return Err(BudgetRejection::Unavailable(format!(
8234            "no reservation tokenizer for model {:?}",
8235            request.model
8236        )));
8237    }
8238    let prompt_tokens =
8239        prepare_budget_prompt(request, tokenizer).map_err(BudgetRejection::Invalid)?;
8240    let completion_tokens =
8241        budget_completion_bound(request, prompt_tokens, st.caps.get(&request.model))
8242            .map_err(BudgetRejection::Invalid)?;
8243    let prompt_tokens = u64::try_from(prompt_tokens)
8244        .map_err(|_| BudgetRejection::Unavailable("prompt token count exceeds u64".into()))?;
8245    let completion_tokens = u64::try_from(completion_tokens)
8246        .map_err(|_| BudgetRejection::Unavailable("completion token bound exceeds u64".into()))?;
8247    match accounting.reserve(
8248        &tenant.tenant,
8249        tenant.key_prefix.as_deref(),
8250        &request.model,
8251        prompt_tokens,
8252        completion_tokens,
8253    ) {
8254        Ok(permit) => Ok(BudgetAdmission {
8255            permit,
8256            reserved_ctx: Some(prompt_tokens.saturating_add(completion_tokens)),
8257        }),
8258        Err(metering::AdmitError::Insufficient) => Err(BudgetRejection::Insufficient),
8259        Err(metering::AdmitError::PrincipalCapped) => Err(BudgetRejection::PrincipalCapped),
8260        // Provisioning-policy blocks intentionally reuse the prepaid 402 shape:
8261        // callers need one recovery action (add credit), while operators can read
8262        // the distinct admission mode from the authenticated admin surface.
8263        Err(metering::AdmitError::Blocked) => Err(BudgetRejection::Insufficient),
8264        Err(metering::AdmitError::Unenrolled) => Err(BudgetRejection::Unenrolled),
8265        Err(metering::AdmitError::Unavailable(err)) => Err(BudgetRejection::Unavailable(err)),
8266    }
8267}
8268
8269fn request_ledger_error_response() -> Response {
8270    error_response_coded(
8271        StatusCode::INTERNAL_SERVER_ERROR,
8272        "request completion could not be committed to the billing ledger",
8273        "server_error",
8274        None,
8275        Some("request_ledger_unavailable"),
8276    )
8277}
8278
8279fn request_ledger_error_body() -> serde_json::Value {
8280    error_body(
8281        "request completion could not be committed to the billing ledger",
8282        "server_error",
8283        None,
8284        Some("request_ledger_unavailable"),
8285    )
8286}
8287
8288fn ledger_rejected(
8289    mut receipt: Option<Box<dyn metering::Receipt>>,
8290    response: Response,
8291    error_code: &str,
8292    request_id: &str,
8293) -> Response {
8294    let status = response.status().as_u16();
8295    if let Some(receipt) = receipt.as_mut()
8296        && let Err(err) = receipt.reject(status, error_code)
8297    {
8298        eprintln!("[ledger] ERROR: request {request_id} rejection receipt failed: {err}");
8299        return with_request_id(request_id, request_ledger_error_response());
8300    }
8301    with_request_id(request_id, response)
8302}
8303
8304/// Settle a receipt with a NAMED zero-debit outcome (`deadline_exceeded`, `shed_deadline`,
8305/// `shed_queue`, `shed_queue_wait`) — `ledger_rejected`'s twin for terminal rows whose outcome the billing
8306/// census distinguishes from a plain rejection. Never bills (enforced again in
8307/// `ledger::PendingReceipt::finalize`).
8308fn ledger_unbilled(
8309    mut receipt: Option<Box<dyn metering::Receipt>>,
8310    response: Response,
8311    outcome: &'static str,
8312    error_code: &str,
8313    request_id: &str,
8314) -> Response {
8315    let status = response.status().as_u16();
8316    if let Some(receipt) = receipt.as_mut()
8317        && let Err(err) = receipt.settle_unbilled(outcome, status, error_code)
8318    {
8319        eprintln!("[ledger] ERROR: request {request_id} {outcome} receipt failed: {err}");
8320        return with_request_id(request_id, request_ledger_error_response());
8321    }
8322    with_request_id(request_id, response)
8323}
8324
8325fn engine_error_code(class: worker::ErrClass) -> &'static str {
8326    use worker::ErrClass as C;
8327    match class {
8328        C::InvalidRequest => "invalid_request",
8329        C::ContextLength => "context_length_exceeded",
8330        C::ModelNotFound => "model_not_found",
8331        C::RateLimit => "rate_limit_exceeded",
8332        C::Overloaded => "overloaded",
8333        C::Engine => "engine_error",
8334    }
8335}
8336
8337/// Canonicalize a requested model id to a LOADED alias, tolerating a stripped vendor prefix.
8338///
8339/// Marketplaces normalize model ids before calling upstream. Onlist lists
8340/// `qwen/qwen3.6-35b-a3b` but probes us for `qwen3.6-35b-a3b`, which produced
8341/// `unknown model "qwen3.6-35b-a3b"; loaded: ["qwen/qwen3.6-27b", "qwen/qwen3.6-35b-a3b"]`.
8342/// The engine was right and the mapping was wrong, but the listing side offers no upstream-id
8343/// override, so inbound tolerance belongs here.
8344///
8345/// An EXACT alias always wins, so nothing already working can change meaning. Otherwise, if
8346/// exactly ONE loaded alias's segment after the last `/` equals the request, that alias is used.
8347/// **Ambiguity is deliberately not resolved**: if two loaded aliases share a suffix
8348/// (`a/m` and `b/m`), the request stays unknown rather than silently routing to the wrong
8349/// weights and billing under the wrong model. `/v1/models` continues to advertise canonical ids
8350/// only — this is request tolerance, not a second public name.
8351/// The immediate 400 for a model id that resolves to nothing. This MUST fire before
8352/// prepaid budget admission: a budgeted tenant's reservation path needs the model's
8353/// tokenizer, so an unresolved id used to surface as a 503 "budget accounting is
8354/// unavailable" — a customer's typo dressed up as our outage. Same class/code the
8355/// worker's own roster rejection uses, so the error shape is identical either way.
8356fn model_not_found_response(models: &[String], requested: &str) -> Response {
8357    error_response_coded(
8358        StatusCode::BAD_REQUEST,
8359        &format!("unknown model {requested:?}; loaded: {models:?}"),
8360        "invalid_request_error",
8361        Some("model"),
8362        Some("model_not_found"),
8363    )
8364}
8365
8366/// prompt_ids OOV gate (hermes, fixed 2026-08-19): `/v1/completions` accepts a raw
8367/// token-id prompt (`prompt_ids`, the exact-token validation-gate path) and NOTHING
8368/// bounded those ids against the model's vocabulary — an out-of-vocab id rode through
8369/// admission into the embed gather, an attacker-chosen row index past the embedding
8370/// table. Checked at INTAKE against worker-probed tokenizer truth (`ModelCaps::n_vocab`):
8371/// a clean 400 naming the first offending id, before the request costs a queue slot or
8372/// reaches the worker. `n_vocab == 0` (unknown) skips the gate — honest-unknown, the
8373/// same convention as every other caps field.
8374fn validate_prompt_ids(ids: &[u32], caps: Option<&ModelCaps>) -> Result<(), String> {
8375    let Some(n_vocab) = caps.map(|c| c.n_vocab).filter(|&n| n > 0) else {
8376        return Ok(());
8377    };
8378    if let Some((pos, &id)) = ids
8379        .iter()
8380        .enumerate()
8381        .find(|&(_, &id)| id as usize >= n_vocab)
8382    {
8383        return Err(format!(
8384            "prompt_ids[{pos}] = {id} is out of vocabulary (model vocab size {n_vocab})"
8385        ));
8386    }
8387    Ok(())
8388}
8389
8390#[cfg(test)]
8391mod prompt_ids_tests {
8392    use super::*;
8393
8394    #[test]
8395    fn prompt_ids_are_bounded_by_the_model_vocab_at_intake() {
8396        let caps = ModelCaps {
8397            n_vocab: 8,
8398            ..Default::default()
8399        };
8400        // in bounds: every id < n_vocab, boundary included.
8401        assert!(validate_prompt_ids(&[0, 3, 7], Some(&caps)).is_ok());
8402        assert!(validate_prompt_ids(&[], Some(&caps)).is_ok());
8403        // out of bounds: first offender named by position and value.
8404        let err = validate_prompt_ids(&[1, 8, 2], Some(&caps)).unwrap_err();
8405        assert!(err.contains("prompt_ids[1] = 8"), "{err}");
8406        assert!(err.contains("vocab size 8"), "{err}");
8407        let err = validate_prompt_ids(&[u32::MAX], Some(&caps)).unwrap_err();
8408        assert!(err.contains("4294967295"), "{err}");
8409        // unknown vocab (0) or unknown model: honest-unknown, no gate.
8410        let unknown = ModelCaps::default();
8411        assert!(validate_prompt_ids(&[u32::MAX], Some(&unknown)).is_ok());
8412        assert!(validate_prompt_ids(&[u32::MAX], None).is_ok());
8413    }
8414}
8415
8416fn canonical_model_id(models: &[String], requested: &str) -> Option<String> {
8417    if models.iter().any(|m| m == requested) {
8418        return Some(requested.to_string());
8419    }
8420    if requested.is_empty() || requested.contains('/') {
8421        return None;
8422    }
8423    let mut matches = models.iter().filter(|m| {
8424        m.rsplit('/')
8425            .next()
8426            .is_some_and(|suffix| suffix == requested)
8427    });
8428    match (matches.next(), matches.next()) {
8429        (Some(only), None) => Some(only.clone()),
8430        _ => None,
8431    }
8432}
8433
8434async fn completions_admitted(
8435    state: State<AppState>,
8436    headers: axum::http::HeaderMap,
8437    trace: Option<Extension<TtftRequestTrace>>,
8438    AdmittedJson(req, admission): AdmittedJson<CompletionReq>,
8439) -> Response {
8440    completions_with_admission(state, headers, trace, Json(req), Some(admission)).await
8441}
8442
8443#[cfg(test)]
8444async fn completions(
8445    State(st): State<AppState>,
8446    headers: axum::http::HeaderMap,
8447    trace: Option<Extension<TtftRequestTrace>>,
8448    request: Json<CompletionReq>,
8449) -> Response {
8450    completions_with_admission(State(st), headers, trace, request, None).await
8451}
8452
8453async fn completions_with_admission(
8454    State(st): State<AppState>,
8455    headers: axum::http::HeaderMap,
8456    trace: Option<Extension<TtftRequestTrace>>,
8457    Json(mut req): Json<CompletionReq>,
8458    mut body_admission: Option<BodyAdmissionLease>,
8459) -> Response {
8460    let env = Envelope::new(false);
8461    if let Err(msg) = req.stop.validate() {
8462        return with_request_id(&env.id, bad_request(&msg, Some("stop")));
8463    }
8464    if let Err(msg) = validate_client_identifier(req.trace_id.as_deref(), "trace_id") {
8465        return with_request_id(&env.id, bad_request(&msg, Some("trace_id")));
8466    }
8467    match canonical_model_id(&st.models, &req.model) {
8468        Some(canonical) => req.model = canonical,
8469        None => {
8470            return with_request_id(&env.id, model_not_found_response(&st.models, &req.model));
8471        }
8472    }
8473    // API key: OpenAI-style `Authorization: Bearer <key>` -> tenant identity
8474    // (MEMRA_API_KEYS keyring and/or the MEMRA_API_KEY single key; nothing set = open).
8475    let ttft = trace.and_then(|Extension(trace)| trace.0);
8476    if let Some(trace) = ttft.as_ref() {
8477        trace.mark_parsed();
8478        trace.bind_request(&env.id, &req.model);
8479    }
8480    let tenant = match authenticate(&st.api_auth, &headers) {
8481        Ok(t) => t,
8482        Err(resp) => return with_request_id(&env.id, resp),
8483    };
8484    let cache_ns = match tenant_namespace(&tenant, &req.cache_salt) {
8485        Ok(ns) => ns,
8486        Err(msg) => return with_request_id(&env.id, bad_request(msg, Some("cache_salt"))),
8487    };
8488    // HONESTY GATE (gap-scan F4): semantic params we can't honor 400 loudly.
8489    if let Err((msg, param)) = reject_unsupported(&[
8490        (
8491            "logit_bias",
8492            req.logit_bias.is_some(),
8493            " (device-side sampling has no bias hook yet)",
8494        ),
8495        ("logprobs", req.logprobs.is_some(), ""),
8496        (
8497            "n",
8498            req.n.is_some_and(|n| n != 1),
8499            " for n != 1 (single choice only)",
8500        ),
8501        (
8502            "best_of",
8503            req.best_of.is_some_and(|n| n != 1),
8504            " (single choice only)",
8505        ),
8506    ]) {
8507        return with_request_id(&env.id, bad_request(&msg, Some(&param)));
8508    }
8509    // OOV gate (hermes): raw prompt_ids are bounded by the model's vocabulary HERE,
8510    // before the request costs a slot or reaches the worker's embed gather.
8511    if let Err(msg) = validate_prompt_ids(&req.prompt_ids, st.caps.get(&req.model)) {
8512        return with_request_id(&env.id, bad_request(&msg, Some("prompt_ids")));
8513    }
8514    // Request deadline (lane/deadline-billing): validated with the other request params
8515    // (a named 400 costs no slot and opens no receipt), armed from this point on.
8516    let deadline = match parse_timeout_ms(req.timeout_ms.as_ref()) {
8517        Ok(ms) => RequestDeadline::starting_now(ms),
8518        Err(msg) => return with_request_id(&env.id, bad_request(&msg, Some("timeout_ms"))),
8519    };
8520    let lane = match lane_for_tenant(&headers, &tenant) {
8521        Ok(l) => l,
8522        Err(resp) => return resp,
8523    };
8524    let (tx, rx) = worker::event_channel();
8525    let model = req.model.clone();
8526    let stream = req.stream;
8527    let affinity = match affinity_key(&req.session_id, &req.user, &headers) {
8528        Ok(affinity) => affinity,
8529        Err(msg) => return with_request_id(&env.id, bad_request(&msg, Some("session_id"))),
8530    };
8531    let mut request = build_request_with_trace(
8532        &req,
8533        tx,
8534        lane,
8535        affinity,
8536        ttft.clone(),
8537        // /v1/completions is a raw-prompt surface: no template render, no thinking
8538        // control, `ThinkMode::Default` always — so the arm law resolves it to the
8539        // primary (thinking) arm through the same `for_mode` body the chat builder uses.
8540        st.sampling_defaults(&model).for_mode(ThinkMode::Default),
8541    );
8542    request.cache_ns = cache_ns;
8543    request.request_id = env.id.clone();
8544    // The wire deadline rides to the worker beside the receipt identity, so the
8545    // first-token deadline gate judges the REMAINING deadline at its own tick.
8546    request.wire_deadline = Some(deadline.at.into_std());
8547    if let Err((message, param)) = apply_model_request_limits(
8548        &mut request,
8549        st.openrouter_metadata.get(&model),
8550        st.caps.get(&model),
8551    ) {
8552        return with_request_id(&env.id, bad_request(&message, Some(param)));
8553    }
8554    // FEASIBILITY GATE: a non-streaming request we can see will not finish inside its
8555    // deadline is refused HERE — before a slot, a receipt or any GPU work — with the
8556    // max_tokens that would fit. Costs nothing and replaces a 90 s wait for a 408 that
8557    // threw away every token it had generated.
8558    if let Err(msg) = nonstream_deadline_gate(
8559        &request,
8560        req.stream,
8561        deadline,
8562        req.max_tokens.is_some(),
8563        st.budget_tokenizers
8564            .as_ref()
8565            .and_then(|t| t.get(&req.model))
8566            .map(Arc::as_ref),
8567    ) {
8568        return with_request_id(
8569            &env.id,
8570            error_response_coded(
8571                StatusCode::BAD_REQUEST,
8572                &msg,
8573                "invalid_request_error",
8574                Some("max_tokens"),
8575                Some("nonstream_deadline_infeasible"),
8576            ),
8577        );
8578    }
8579    // DRAIN GATE (gap-scan F11): preserve the existing shutdown contract before
8580    // consulting tenant balances or touching any slot/queue state.
8581    if draining() {
8582        let receipt = start_request_receipt(
8583            &st,
8584            &env,
8585            &tenant,
8586            &req.model,
8587            "/v1/completions",
8588            lane,
8589            req.stream,
8590            effective_max_tokens(&request),
8591            None,
8592            None,
8593        );
8594        return ledger_rejected(receipt, drain_response(), "draining", &env.id);
8595    }
8596    let budget = match admit_tenant_budget(&st, &tenant, &mut request) {
8597        Ok(budget) => budget,
8598        Err(rejection) => {
8599            let (response, error_code) = rejection.into_response();
8600            let receipt = start_request_receipt(
8601                &st,
8602                &env,
8603                &tenant,
8604                &req.model,
8605                "/v1/completions",
8606                lane,
8607                req.stream,
8608                effective_max_tokens(&request),
8609                None,
8610                None,
8611            );
8612            return ledger_rejected(receipt, response, error_code, &env.id);
8613        }
8614    };
8615    let receipt = start_request_receipt(
8616        &st,
8617        &env,
8618        &tenant,
8619        &req.model,
8620        "/v1/completions",
8621        lane,
8622        req.stream,
8623        effective_max_tokens(&request),
8624        budget.reserved_ctx,
8625        budget.permit,
8626    );
8627    let receipt = arm_capture(receipt, || json!({ "prompt": req.prompt }));
8628    // RATE-LIMIT SNAPSHOT (gap-scan F12): take the in-flight slot at submission time;
8629    // the guard rides the response (stream included) and frees the slot at completion.
8630    let (guard, rl) = match acquire_request_slot(&st, lane, &tenant, &env) {
8631        Ok(slot) => slot,
8632        Err(resp) => {
8633            return ledger_rejected(receipt, resp, "rate_limit_exceeded", &env.id);
8634        }
8635    };
8636    if let Some(admission) = body_admission.as_mut() {
8637        admission.release();
8638    }
8639    // BACKPRESSURE (lane/deadline-billing): shed at submission — never after — when the
8640    // queue is at its bound or the estimated wait cannot fit the request's deadline.
8641    let pending_admit = match reserve_pending_admit(&st, lane, &rl, deadline) {
8642        Ok(guard) => guard,
8643        Err((resp, outcome)) => {
8644            return ledger_unbilled(receipt, rl.attach(resp), outcome, outcome, &env.id);
8645        }
8646    };
8647    meter_admit(&env, &tenant, &model, lane);
8648    let stop_strings = request.stop_strings.clone();
8649
8650    // Admission yield (lane/admission-latency): raise the pending-admit gauge BEFORE the
8651    // send — an in-flight spec burst polls it at every round boundary and ends early so
8652    // this request's admission wait stops scaling with MEMRA_SPEC_BURST. The worker
8653    // decrements at pop (handle_cmd).
8654    if let Some(trace) = ttft.as_ref() {
8655        trace.mark_submitted();
8656    }
8657    if st.cmd_tx.send(Cmd::Generate(Box::new(request))).is_err() {
8658        drop(pending_admit);
8659        return ledger_rejected(
8660            receipt,
8661            rl.attach(worker_unavailable_response()),
8662            "worker_unavailable",
8663            &env.id,
8664        );
8665    }
8666    pending_admit.commit();
8667    // DEADLINE: the admission wait counts against timeout_ms (a queued request that can
8668    // no longer answer in time is a miss). Dropping rx on a miss IS the cancel — the
8669    // worker prunes closed-channel requests still queued at the next tick.
8670    let rx = match tokio::time::timeout_at(deadline.at, peek_admission(rx)).await {
8671        Ok(Ok(rx)) => rx,
8672        Ok(Err((resp, error_code))) => {
8673            return ledger_rejected(receipt, rl.attach(resp), error_code, &env.id);
8674        }
8675        Err(_) => {
8676            return ledger_unbilled(
8677                receipt,
8678                rl.attach(deadline_exceeded_response(deadline.ms, stream)),
8679                "deadline_exceeded",
8680                "deadline_exceeded",
8681                &env.id,
8682            );
8683        }
8684    };
8685
8686    let resp = if stream {
8687        // Streaming: timeout_ms bounds TIME-TO-FIRST-TOKEN only. Once the first token has
8688        // streamed the parameter is spent — a client that walks away mid-stream is the
8689        // existing "abandoned" path (user fault, partial billed, owner-ratified).
8690        let rx = match peek_first_token(rx, deadline).await {
8691            Ok(rx) => rx,
8692            Err(()) => {
8693                return ledger_unbilled(
8694                    receipt,
8695                    rl.attach(deadline_exceeded_response(deadline.ms, true)),
8696                    "deadline_exceeded",
8697                    "deadline_exceeded",
8698                    &env.id,
8699                );
8700            }
8701        };
8702        sse_response_with_receipt(
8703            rx,
8704            model,
8705            false,
8706            None,
8707            env.clone(),
8708            stop_strings,
8709            Some(guard),
8710            receipt,
8711        )
8712        .into_response()
8713    } else {
8714        // Non-streaming: the deadline is handled INSIDE the collector, which delivers what
8715        // was generated (billed) instead of discarding it. The old shape here was
8716        // `timeout_at(deadline.at, collect)`, whose miss dropped the future and threw away
8717        // up to 90 s of tokens to answer a 408 — the 2026-08-26 customer report. A
8718        // zero-token miss still answers 408 unbilled, from in there.
8719        let mut receipt = receipt;
8720        let resp = blocking_response_with_receipt(
8721            rx,
8722            model,
8723            false,
8724            stop_strings,
8725            None,
8726            env.clone(),
8727            &mut receipt,
8728            Some(deadline),
8729        )
8730        .await;
8731        drop(guard); // response complete or cut — free the slot before headers
8732        resp.into_response()
8733    };
8734    rl.attach(with_request_id(&env.id, resp))
8735}
8736
8737async fn chat_completions_admitted(
8738    state: State<AppState>,
8739    headers: axum::http::HeaderMap,
8740    trace: Option<Extension<TtftRequestTrace>>,
8741    AdmittedJson(req, admission): AdmittedJson<ChatCompletionReq>,
8742) -> Response {
8743    chat_completions_with_admission(state, headers, trace, Json(req), Some(admission)).await
8744}
8745
8746#[cfg(test)]
8747async fn chat_completions(
8748    State(st): State<AppState>,
8749    headers: axum::http::HeaderMap,
8750    trace: Option<Extension<TtftRequestTrace>>,
8751    request: Json<ChatCompletionReq>,
8752) -> Response {
8753    chat_completions_with_admission(State(st), headers, trace, request, None).await
8754}
8755
8756async fn chat_completions_with_admission(
8757    State(st): State<AppState>,
8758    headers: axum::http::HeaderMap,
8759    trace: Option<Extension<TtftRequestTrace>>,
8760    Json(mut req): Json<ChatCompletionReq>,
8761    mut body_admission: Option<BodyAdmissionLease>,
8762) -> Response {
8763    let env = Envelope::new(true);
8764    // Canonicalize before ANY downstream use: metadata limits, caps, cache namespace, ledger
8765    // pricing and the worker's roster all key off this id and must agree on one spelling.
8766    // An id that resolves to nothing refuses HERE — before budget admission (see
8767    // model_not_found_response for why the ordering is the whole point).
8768    match canonical_model_id(&st.models, &req.model) {
8769        Some(canonical) => req.model = canonical,
8770        None => {
8771            return with_request_id(&env.id, model_not_found_response(&st.models, &req.model));
8772        }
8773    }
8774    let ttft = trace.and_then(|Extension(trace)| trace.0);
8775    if let Some(trace) = ttft.as_ref() {
8776        trace.mark_parsed();
8777        trace.bind_request(&env.id, &req.model);
8778    }
8779    let tenant = match authenticate(&st.api_auth, &headers) {
8780        Ok(t) => t,
8781        Err(resp) => return with_request_id(&env.id, resp),
8782    };
8783    let cache_ns = match tenant_namespace(&tenant, &req.cache_salt) {
8784        Ok(ns) => ns,
8785        Err(msg) => return with_request_id(&env.id, bad_request(msg, Some("cache_salt"))),
8786    };
8787    if req.messages.is_empty()
8788        || req.messages.iter().any(|message| {
8789            !matches!(
8790                message.role.as_str(),
8791                "system" | "developer" | "user" | "assistant" | "tool"
8792            )
8793        })
8794    {
8795        return with_request_id(
8796            &env.id,
8797            bad_request(
8798                "messages must use system/developer/user/assistant/tool roles",
8799                Some("messages"),
8800            ),
8801        );
8802    }
8803    // HONESTY GATE (gap-scan F4): semantic params we can't honor 400 loudly, never
8804    // silent downgrades. response_format json_object/json_schema are now REAL
8805    // (constrained decoding, lane/constrained) — parsed below; bad forms 400 with the
8806    // parser's own message.
8807    if let Err((msg, param)) = reject_unsupported(&[
8808        (
8809            "logit_bias",
8810            req.logit_bias.is_some(),
8811            " (device-side sampling has no bias hook yet)",
8812        ),
8813        (
8814            "logprobs",
8815            req.logprobs
8816                .as_ref()
8817                .is_some_and(|v| v.as_bool() != Some(false)),
8818            "",
8819        ),
8820        ("top_logprobs", req.top_logprobs.is_some(), ""),
8821        (
8822            "n",
8823            req.n.is_some_and(|n| n != 1),
8824            " for n != 1 (single choice only)",
8825        ),
8826    ]) {
8827        return with_request_id(&env.id, bad_request(&msg, Some(&param)));
8828    }
8829    // Request deadline (lane/deadline-billing): validated with the other request params
8830    // (a named 400 costs no slot and opens no receipt), armed from this point on.
8831    let deadline = match parse_timeout_ms(req.timeout_ms.as_ref()) {
8832        Ok(ms) => RequestDeadline::starting_now(ms),
8833        Err(msg) => return with_request_id(&env.id, bad_request(&msg, Some("timeout_ms"))),
8834    };
8835    let lane = match lane_for_tenant(&headers, &tenant) {
8836        Ok(l) => l,
8837        Err(resp) => return resp,
8838    };
8839    let model = req.model.clone();
8840    let stream = req.stream;
8841    // Snapshot the capture payload BEFORE the plan build consumes the request. Only
8842    // marked tenants pay for the copy; everyone else gets a lock-read and a None.
8843    let capture_prompt = st
8844        .metering
8845        .as_ref()
8846        .filter(|m| m.captures(&tenant.tenant))
8847        .map(|_| capture_chat_messages(&req.messages));
8848    // Read BEFORE the plan build consumes `req`: the feasibility gate judges only a
8849    // caller-DECLARED max_tokens (an omitted one is resolved to the model max downstream,
8850    // which is not a number the caller chose).
8851    let declared_max_tokens = req.max_tokens.is_some();
8852    // Preprocessing has its own bounded permit. GIFs must be decoded while the plan is built so
8853    // their sampled timestamps can render the prompt, while still images decode later; serializing
8854    // this phase keeps their transient canvases from multiplying outside request admission.
8855    let vision_preprocess_permit = match try_vision_preprocess(request_has_vision(&req)) {
8856        Ok(permit) => permit,
8857        Err(response) => return with_request_id(&env.id, response),
8858    };
8859    let (tx, rx) = worker::event_channel();
8860    let affinity = match affinity_key(&req.session_id, &req.user, &headers) {
8861        Ok(affinity) => affinity,
8862        Err(msg) => return with_request_id(&env.id, bad_request(&msg, Some("session_id"))),
8863    };
8864    let mut plan = match build_chat_request_with_trace(
8865        req,
8866        st.caps.get(&model),
8867        tx,
8868        lane,
8869        affinity,
8870        ttft.clone(),
8871        st.openrouter_metadata
8872            .get(&model)
8873            .and_then(|m| m.default_reasoning_effort.as_deref()),
8874        &st.sampling_defaults(&model),
8875    ) {
8876        Ok(plan) => plan,
8877        Err(err) => {
8878            return with_request_id(&env.id, bad_request(&err, None));
8879        }
8880    };
8881    plan.request.cache_ns = cache_ns;
8882    plan.request.request_id = env.id.clone();
8883    plan.request.wire_deadline = Some(deadline.at.into_std());
8884    if let Err((message, param)) = apply_model_request_limits(
8885        &mut plan.request,
8886        st.openrouter_metadata.get(&model),
8887        st.caps.get(&model),
8888    ) {
8889        return with_request_id(&env.id, bad_request(&message, Some(param)));
8890    }
8891    // FEASIBILITY GATE — same body as the /v1/completions surface (standard-surface law:
8892    // one implementation, every entry path). See nonstream_deadline_gate.
8893    if let Err(msg) = nonstream_deadline_gate(
8894        &plan.request,
8895        stream,
8896        deadline,
8897        declared_max_tokens,
8898        st.budget_tokenizers
8899            .as_ref()
8900            .and_then(|t| t.get(&model))
8901            .map(Arc::as_ref),
8902    ) {
8903        return with_request_id(
8904            &env.id,
8905            error_response_coded(
8906                StatusCode::BAD_REQUEST,
8907                &msg,
8908                "invalid_request_error",
8909                Some("max_tokens"),
8910                Some("nonstream_deadline_infeasible"),
8911            ),
8912        );
8913    }
8914    plan.vision_memory = match reserve_vision_memory(&plan) {
8915        Ok(permit) => permit,
8916        Err(err) => {
8917            return with_request_id(&env.id, vision_memory_error_response(err, Some("messages")));
8918        }
8919    };
8920    // DRAIN GATE (gap-scan F11): preserve the existing shutdown contract before
8921    // consulting tenant balances or touching any slot/queue state.
8922    if draining() {
8923        let receipt = start_request_receipt(
8924            &st,
8925            &env,
8926            &tenant,
8927            &model,
8928            "/v1/chat/completions",
8929            lane,
8930            stream,
8931            effective_max_tokens(&plan.request),
8932            None,
8933            None,
8934        );
8935        return ledger_rejected(receipt, drain_response(), "draining", &env.id);
8936    }
8937    let budget = match admit_tenant_budget(&st, &tenant, &mut plan.request) {
8938        Ok(budget) => budget,
8939        Err(rejection) => {
8940            let (response, error_code) = rejection.into_response();
8941            let receipt = start_request_receipt(
8942                &st,
8943                &env,
8944                &tenant,
8945                &model,
8946                "/v1/chat/completions",
8947                lane,
8948                stream,
8949                effective_max_tokens(&plan.request),
8950                None,
8951                None,
8952            );
8953            return ledger_rejected(receipt, response, error_code, &env.id);
8954        }
8955    };
8956    let receipt = start_request_receipt(
8957        &st,
8958        &env,
8959        &tenant,
8960        &model,
8961        "/v1/chat/completions",
8962        lane,
8963        stream,
8964        effective_max_tokens(&plan.request),
8965        budget.reserved_ctx,
8966        budget.permit,
8967    );
8968    let receipt = if let Some(prompt) = capture_prompt {
8969        arm_capture(receipt, move || prompt)
8970    } else {
8971        receipt
8972    };
8973    // RATE-LIMIT SNAPSHOT (gap-scan F12): slot taken at submission (post-validation —
8974    // a 400 never held a slot); freed when the response completes (guard). It is deliberately
8975    // acquired BEFORE vision decode so a rejected/rate-limited request cannot expand canvases.
8976    let (guard, rl) = match acquire_request_slot(&st, lane, &tenant, &env) {
8977        Ok(slot) => slot,
8978        Err(resp) => {
8979            return ledger_rejected(receipt, resp, "rate_limit_exceeded", &env.id);
8980        }
8981    };
8982    if let Some(admission) = body_admission.as_mut() {
8983        admission.release();
8984    }
8985    // BACKPRESSURE (lane/deadline-billing): shed at submission — never after — when the
8986    // queue is at its bound or the estimated wait cannot fit the request's deadline.
8987    let pending_admit = match reserve_pending_admit(&st, lane, &rl, deadline) {
8988        Ok(guard) => guard,
8989        Err((resp, outcome)) => {
8990            return ledger_unbilled(receipt, rl.attach(resp), outcome, outcome, &env.id);
8991        }
8992    };
8993    // Vision phase 2 (hermes decode-bomb finding, fixed 2026-08-23): the canvases expand
8994    // only HERE — after budget admission and request-slot admission priced the header-planned
8995    // pad runs. The process-wide memory permit moves into the worker request below and survives
8996    // streaming responses until completion/cancellation.
8997    if let Err(err) = decode_pending_vision(&mut plan) {
8998        return ledger_rejected(
8999            receipt,
9000            rl.attach(bad_request(&err, Some("messages"))),
9001            "invalid_request_error",
9002            &env.id,
9003        );
9004    }
9005    plan.request.vision_memory = plan.vision_memory.take();
9006    drop(vision_preprocess_permit);
9007    let constraint_ready = if plan.request.grammar.is_some() {
9008        let (ready_tx, ready_rx) = tokio::sync::oneshot::channel();
9009        plan.request.constraint_ready = Some(ready_tx);
9010        Some(ready_rx)
9011    } else {
9012        None
9013    };
9014    meter_admit(&env, &tenant, &model, lane);
9015    let stop_strings = plan.request.stop_strings.clone();
9016    // Admission yield (lane/admission-latency): gauge up before send — see completions.
9017    if let Some(trace) = ttft.as_ref() {
9018        trace.mark_submitted();
9019    }
9020    if st
9021        .cmd_tx
9022        .send(Cmd::Generate(Box::new(plan.request)))
9023        .is_err()
9024    {
9025        drop(pending_admit);
9026        return ledger_rejected(
9027            receipt,
9028            rl.attach(worker_unavailable_response()),
9029            "worker_unavailable",
9030            &env.id,
9031        );
9032    }
9033    pending_admit.commit();
9034    // A constrained stream must not commit HTTP 200 before its schema has compiled. This wait
9035    // is asynchronous; the compiler runs on its bounded model thread and the GPU worker keeps
9036    // stepping. Timeout/invalid schema therefore remains a clean pre-header 503/400. The wait
9037    // is additionally bounded by the request's own deadline (a sub-5s timeout_ms must not be
9038    // overshot by the compile window).
9039    if let Some(ready) = constraint_ready {
9040        let bound = constrained::CONSTRAINT_COMPILE_TIMEOUT.min(deadline.remaining());
9041        match tokio::time::timeout(bound, ready).await {
9042            Ok(Ok(Ok(()))) => {}
9043            Ok(Ok(Err(err))) => {
9044                return ledger_rejected(
9045                    receipt,
9046                    rl.attach(engine_error_response(&err)),
9047                    engine_error_code(err.class),
9048                    &env.id,
9049                );
9050            }
9051            Ok(Err(_)) => {
9052                return ledger_rejected(
9053                    receipt,
9054                    rl.attach(worker_unavailable_response()),
9055                    "worker_unavailable",
9056                    &env.id,
9057                );
9058            }
9059            Err(_) if deadline.remaining().is_zero() => {
9060                return ledger_unbilled(
9061                    receipt,
9062                    rl.attach(deadline_exceeded_response(deadline.ms, stream)),
9063                    "deadline_exceeded",
9064                    "deadline_exceeded",
9065                    &env.id,
9066                );
9067            }
9068            Err(_) => {
9069                return ledger_rejected(
9070                    receipt,
9071                    rl.attach(engine_error_response(&worker::constraint_timeout_error())),
9072                    "constraint_compile_timeout",
9073                    &env.id,
9074                );
9075            }
9076        }
9077    }
9078    // DEADLINE: the admission wait counts against timeout_ms — see `completions`.
9079    let rx = match tokio::time::timeout_at(deadline.at, peek_admission(rx)).await {
9080        Ok(Ok(rx)) => rx,
9081        Ok(Err((resp, error_code))) => {
9082            return ledger_rejected(receipt, rl.attach(resp), error_code, &env.id);
9083        }
9084        Err(_) => {
9085            return ledger_unbilled(
9086                receipt,
9087                rl.attach(deadline_exceeded_response(deadline.ms, stream)),
9088                "deadline_exceeded",
9089                "deadline_exceeded",
9090                &env.id,
9091            );
9092        }
9093    };
9094    let resp = if stream {
9095        // Streaming: timeout_ms bounds TIME-TO-FIRST-TOKEN only — see `completions`.
9096        let rx = match peek_first_token(rx, deadline).await {
9097            Ok(rx) => rx,
9098            Err(()) => {
9099                return ledger_unbilled(
9100                    receipt,
9101                    rl.attach(deadline_exceeded_response(deadline.ms, true)),
9102                    "deadline_exceeded",
9103                    "deadline_exceeded",
9104                    &env.id,
9105                );
9106            }
9107        };
9108        sse_response_with_receipt(
9109            rx,
9110            model,
9111            true,
9112            plan.parser,
9113            env.clone(),
9114            stop_strings,
9115            Some(guard),
9116            receipt,
9117        )
9118        .into_response()
9119    } else {
9120        // Non-streaming: the deadline is handled INSIDE the collector, which delivers what
9121        // was generated instead of discarding it — see `completions`.
9122        let mut receipt = receipt;
9123        let resp = blocking_response_with_receipt(
9124            rx,
9125            model,
9126            true,
9127            stop_strings,
9128            plan.parser,
9129            env.clone(),
9130            &mut receipt,
9131            Some(deadline),
9132        )
9133        .await;
9134        drop(guard); // response complete or cut — free the slot before headers
9135        resp.into_response()
9136    };
9137    rl.attach(with_request_id(&env.id, resp))
9138}
9139
9140/// Streaming (SSE): forward each Token as an SSE `data:` line; emit a final `done` event.
9141/// `parser`: Some only for tools-armed chat requests — content routes through the tool-call
9142/// parser and parsed calls stream as OpenAI `tool_calls` deltas (one header chunk carrying
9143/// id/type/name, one arguments chunk), with `finish_reason:"tool_calls"` on the final chunk.
9144/// ENVELOPE (gap-scan F1): every OpenAI-shape chunk is stamped with the request's
9145/// id/created/system_fingerprint; the FIRST chat delta carries `role:"assistant"` (SDK
9146/// stream-accumulator contract); mid-stream worker errors go out as a `data:` error chunk
9147/// (OpenAI clients never parse named SSE events) followed by [DONE].
9148#[cfg(test)]
9149fn sse_response(
9150    rx: worker::EventReceiver,
9151    model: String,
9152    chat: bool,
9153    parser: Option<ToolStreamParser>,
9154    env: Envelope,
9155    stop_strings: Vec<String>,
9156    guard: Option<InflightGuard>,
9157) -> Sse<impl futures_core::Stream<Item = Result<SseEvent, std::convert::Infallible>>> {
9158    sse_response_with_receipt(rx, model, chat, parser, env, stop_strings, guard, None)
9159}
9160
9161#[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
9162fn sse_response_with_receipt(
9163    mut rx: worker::EventReceiver,
9164    model: String,
9165    chat: bool,
9166    mut parser: Option<ToolStreamParser>,
9167    env: Envelope,
9168    stop_strings: Vec<String>,
9169    guard: Option<InflightGuard>,
9170    mut receipt: Option<Box<dyn metering::Receipt>>,
9171) -> Sse<impl futures_core::Stream<Item = Result<SseEvent, std::convert::Infallible>>> {
9172    // STOP-LEAK holdback (gap-scan F9), OpenAI shapes only: content deltas buffer until
9173    // they can't start a stop string; matched stop text is excluded exactly like the
9174    // non-stream shape. The memra-native stream stays byte-identical (no scrubber).
9175    let mut scrub = (!stop_strings.is_empty() && (chat || openai_compat()))
9176        .then(|| StopScrubber::new(stop_strings));
9177    let stream = async_stream::stream! {
9178        // in-flight slot rides the stream: freed when the stream completes or the
9179        // client disconnects (drop) — the rate-limit gauge + drain barrier source.
9180        let _guard = guard;
9181        let mut call_index: usize = 0;
9182        // first chat delta carries the role (applied to whatever delta comes first —
9183        // content, reasoning, or the tool-call header).
9184        let mut role_sent = false;
9185        macro_rules! chat_chunk {
9186            ($delta:expr, $finish:expr) => {{
9187                let mut delta = $delta;
9188                if chat && !role_sent {
9189                    role_sent = true;
9190                    delta["role"] = json!("assistant");
9191                }
9192                env.stamp(json!({ "object": "chat.completion.chunk", "model": model,
9193                                  "choices": [{ "index": 0, "delta": delta,
9194                                                "finish_reason": $finish }] }))
9195                    .to_string()
9196            }};
9197        }
9198        // renders Piece -> chat.completion.chunk payloads (tools-armed path only).
9199        macro_rules! piece_chunks {
9200            ($piece:expr) => {{
9201                let mut payloads: Vec<String> = Vec::new();
9202                match $piece {
9203                    Piece::Content(text) => {
9204                        let text = match scrub.as_mut() {
9205                            Some(sc) => sc.push(&text),
9206                            None => text,
9207                        };
9208                        if !text.is_empty() {
9209                            payloads.push(chat_chunk!(json!({ "content": text }),
9210                                                      serde_json::Value::Null));
9211                        }
9212                    }
9213                    // OR reasoning dialect (gap-scan F13): think text streams as
9214                    // delta.reasoning, never as content (stop strings scrub content only,
9215                    // same as the non-stream truncate law).
9216                    Piece::Reasoning(text) => payloads.push(
9217                        chat_chunk!(json!({ "reasoning": text }), serde_json::Value::Null)),
9218                    Piece::Call(call) => {
9219                        payloads.push(chat_chunk!(json!({ "tool_calls": [{
9220                            "index": call_index, "id": call.id, "type": "function",
9221                            "function": { "name": call.name, "arguments": "" } }] }),
9222                            serde_json::Value::Null));
9223                        payloads.push(chat_chunk!(json!({ "tool_calls": [{
9224                            "index": call_index,
9225                            "function": { "arguments": call.arguments } }] }),
9226                            serde_json::Value::Null));
9227                        call_index += 1;
9228                    }
9229                }
9230                payloads
9231            }};
9232        }
9233        // Set by every arm that BREAKS with its receipt handled; false when the loop ends
9234        // because the worker closed the channel without Done/Error (worker restart) — the
9235        // post-loop arm below settles that as rejected, debit zero, never "abandoned".
9236        let mut terminal = false;
9237        while let Some(ev) = rx.recv().await {
9238            match ev {
9239                Event::PromptCapture { .. } => {} // embeddings/rerank surface only
9240                Event::PromptUsage { n_prompt, n_cached } => {
9241                    if let Some(receipt) = receipt.as_mut()
9242                        && let Err(err) = receipt.record_prompt_usage(
9243                            n_prompt as u64,
9244                            n_cached as u64,
9245                        )
9246                    {
9247                        eprintln!(
9248                            "[ledger] ERROR: request {} partial prompt receipt failed: {err}",
9249                            env.id
9250                        );
9251                        // Settle as rejected (best effort) so Drop cannot classify OUR
9252                        // bookkeeping failure as a billable client abandon.
9253                        let _ = receipt.reject(500, "request_ledger_unavailable");
9254                        let payload = request_ledger_error_body().to_string();
9255                        if chat || openai_compat() {
9256                            yield Ok(SseEvent::default().data(payload));
9257                            yield Ok(SseEvent::default().data("[DONE]"));
9258                        } else {
9259                            yield Ok(SseEvent::default().event("error").data(payload));
9260                        }
9261                        terminal = true;
9262                        break;
9263                    }
9264                }
9265                Event::Token { id, text } => {
9266                    if let Some(receipt) = receipt.as_mut()
9267                        && let Err(err) = receipt.record_completion_token()
9268                    {
9269                        eprintln!(
9270                            "[ledger] ERROR: request {} partial completion receipt failed: {err}",
9271                            env.id
9272                        );
9273                        let _ = receipt.reject(500, "request_ledger_unavailable");
9274                        let payload = request_ledger_error_body().to_string();
9275                        if chat || openai_compat() {
9276                            yield Ok(SseEvent::default().data(payload));
9277                            yield Ok(SseEvent::default().data("[DONE]"));
9278                        } else {
9279                            yield Ok(SseEvent::default().event("error").data(payload));
9280                        }
9281                        terminal = true;
9282                        break;
9283                    }
9284                    // Capture accumulates the RAW generated text — before tool parsing
9285                    // and stop-scrub holdback — which is the model output a corpus wants.
9286                    if let Some(receipt) = receipt.as_mut() {
9287                        receipt.capture_completion_delta(&text);
9288                    }
9289                    if let Some(p) = parser.as_mut() {
9290                        for piece in p.push(&text) {
9291                            for payload in piece_chunks!(piece) {
9292                                yield Ok(SseEvent::default().data(payload));
9293                            }
9294                        }
9295                        continue;
9296                    }
9297                    let text = match scrub.as_mut() {
9298                        Some(sc) => sc.push(&text),
9299                        None => text,
9300                    };
9301                    if text.is_empty() && scrub.is_some() {
9302                        continue; // held back (possible stop prefix) or post-stop
9303                    }
9304                    let payload = if chat {
9305                        chat_chunk!(json!({ "content": text }), serde_json::Value::Null)
9306                    } else if openai_compat() {
9307                        env.stamp(json!({ "object": "text_completion", "model": model,
9308                                "choices": [{ "index": 0, "text": text, "finish_reason": null }] }))
9309                            .to_string()
9310                    } else {
9311                        json!({ "model": model, "id": id, "text": text }).to_string()
9312                    };
9313                    yield Ok(SseEvent::default().data(payload));
9314                }
9315                // Blocking native responses use this terminal snapshot to recover every id
9316                // from coalesced speculative rounds. SSE already emitted the corresponding
9317                // text and intentionally has no terminal token-array surface.
9318                Event::TokenSnapshot(_) => {}
9319                Event::Done { stop_reason, n_tokens, n_prompt, n_cached, elapsed_s, spec } => {
9320                    let mut finish = stop_reason_to_finish(&stop_reason);
9321                    if let Some(p) = parser.as_mut() {
9322                        for piece in p.finish() {
9323                            for payload in piece_chunks!(piece) {
9324                                yield Ok(SseEvent::default().data(payload));
9325                            }
9326                        }
9327                        if p.n_calls() > 0 { finish = "tool_calls"; }
9328                    }
9329                    // stop-scrubber flush: held-back text that never became a stop.
9330                    if let Some(sc) = scrub.as_mut() {
9331                        let tail = sc.finish();
9332                        if !tail.is_empty() {
9333                            let payload = if chat {
9334                                chat_chunk!(json!({ "content": tail }),
9335                                            serde_json::Value::Null)
9336                            } else {
9337                                env.stamp(json!({ "object": "text_completion",
9338                                    "model": model,
9339                                    "choices": [{ "index": 0, "text": tail,
9340                                                  "finish_reason": null }] })).to_string()
9341                            };
9342                            yield Ok(SseEvent::default().data(payload));
9343                        }
9344                    }
9345                    if let Some(receipt) = receipt.as_mut()
9346                        && let Err(err) = receipt.complete(
9347                            metering::UsageCounts {
9348                                prompt_tokens: n_prompt as u64,
9349                                cached_prompt_tokens: n_cached as u64,
9350                                completion_tokens: n_tokens as u64,
9351                            },
9352                            elapsed_s,
9353                        )
9354                    {
9355                        eprintln!(
9356                            "[ledger] ERROR: request {} completion receipt failed: {err}",
9357                            env.id
9358                        );
9359                        // A pricing failure inside complete() leaves the receipt
9360                        // unfinalized; settle it rejected (best effort — a no-op when
9361                        // the append itself already latched) so Drop cannot bill it.
9362                        let _ = receipt.reject(500, "request_ledger_unavailable");
9363                        let payload = request_ledger_error_body().to_string();
9364                        if chat || openai_compat() {
9365                            yield Ok(SseEvent::default().data(payload));
9366                            yield Ok(SseEvent::default().data("[DONE]"));
9367                        } else {
9368                            yield Ok(SseEvent::default().event("error").data(payload));
9369                        }
9370                        terminal = true;
9371                        break;
9372                    }
9373                    if chat || openai_compat() {
9374                        let usage = usage_json(n_prompt, n_tokens, n_cached, elapsed_s, spec);
9375                        let fin = if chat {
9376                            let mut v = env.stamp(json!({
9377                                "object": "chat.completion.chunk", "model": model,
9378                                "choices": [{ "index": 0, "delta": {},
9379                                              "finish_reason": finish }],
9380                                "usage": usage }));
9381                            // zero-token stream: the role must still arrive (SDK contract).
9382                            if !role_sent {
9383                                v["choices"][0]["delta"]["role"] = json!("assistant");
9384                            }
9385                            v
9386                        } else {
9387                            env.stamp(json!({ "object": "text_completion", "model": model,
9388                                "choices": [{ "index": 0, "text": "",
9389                                              "finish_reason": finish }],
9390                                "usage": usage }))
9391                        }.to_string();
9392                        yield Ok(SseEvent::default().data(fin));
9393                        yield Ok(SseEvent::default().data("[DONE]"));
9394                    } else {
9395                        let payload = json!({
9396                            "stop_reason": stop_reason, "n_tokens": n_tokens,
9397                            "prompt_tokens": n_prompt, "cached_tokens": n_cached,
9398                            "elapsed_s": elapsed_s
9399                        }).to_string();
9400                        yield Ok(SseEvent::default().event("done").data(payload));
9401                    }
9402                    terminal = true;
9403                    break;
9404                }
9405                Event::Error(err) => {
9406                    // MID-STREAM FAILURE (G6). The response status is already 200 and the
9407                    // headers are gone, so there is no status code left to change: the ONLY
9408                    // honest signal is an error object in the stream followed by closing the
9409                    // connection. Both happen here — the `break` ends the generator, which
9410                    // drops the SSE body and closes.
9411                    //
9412                    // The class-derived type/code now travels with it (previously hardcoded
9413                    // "server_error" for every cause, so a client could not tell an
9414                    // out-of-VRAM from a context-length mistake once streaming had begun).
9415                    let ledger_error = if let Some(receipt) = receipt.as_mut() {
9416                        receipt
9417                            .reject(class_http(err.class).0.as_u16(), engine_error_code(err.class))
9418                            .err()
9419                    } else {
9420                        None
9421                    };
9422                    if let Some(ref ledger_error) = ledger_error {
9423                        eprintln!(
9424                            "[ledger] ERROR: request {} failure receipt failed: {ledger_error}",
9425                            env.id
9426                        );
9427                    }
9428                    let payload = if ledger_error.is_some() {
9429                        request_ledger_error_body().to_string()
9430                    } else {
9431                        engine_error_body(&err).to_string()
9432                    };
9433                    if chat || openai_compat() {
9434                        // OpenAI clients only parse `data:` lines — a named `event: error`
9435                        // reads as a silent hang. Error object as the final data chunk.
9436                        yield Ok(SseEvent::default().data(payload));
9437                        yield Ok(SseEvent::default().data("[DONE]"));
9438                    } else {
9439                        // Native (non-OpenAI) surface keeps its named `error` event: its
9440                        // clients are memra's own tools, which do parse named events.
9441                        yield Ok(SseEvent::default().event("error").data(payload));
9442                    }
9443                    terminal = true;
9444                    break;
9445                }
9446            }
9447        }
9448        if !terminal {
9449            // Channel closed without Done/Error: the worker thread is gone (panicked or
9450            // restarting) — OUR fault, so the receipt settles rejected with debit ZERO
9451            // (fault-attribution ruling 2026-08-23; this used to fall through to Drop and
9452            // bill the partial stream as a client "abandon"), and the failure is LOUD:
9453            // the same error object the blocking path returns, as the final chunk.
9454            let e = worker::EngineError::overloaded(
9455                "worker closed the stream without completing (worker restart in progress)",
9456            );
9457            if let Some(receipt) = receipt.as_mut()
9458                && let Err(ledger_err) = receipt.reject(
9459                    class_http(e.class).0.as_u16(),
9460                    engine_error_code(e.class),
9461                )
9462            {
9463                eprintln!(
9464                    "[ledger] ERROR: request {} closed-stream receipt failed: {ledger_err}",
9465                    env.id
9466                );
9467            }
9468            let payload = engine_error_body(&e).to_string();
9469            if chat || openai_compat() {
9470                yield Ok(SseEvent::default().data(payload));
9471                yield Ok(SseEvent::default().data("[DONE]"));
9472            } else {
9473                yield Ok(SseEvent::default().event("error").data(payload));
9474            }
9475        }
9476    };
9477    Sse::new(stream).keep_alive(
9478        // OR cancels + fails over on silent phases (fetch timeout) — long-prompt prefill
9479        // streams nothing for many seconds before first token. SSE comment every 5s.
9480        axum::response::sse::KeepAlive::new().interval(std::time::Duration::from_secs(5)),
9481    )
9482}
9483
9484/// Blocking JSON: collect all tokens, return one {text, tokens, stop_reason} when done.
9485fn truncate_at_stop(text: &mut String, stop_strings: &[String]) {
9486    if let Some(offset) = stop_strings.iter().filter_map(|stop| text.find(stop)).min() {
9487        text.truncate(offset);
9488    }
9489}
9490
9491/// Longest PROPER prefix of `tag` (on tag char boundaries) that `s` ends with — the
9492/// char-boundary-safe twin of toolcall's ASCII-tag helper (stop strings are client text).
9493fn partial_stop_suffix(s: &str, tag: &str) -> usize {
9494    let mut best = 0;
9495    for (k, _) in tag.char_indices().skip(1) {
9496        if k <= s.len() && s.ends_with(&tag[..k]) {
9497            best = k;
9498        }
9499    }
9500    best
9501}
9502
9503/// STREAMING STOP SCRUBBER (gap-scan F9): the worker emits the token delta BEFORE its
9504/// stop check, so streams used to leak the stop text (and same-token overshoot) that
9505/// non-stream clients never see. Content deltas route through this holdback buffer:
9506/// text is released only once it can no longer be the start of a stop string, and a
9507/// completed stop truncates exactly like the non-stream `truncate_at_stop`.
9508struct StopScrubber {
9509    stops: Vec<String>,
9510    buf: String,
9511    done: bool,
9512}
9513
9514impl StopScrubber {
9515    fn new(stops: Vec<String>) -> Self {
9516        Self {
9517            stops,
9518            buf: String::new(),
9519            done: false,
9520        }
9521    }
9522
9523    /// Feed a content delta; returns the text now safe to emit.
9524    fn push(&mut self, text: &str) -> String {
9525        if self.done {
9526            return String::new();
9527        }
9528        self.buf.push_str(text);
9529        if let Some(i) = self
9530            .stops
9531            .iter()
9532            .filter_map(|s| self.buf.find(s.as_str()))
9533            .min()
9534        {
9535            self.done = true;
9536            let out = self.buf[..i].to_string();
9537            self.buf.clear();
9538            return out;
9539        }
9540        let keep = self
9541            .stops
9542            .iter()
9543            .map(|s| partial_stop_suffix(&self.buf, s))
9544            .max()
9545            .unwrap_or(0);
9546        let emit_to = self.buf.len() - keep;
9547        let out = self.buf[..emit_to].to_string();
9548        self.buf.drain(..emit_to);
9549        out
9550    }
9551
9552    /// End of stream: release held-back text (it never became a stop).
9553    fn finish(&mut self) -> String {
9554        if self.done {
9555            self.buf.clear();
9556            return String::new();
9557        }
9558        std::mem::take(&mut self.buf)
9559    }
9560}
9561
9562#[cfg(test)]
9563async fn blocking_response(
9564    rx: worker::EventReceiver,
9565    model: String,
9566    chat: bool,
9567    stop_strings: Vec<String>,
9568    parser: Option<ToolStreamParser>,
9569    env: Envelope,
9570) -> Response {
9571    blocking_response_with_receipt(rx, model, chat, stop_strings, parser, env, &mut None, None)
9572        .await
9573}
9574
9575/// Everything the non-streaming JSON shapes need. ONE body builds the response for both
9576/// the normal completion and the deadline-partial path, so the two can never drift into
9577/// different shapes for the same surface (standard-surface law).
9578struct BlockingPayload<'a> {
9579    env: &'a Envelope,
9580    model: String,
9581    chat: bool,
9582    finish: &'static str,
9583    text: String,
9584    reasoning: String,
9585    calls: Vec<ParsedToolCall>,
9586    tokens: Vec<u32>,
9587    stop_reason: String,
9588    n_prompt: usize,
9589    n_tokens: usize,
9590    n_cached: usize,
9591    elapsed_s: f64,
9592    spec: Option<worker::SpecUsage>,
9593    /// Set ONLY when the request's deadline landed mid-generation and we are delivering
9594    /// what was produced. Carries the OpenRouter-dialect error object that rides a
9595    /// `finish_reason: "error"` partial, so a caller can tell "cut by time" from "hit
9596    /// max_tokens" — which `finish_reason: "length"` alone cannot say, and which no
9597    /// provider's finish-reason enum has a value for.
9598    deadline_error: Option<serde_json::Value>,
9599}
9600
9601fn blocking_payload(p: BlockingPayload<'_>) -> Response {
9602    let BlockingPayload {
9603        env,
9604        model,
9605        chat,
9606        finish,
9607        text,
9608        reasoning,
9609        calls,
9610        tokens,
9611        stop_reason,
9612        n_prompt,
9613        n_tokens,
9614        n_cached,
9615        elapsed_s,
9616        spec,
9617        deadline_error,
9618    } = p;
9619    if chat {
9620        // OpenAI shape: content is null on a pure tool-call turn.
9621        let content = if !calls.is_empty() && text.is_empty() {
9622            serde_json::Value::Null
9623        } else {
9624            serde_json::Value::String(text)
9625        };
9626        let mut message = json!({ "role": "assistant", "content": content });
9627        // OR reasoning dialect (gap-scan F13): think text is a dedicated
9628        // message field (+ reasoning_details), content is post-think only.
9629        if !reasoning.is_empty() {
9630            message["reasoning"] = json!(reasoning);
9631            message["reasoning_details"] = json!([{
9632                "type": "reasoning.text", "text": reasoning }]);
9633        }
9634        if !calls.is_empty() {
9635            message["tool_calls"] =
9636                serde_json::Value::Array(calls.iter().map(tool_call_json).collect());
9637        }
9638        let mut body = json!({
9639            "object": "chat.completion", "model": model,
9640            "choices": [{ "index": 0,
9641                          "message": message,
9642                          "finish_reason": finish }],
9643            "usage": usage_json(n_prompt, n_tokens, n_cached, elapsed_s, spec)
9644        });
9645        if let Some(err) = deadline_error {
9646            body["choices"][0]["native_finish_reason"] = json!("deadline_exceeded");
9647            body["error"] = err;
9648        }
9649        return Json(env.stamp(body)).into_response();
9650    }
9651    if openai_compat() {
9652        let mut body = json!({
9653            "object": "text_completion", "model": model,
9654            "choices": [{ "index": 0, "text": text,
9655                          "finish_reason": finish }],
9656            "usage": usage_json(n_prompt, n_tokens, n_cached, elapsed_s, spec)
9657        });
9658        if let Some(err) = deadline_error {
9659            body["choices"][0]["native_finish_reason"] = json!("deadline_exceeded");
9660            body["error"] = err;
9661        }
9662        return Json(env.stamp(body)).into_response();
9663    }
9664    Json(CompletionResp {
9665        model,
9666        text,
9667        tokens,
9668        stop_reason,
9669        error: deadline_error,
9670        n_tokens,
9671        prompt_tokens: n_prompt,
9672        cached_tokens: n_cached,
9673        elapsed_s,
9674    })
9675    .into_response()
9676}
9677
9678/// Collect a complete non-streaming response.
9679///
9680/// `receipt` is BORROWED (lane/deadline-billing): it outlives this future so a deadline can
9681/// be settled with a named outcome rather than left to `Drop`, which would classify OUR cut
9682/// as an `abandoned` client. What changed in lane/deadline-partial-20260826 is WHERE the
9683/// deadline is handled and what it settles: no production handler wraps this future in
9684/// `timeout_at` any more (both pass `Some(deadline)` and the race is inside the loop below;
9685/// the `None` path is the `#[cfg(test)]` shim), and a MID-GENERATION miss settles the
9686/// BILLABLE `deadline_partial` because the caller received those tokens. Only a zero-token
9687/// miss settles `deadline_exceeded`, debit zero.
9688///
9689/// `deadline` is the request's own deadline and is handled HERE rather than by wrapping
9690/// this future in `timeout_at`. That wrapper was the 2026-08-26 customer bug: a miss
9691/// DROPPED this future, so every token already generated was discarded and the caller got
9692/// a 408 after the full 90 s (darklanes research/nonstream-deadline-20260826). Now the
9693/// deadline is a race inside the loop: whatever has been generated is DELIVERED, as an
9694/// OpenRouter-dialect partial (`finish_reason: "error"` + an `error` object naming
9695/// `error_type: "timeout"`), and billed for the tokens the caller actually received.
9696///
9697/// `finish_reason: "length"` would have been the cheaper lie: no provider's finish-reason
9698/// enum has a time value (OpenAI, Anthropic, Google and the hosted resellers all mean max_tokens by
9699/// "length"/MAX_TOKENS), so reporting a time cut as "length" tells the caller to ask for
9700/// more tokens when the truth is that it needs to stream. Only a zero-token miss still
9701/// answers 408 unbilled — there is nothing to deliver.
9702#[allow(clippy::too_many_arguments)] // allow: the parameter list mirrors the kernel/FFI/call contract; bundling into a struct is a refactor, not a lint fix
9703async fn blocking_response_with_receipt(
9704    mut rx: worker::EventReceiver,
9705    model: String,
9706    chat: bool,
9707    stop_strings: Vec<String>,
9708    mut parser: Option<ToolStreamParser>,
9709    env: Envelope,
9710    receipt: &mut Option<Box<dyn metering::Receipt>>,
9711    deadline: Option<RequestDeadline>,
9712) -> Response {
9713    let mut text = String::new();
9714    let mut reasoning = String::new();
9715    let mut tokens: Vec<u32> = Vec::new();
9716    let mut calls: Vec<ParsedToolCall> = Vec::new();
9717    let consume = |pieces: Vec<Piece>,
9718                   text: &mut String,
9719                   reasoning: &mut String,
9720                   calls: &mut Vec<ParsedToolCall>| {
9721        for piece in pieces {
9722            match piece {
9723                Piece::Content(t) => text.push_str(&t),
9724                Piece::Reasoning(t) => reasoning.push_str(&t),
9725                Piece::Call(c) => calls.push(c),
9726            }
9727        }
9728    };
9729    // Remembered for the deadline path, which has no Done event to read them from.
9730    let started = std::time::Instant::now();
9731    let mut seen_prompt: usize = 0;
9732    let mut seen_cached: usize = 0;
9733    let mut seen_tokens: usize = 0;
9734    loop {
9735        let ev = match deadline {
9736            Some(d) => tokio::select! {
9737                biased;
9738                ev = rx.recv() => ev,
9739                () = tokio::time::sleep_until(d.at) => {
9740                    // Stop the worker at its next tick by dropping the channel, then
9741                    // deliver what we have.
9742                    drop(rx);
9743                    if seen_tokens == 0 {
9744                        // NAMED outcome, not `rejected`: every sibling deadline path in
9745                        // this server writes `deadline_exceeded`, and a review caught this
9746                        // one-word census regression.
9747                        if let Some(receipt) = receipt.as_mut()
9748                            && let Err(err) = receipt.settle_unbilled(
9749                                "deadline_exceeded",
9750                                StatusCode::REQUEST_TIMEOUT.as_u16(),
9751                                "deadline_exceeded",
9752                            )
9753                        {
9754                            eprintln!(
9755                                "[ledger] ERROR: request {} deadline receipt failed: {err}",
9756                                env.id
9757                            );
9758                            return request_ledger_error_response();
9759                        }
9760                        return deadline_exceeded_response(d.ms, false);
9761                    }
9762                    if let Some(p) = parser.as_mut() {
9763                        consume(p.finish(), &mut text, &mut reasoning, &mut calls);
9764                    }
9765                    truncate_at_stop(&mut text, &stop_strings);
9766                    let elapsed_s = started.elapsed().as_secs_f64();
9767                    // BILLED: the caller received these tokens. The unbilled promise
9768                    // covers a request we failed to answer, not one we answered short.
9769                    if let Some(receipt) = receipt.as_mut()
9770                        && let Err(err) = receipt.complete_deadline_partial(
9771                            metering::UsageCounts {
9772                                prompt_tokens: seen_prompt as u64,
9773                                cached_prompt_tokens: seen_cached as u64,
9774                                completion_tokens: seen_tokens as u64,
9775                            },
9776                            elapsed_s,
9777                        )
9778                    {
9779                        eprintln!(
9780                            "[ledger] ERROR: request {} partial-deadline receipt failed: {err}",
9781                            env.id
9782                        );
9783                        let _ = receipt.reject(500, "request_ledger_unavailable");
9784                        return request_ledger_error_response();
9785                    }
9786                    eprintln!(
9787                        "[deadline] request {} delivered PARTIAL: {} tokens in {:.1}s of a \
9788                         {} ms deadline (prompt {}); non-streaming caller advised to stream",
9789                        env.id, seen_tokens, elapsed_s, d.ms, seen_prompt
9790                    );
9791                    let err_obj = json!({
9792                        "message": format!(
9793                            "deadline of {} ms (timeout_ms; default {}) elapsed mid-generation; \
9794                             the {} tokens produced before the cut are delivered above and are \
9795                             billed. Set \"stream\": true for work this long — a stream's \
9796                             deadline bounds only the time to first token — or lower max_tokens.",
9797                            d.ms, TIMEOUT_MS_DEFAULT, seen_tokens
9798                        ),
9799                        "code": "deadline_exceeded",
9800                        "metadata": { "error_type": "timeout", "provider_name": "memra" }
9801                    });
9802                    return blocking_payload(BlockingPayload {
9803                        env: &env,
9804                        model,
9805                        chat,
9806                        finish: "error",
9807                        text,
9808                        reasoning,
9809                        calls,
9810                        tokens,
9811                        stop_reason: "Deadline".to_string(),
9812                        n_prompt: seen_prompt,
9813                        n_tokens: seen_tokens,
9814                        n_cached: seen_cached,
9815                        elapsed_s,
9816                        spec: None,
9817                        deadline_error: Some(err_obj),
9818                    });
9819                }
9820            },
9821            None => rx.recv().await,
9822        };
9823        let Some(ev) = ev else { break };
9824        match ev {
9825            Event::PromptCapture { .. } => {} // embeddings/rerank surface only
9826            Event::PromptUsage { n_prompt, n_cached } => {
9827                if let Some(receipt) = receipt.as_mut()
9828                    && let Err(err) = receipt.record_prompt_usage(n_prompt as u64, n_cached as u64)
9829                {
9830                    eprintln!(
9831                        "[ledger] ERROR: request {} partial prompt receipt failed: {err}",
9832                        env.id
9833                    );
9834                    // Settle the receipt as rejected (best effort) so its Drop cannot
9835                    // classify OUR bookkeeping failure as a billable client abandon.
9836                    let _ = receipt.reject(500, "request_ledger_unavailable");
9837                    return request_ledger_error_response();
9838                }
9839                seen_prompt = n_prompt;
9840                seen_cached = n_cached;
9841            }
9842            Event::Token { id, text: delta } => {
9843                if let Some(receipt) = receipt.as_mut()
9844                    && let Err(err) = receipt.record_completion_token()
9845                {
9846                    eprintln!(
9847                        "[ledger] ERROR: request {} partial completion receipt failed: {err}",
9848                        env.id
9849                    );
9850                    let _ = receipt.reject(500, "request_ledger_unavailable");
9851                    return request_ledger_error_response();
9852                }
9853                // Raw generated text, pre-parse and pre-stop-truncation (see the SSE twin).
9854                if let Some(receipt) = receipt.as_mut() {
9855                    receipt.capture_completion_delta(&delta);
9856                }
9857                tokens.push(id);
9858                seen_tokens += 1;
9859                match parser.as_mut() {
9860                    Some(p) => consume(p.push(&delta), &mut text, &mut reasoning, &mut calls),
9861                    None => text.push_str(&delta),
9862                }
9863            }
9864            Event::TokenSnapshot(ids) => tokens = ids,
9865            Event::Done {
9866                stop_reason,
9867                n_tokens,
9868                n_prompt,
9869                n_cached,
9870                elapsed_s,
9871                spec,
9872            } => {
9873                if let Some(p) = parser.as_mut() {
9874                    consume(p.finish(), &mut text, &mut reasoning, &mut calls);
9875                }
9876                truncate_at_stop(&mut text, &stop_strings);
9877                let finish = if calls.is_empty() {
9878                    stop_reason_to_finish(&stop_reason)
9879                } else {
9880                    "tool_calls"
9881                };
9882                if let Some(receipt) = receipt.as_mut()
9883                    && let Err(err) = receipt.complete(
9884                        metering::UsageCounts {
9885                            prompt_tokens: n_prompt as u64,
9886                            cached_prompt_tokens: n_cached as u64,
9887                            completion_tokens: n_tokens as u64,
9888                        },
9889                        elapsed_s,
9890                    )
9891                {
9892                    eprintln!(
9893                        "[ledger] ERROR: request {} completion receipt failed: {err}",
9894                        env.id
9895                    );
9896                    // A pricing failure inside complete() leaves the receipt unfinalized;
9897                    // settle it rejected (best effort) so Drop cannot bill OUR failure.
9898                    let _ = receipt.reject(500, "request_ledger_unavailable");
9899                    return request_ledger_error_response();
9900                }
9901                return blocking_payload(BlockingPayload {
9902                    env: &env,
9903                    model,
9904                    chat,
9905                    finish,
9906                    text,
9907                    reasoning,
9908                    calls,
9909                    tokens,
9910                    stop_reason,
9911                    n_prompt,
9912                    n_tokens,
9913                    n_cached,
9914                    elapsed_s,
9915                    spec,
9916                    deadline_error: None,
9917                });
9918            }
9919            Event::Error(err) => {
9920                // G6: the class decides the status. This single line used to be
9921                // `bad_request(&msg, None)` — every CUDA fault, VRAM exhaustion and admission
9922                // shed reported as 400 invalid_request_error, which no SDK retries.
9923                if let Some(receipt) = receipt.as_mut()
9924                    && let Err(ledger_err) = receipt.reject(
9925                        class_http(err.class).0.as_u16(),
9926                        engine_error_code(err.class),
9927                    )
9928                {
9929                    eprintln!(
9930                        "[ledger] ERROR: request {} failure receipt failed: {ledger_err}",
9931                        env.id
9932                    );
9933                    return request_ledger_error_response();
9934                }
9935                return engine_error_response(&err);
9936            }
9937        }
9938    }
9939    // The worker's Event channel closed without a Done or an Error: the worker thread is gone
9940    // (panicked and unrecoverable, or shutting down). 503 + Retry-After, not 500: this is a
9941    // process-level condition the supervisor is already acting on, and a client's retry may
9942    // well land on a restarted process.
9943    let e = worker::EngineError::overloaded(
9944        "worker closed the stream without completing (worker restart in progress)",
9945    );
9946    if let Some(receipt) = receipt.as_mut()
9947        && let Err(ledger_err) =
9948            receipt.reject(class_http(e.class).0.as_u16(), engine_error_code(e.class))
9949    {
9950        eprintln!(
9951            "[ledger] ERROR: request {} closed-stream receipt failed: {ledger_err}",
9952            env.id
9953        );
9954        return request_ledger_error_response();
9955    }
9956    engine_error_response(&e)
9957}
9958
9959#[cfg(test)]
9960mod tests {
9961    use super::*;
9962
9963    /// Multi-item capture requests (`/v1/embeddings` N inputs, `/v1/rerank` N documents)
9964    /// give every capture its own ledger identity under the parent envelope: distinct per
9965    /// index, prefixed by the parent id, same `created`. The ledger keys debits by request
9966    /// id as a replay guard, so siblings sharing the parent id billed as one capture or
9967    /// failed the request (`conflicting budget debits`); see `Envelope::capture_child`.
9968    #[test]
9969    fn capture_children_are_distinct_ledger_identities_under_the_parent_id() {
9970        let parent = Envelope::new(false);
9971        assert!(parent.id.starts_with("cmpl-"));
9972        let a = parent.capture_child(0);
9973        let b = parent.capture_child(1);
9974        let c = parent.capture_child(2);
9975        assert_eq!(a.id, format!("{}.0", parent.id));
9976        assert_eq!(b.id, format!("{}.1", parent.id));
9977        assert_eq!(c.id, format!("{}.2", parent.id));
9978        assert_ne!(a.id, b.id);
9979        assert_ne!(b.id, c.id);
9980        for child in [&a, &b, &c] {
9981            assert!(
9982                child.id.starts_with(&parent.id),
9983                "child nests under the parent by prefix"
9984            );
9985            assert_ne!(
9986                child.id, parent.id,
9987                "a child never reuses the parent's ledger id"
9988            );
9989            assert_eq!(child.created, parent.created);
9990        }
9991        // The same index always derives the same child: a retry of one capture stays a
9992        // replay to the ledger instead of a fresh debit.
9993        assert_eq!(parent.capture_child(1).id, b.id);
9994    }
9995
9996    /// What the handler is OBLIGED to tell any metering implementation, recorded as a
9997    /// flat event log. These tests used to run the in-tree prepaid ledger and assert
9998    /// its JSONL rows; that implementation is a deployment concern now (only the
9999    /// engine is open), so the public teeth assert the SEAM CALLS — which terminal
10000    /// method fired, with which worker-truth counts. Row/money assertions live with
10001    /// the implementation, and the cross-binary billing parity battery covers the
10002    /// composed behavior end to end.
10003    #[derive(Debug, Clone, PartialEq)]
10004    enum MeterEvent {
10005        Reserve {
10006            tenant: String,
10007            principal: Option<String>,
10008            model: String,
10009        },
10010        Open {
10011            request_id: String,
10012            tenant: String,
10013            model: String,
10014            route: &'static str,
10015            stream: bool,
10016            with_permit: bool,
10017        },
10018        PromptUsage {
10019            prompt: u64,
10020            cached: u64,
10021        },
10022        Token,
10023        CapturePrompt(serde_json::Value),
10024        CaptureDelta(String),
10025        Complete {
10026            prompt: u64,
10027            cached: u64,
10028            completion: u64,
10029        },
10030        DeadlinePartial {
10031            prompt: u64,
10032            cached: u64,
10033            completion: u64,
10034        },
10035        Reject {
10036            status: u16,
10037            code: String,
10038        },
10039        Unbilled {
10040            outcome: &'static str,
10041            status: u16,
10042            code: String,
10043        },
10044        /// The receipt died unfinalized — the abandoned-client path. The counts are
10045        /// whatever the handler had recorded by then.
10046        Dropped {
10047            prompt: u64,
10048            cached: u64,
10049            completion: u64,
10050        },
10051    }
10052
10053    /// Scripted admission answers, consumed in order; an empty script admits with no
10054    /// permit (the "limits off / nothing reserved" shape).
10055    enum ReserveScript {
10056        Admit { with_permit: bool },
10057        Insufficient,
10058        Blocked,
10059        PrincipalCapped,
10060    }
10061
10062    struct MockMetering {
10063        events: Arc<std::sync::Mutex<Vec<MeterEvent>>>,
10064        limits: bool,
10065        limited: bool,
10066        reserve_script: std::sync::Mutex<std::collections::VecDeque<ReserveScript>>,
10067        captures: bool,
10068    }
10069
10070    impl MockMetering {
10071        fn admit_all() -> Arc<Self> {
10072            Arc::new(MockMetering {
10073                events: Arc::new(std::sync::Mutex::new(Vec::new())),
10074                limits: false,
10075                limited: true,
10076                reserve_script: std::sync::Mutex::new(std::collections::VecDeque::new()),
10077                captures: false,
10078            })
10079        }
10080
10081        fn with_limits(script: Vec<ReserveScript>) -> Arc<Self> {
10082            Arc::new(MockMetering {
10083                events: Arc::new(std::sync::Mutex::new(Vec::new())),
10084                limits: true,
10085                limited: true,
10086                reserve_script: std::sync::Mutex::new(script.into()),
10087                captures: false,
10088            })
10089        }
10090
10091        fn capturing() -> Arc<Self> {
10092            Arc::new(MockMetering {
10093                events: Arc::new(std::sync::Mutex::new(Vec::new())),
10094                limits: false,
10095                limited: true,
10096                reserve_script: std::sync::Mutex::new(std::collections::VecDeque::new()),
10097                captures: true,
10098            })
10099        }
10100
10101        fn events(&self) -> Vec<MeterEvent> {
10102            self.events.lock().unwrap().clone()
10103        }
10104    }
10105
10106    impl metering::Metering for MockMetering {
10107        fn enforces_limits(&self) -> bool {
10108            self.limits
10109        }
10110
10111        fn is_limited(&self, _tenant: &str) -> Result<bool, metering::AdmitError> {
10112            Ok(self.limited)
10113        }
10114
10115        fn reserve(
10116            &self,
10117            tenant: &str,
10118            principal: Option<&str>,
10119            model: &str,
10120            _prompt_tokens: u64,
10121            _completion_bound: u64,
10122        ) -> Result<Option<metering::Permit>, metering::AdmitError> {
10123            self.events.lock().unwrap().push(MeterEvent::Reserve {
10124                tenant: tenant.into(),
10125                principal: principal.map(str::to_owned),
10126                model: model.into(),
10127            });
10128            match self.reserve_script.lock().unwrap().pop_front() {
10129                None | Some(ReserveScript::Admit { with_permit: false }) => Ok(None),
10130                Some(ReserveScript::Admit { with_permit: true }) => {
10131                    Ok(Some(Box::new(()) as metering::Permit))
10132                }
10133                Some(ReserveScript::Insufficient) => Err(metering::AdmitError::Insufficient),
10134                Some(ReserveScript::Blocked) => Err(metering::AdmitError::Blocked),
10135                Some(ReserveScript::PrincipalCapped) => Err(metering::AdmitError::PrincipalCapped),
10136            }
10137        }
10138
10139        fn open(
10140            &self,
10141            meta: &metering::RequestMeta<'_>,
10142            permit: Option<metering::Permit>,
10143        ) -> Box<dyn metering::Receipt> {
10144            self.events.lock().unwrap().push(MeterEvent::Open {
10145                request_id: meta.request_id.into(),
10146                tenant: meta.tenant.into(),
10147                model: meta.model.into(),
10148                route: meta.route,
10149                stream: meta.stream,
10150                with_permit: permit.is_some(),
10151            });
10152            Box::new(MockReceipt {
10153                events: self.events.clone(),
10154                wants_capture: self.captures,
10155                prompt: 0,
10156                cached: 0,
10157                completion: 0,
10158                finalized: false,
10159            })
10160        }
10161
10162        fn captures(&self, _tenant: &str) -> bool {
10163            self.captures
10164        }
10165
10166        fn limits_health(&self) -> Option<metering::LimitsHealth> {
10167            self.limits.then_some(metering::LimitsHealth {
10168                source_reload_failed: 0,
10169                source_reload_consecutive: 0,
10170                source_available: true,
10171            })
10172        }
10173    }
10174
10175    struct MockReceipt {
10176        events: Arc<std::sync::Mutex<Vec<MeterEvent>>>,
10177        wants_capture: bool,
10178        prompt: u64,
10179        cached: u64,
10180        completion: u64,
10181        finalized: bool,
10182    }
10183
10184    impl metering::Receipt for MockReceipt {
10185        fn wants_capture(&self) -> bool {
10186            self.wants_capture
10187        }
10188
10189        fn arm_capture(&mut self, prompt: serde_json::Value) {
10190            self.events
10191                .lock()
10192                .unwrap()
10193                .push(MeterEvent::CapturePrompt(prompt));
10194        }
10195
10196        fn capture_completion_delta(&mut self, text: &str) {
10197            if self.wants_capture {
10198                self.events
10199                    .lock()
10200                    .unwrap()
10201                    .push(MeterEvent::CaptureDelta(text.into()));
10202            }
10203        }
10204
10205        fn record_prompt_usage(&mut self, prompt: u64, cached: u64) -> Result<(), String> {
10206            self.prompt = prompt;
10207            self.cached = cached;
10208            self.events
10209                .lock()
10210                .unwrap()
10211                .push(MeterEvent::PromptUsage { prompt, cached });
10212            Ok(())
10213        }
10214
10215        fn record_completion_token(&mut self) -> Result<(), String> {
10216            self.completion += 1;
10217            self.events.lock().unwrap().push(MeterEvent::Token);
10218            Ok(())
10219        }
10220
10221        fn complete(
10222            &mut self,
10223            usage: metering::UsageCounts,
10224            _worker_elapsed_s: f64,
10225        ) -> Result<(), String> {
10226            self.finalized = true;
10227            self.events.lock().unwrap().push(MeterEvent::Complete {
10228                prompt: usage.prompt_tokens,
10229                cached: usage.cached_prompt_tokens,
10230                completion: usage.completion_tokens,
10231            });
10232            Ok(())
10233        }
10234
10235        fn complete_deadline_partial(
10236            &mut self,
10237            usage: metering::UsageCounts,
10238            _worker_elapsed_s: f64,
10239        ) -> Result<(), String> {
10240            self.finalized = true;
10241            self.events
10242                .lock()
10243                .unwrap()
10244                .push(MeterEvent::DeadlinePartial {
10245                    prompt: usage.prompt_tokens,
10246                    cached: usage.cached_prompt_tokens,
10247                    completion: usage.completion_tokens,
10248                });
10249            Ok(())
10250        }
10251
10252        fn reject(&mut self, status: u16, error_code: &str) -> Result<(), String> {
10253            self.finalized = true;
10254            self.events.lock().unwrap().push(MeterEvent::Reject {
10255                status,
10256                code: error_code.into(),
10257            });
10258            Ok(())
10259        }
10260
10261        fn settle_unbilled(
10262            &mut self,
10263            outcome: &'static str,
10264            status: u16,
10265            error_code: &str,
10266        ) -> Result<(), String> {
10267            self.finalized = true;
10268            self.events.lock().unwrap().push(MeterEvent::Unbilled {
10269                outcome,
10270                status,
10271                code: error_code.into(),
10272            });
10273            Ok(())
10274        }
10275    }
10276
10277    impl Drop for MockReceipt {
10278        fn drop(&mut self) {
10279            if !self.finalized {
10280                self.events.lock().unwrap().push(MeterEvent::Dropped {
10281                    prompt: self.prompt,
10282                    cached: self.cached,
10283                    completion: self.completion,
10284                });
10285            }
10286        }
10287    }
10288
10289    /// Serializes every test that READS or FLIPS `MEMRA_NONSTREAM_DEADLINE_GATE`. The
10290    /// off-switch arm mutates process-global env, and the other gate tests call the gate and
10291    /// would observe that mutation if they ran in parallel — DRAIN_LOCK does not cover them
10292    /// because they have no reason to touch the drain flag. Flagged by review.
10293    static GATE_ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
10294
10295    /// Acquire GATE_ENV_LOCK surviving a poisoned peer, and restore the baseline it
10296    /// guards: `MEMRA_NONSTREAM_DEADLINE_GATE` unset (the documented default). The
10297    /// off-switch arm can panic between its `set_var` and its `remove_var`, and a plain
10298    /// `.unwrap()` would then hand every peer a PoisonError — the DRAIN_LOCK cascade of
10299    /// 2026-09-01 (one flake, 21 reds), same class. Recovery is sound because the env
10300    /// var is the only state under this lock and this resets it.
10301    fn gate_env_lock() -> std::sync::MutexGuard<'static, ()> {
10302        let guard = GATE_ENV_LOCK.lock().unwrap_or_else(|poisoned| {
10303            // Un-latch the flag too: poison otherwise persists forever, and only call
10304            // sites routed through this helper would survive it.
10305            GATE_ENV_LOCK.clear_poison();
10306            poisoned.into_inner()
10307        });
10308        unsafe { std::env::remove_var("MEMRA_NONSTREAM_DEADLINE_GATE") };
10309        guard
10310    }
10311
10312    /// A Request shaped for the feasibility-gate tests: `max_new` declared, prompt given as
10313    /// raw ids so the estimate is exact rather than a byte proxy.
10314    fn gate_request(max_new: usize, prompt_ids: usize) -> worker::Request {
10315        let req: CompletionReq = serde_json::from_value(json!({
10316            "model": "qwen/qwen3.8-27b",
10317            "prompt_ids": vec![7u32; prompt_ids],
10318        }))
10319        .unwrap();
10320        let (tx, _rx) = worker::event_channel();
10321        let mut request = build_request(&req, tx, lanes::Lane::Interactive, None);
10322        request.params.max_new = max_new;
10323        request
10324    }
10325
10326    /// The gate's boundary must sit where the MEASURED ladder sits. Numbers from
10327    /// darklanes research/nonstream-deadline-20260826, 30,278-token prompt through the
10328    /// customer path: 4096 out took 52.0 s, 5120 61.9 s, 6144 71.5 s (all 200), 8192
10329    /// 90.7 s and 16384 91.5 s (both 408). So the gate must ALLOW up to 6144 and REFUSE
10330    /// 8192 and 16384 — a gate that refuses 6144 would break a request that works, and one
10331    /// that allows 16384 would keep the bug.
10332    #[test]
10333    fn the_feasibility_gate_boundary_matches_the_measured_ladder() {
10334        let prompt = 30_278u64;
10335        let deadline_ms = TIMEOUT_MS_DEFAULT;
10336        let margin = |max_new: u64| {
10337            let prefill_ms = prompt * 1_000 / PREFILL_FLOOR_TOK_S;
10338            let decode_ms = max_new * 1_000 / DECODE_FLOOR_TOK_S;
10339            (prefill_ms + decode_ms) <= deadline_ms * DEADLINE_INFEASIBLE_MARGIN_PCT / 100
10340        };
10341        for allowed in [64u64, 2048, 4096, 5120, 6144] {
10342            assert!(margin(allowed), "{allowed} measured OK and must be allowed");
10343        }
10344        for refused in [8192u64, 16384, 262_144] {
10345            assert!(
10346                !margin(refused),
10347                "{refused} measured as a 408 and must be refused"
10348            );
10349        }
10350    }
10351
10352    #[test]
10353    fn the_gate_names_a_max_tokens_that_actually_fits() {
10354        // At 30k prompt the floors leave ~75 s of decode inside a 90 s deadline, so the
10355        // advice must be a positive number well under the measured 7.8k ceiling.
10356        let fits = deadline_fitting_max_tokens(30_278, TIMEOUT_MS_DEFAULT).unwrap();
10357        assert!(
10358            fits > 0 && fits < 7_800,
10359            "advice {fits} must fit the measured ceiling"
10360        );
10361        // A prompt so large that prefill alone eats the deadline has NO feasible length.
10362        assert_eq!(
10363            deadline_fitting_max_tokens(400_000, TIMEOUT_MS_DEFAULT),
10364            None
10365        );
10366    }
10367
10368    #[test]
10369    fn streaming_is_never_gated_and_the_gate_can_be_switched_off() {
10370        let req = gate_request(262_144, 30_000);
10371        let deadline = RequestDeadline::starting_now(TIMEOUT_MS_DEFAULT);
10372        // Non-streaming: refused, and the message has to be actionable, not just "no".
10373        let err = nonstream_deadline_gate(&req, false, deadline, true, None).unwrap_err();
10374        assert!(
10375            err.contains("stream"),
10376            "message must name the streaming alternative: {err}"
10377        );
10378        assert!(
10379            err.contains("max_tokens"),
10380            "message must name the knob: {err}"
10381        );
10382        // Streaming: the same request is fine — its deadline bounds only first-token time.
10383        assert!(nonstream_deadline_gate(&req, true, deadline, true, None).is_ok());
10384        // THE OFF SWITCH, ACTUALLY EXERCISED. This test's NAME claimed this behaviour while
10385        // asserting only the streaming half, and the seam was in fact DEAD: the flag was read
10386        // through a positive-only numeric reader, so `=0` fell back to the default and the
10387        // gate kept firing. The bench gate found it (arm 7 ran with the flag set to 0 and was
10388        // still refused); this arm is why it cannot come back.
10389        let _l = gate_env_lock(); // mutates process env
10390        for off in ["0", "off", "false"] {
10391            unsafe { std::env::set_var("MEMRA_NONSTREAM_DEADLINE_GATE", off) };
10392            assert!(
10393                nonstream_deadline_gate(&req, false, deadline, true, None).is_ok(),
10394                "MEMRA_NONSTREAM_DEADLINE_GATE={off} must disable the gate"
10395            );
10396        }
10397        unsafe { std::env::set_var("MEMRA_NONSTREAM_DEADLINE_GATE", "1") };
10398        assert!(nonstream_deadline_gate(&req, false, deadline, true, None).is_err());
10399        unsafe { std::env::remove_var("MEMRA_NONSTREAM_DEADLINE_GATE") };
10400        assert!(
10401            nonstream_deadline_gate(&req, false, deadline, true, None).is_err(),
10402            "unset means ON (the documented default)"
10403        );
10404    }
10405
10406    /// TEETH FOR THE STANDARD-SURFACE CLAIM. The first version of this lane wired the
10407    /// feasibility gate into /v1/completions and /v1/chat/completions only, while its own
10408    /// comment claimed "one implementation, every entry path" — /v1/messages and
10409    /// /v1/responses kept the discard-and-408 shape. A review caught it. This asserts the
10410    /// call is present on the translated surfaces' SHARED admission body too, read from
10411    /// comment-stripped source so a mention in prose cannot satisfy it.
10412    #[test]
10413    fn the_feasibility_gate_is_wired_on_every_surface_not_just_the_two_i_remembered() {
10414        // Comment-stripped so a mention in prose cannot satisfy this, and scoped to each
10415        // HANDLER BODY so the gate's own definition, this test's needle literal, and the
10416        // test-module calls cannot satisfy it either. The first version asserted only
10417        // `source.contains(needle)`, which could never fail while the function existed in the
10418        // file at all — a review caught it, and it is the wiring-assertions-match-prose trap
10419        // this repo has been bitten by before.
10420        let strip = |src: &str| -> String {
10421            src.lines()
10422                .map(|line| match line.find("//") {
10423                    Some(i) => line[..i].to_string(),
10424                    None => line.to_string(),
10425                })
10426                .collect::<Vec<_>>()
10427                .join("\n")
10428        };
10429        /// The slice from a function's signature to the start of the next top-level item.
10430        fn body<'a>(src: &'a str, signature: &str) -> &'a str {
10431            let start = src
10432                .find(signature)
10433                .unwrap_or_else(|| panic!("{signature} not found — did the handler get renamed?"));
10434            let rest = &src[start + signature.len()..];
10435            let end = rest.find("\nasync fn ").unwrap_or(rest.len());
10436            let end = rest[..end].find("\npub(crate) async fn ").unwrap_or(end);
10437            &rest[..end]
10438        }
10439        let main_src = strip(include_str!("lib.rs"));
10440        let surfaces_src = strip(include_str!("surfaces.rs"));
10441        for (surface, src, signature) in [
10442            (
10443                "/v1/completions",
10444                &main_src,
10445                "async fn completions_with_admission(",
10446            ),
10447            (
10448                "/v1/chat/completions",
10449                &main_src,
10450                "async fn chat_completions_with_admission(",
10451            ),
10452            (
10453                "/v1/messages + /v1/responses (shared admission)",
10454                &surfaces_src,
10455                "pub(crate) async fn admit_translated(",
10456            ),
10457        ] {
10458            let handler = body(src, signature);
10459            assert!(
10460                handler.contains("nonstream_deadline_gate("),
10461                "{surface} must CALL the feasibility gate inside {signature}"
10462            );
10463            // And it must run AFTER the model limits resolve max_tokens, or it would judge a
10464            // cap that does not exist yet.
10465            let limits = handler
10466                .find("apply_model_request_limits(")
10467                .unwrap_or_else(|| panic!("{surface}: no apply_model_request_limits call"));
10468            let gate = handler.find("nonstream_deadline_gate(").unwrap();
10469            assert!(
10470                limits < gate,
10471                "{surface}: the gate must run after apply_model_request_limits"
10472            );
10473        }
10474    }
10475
10476    /// The native (non-OpenAI) response shape must carry the deadline signal too. The first
10477    /// version of `blocking_payload` dropped the error object on that branch, so a cut
10478    /// response looked complete apart from an undocumented stop_reason — flagged by review.
10479    #[test]
10480    fn the_native_shape_carries_the_deadline_error_and_omits_it_otherwise() {
10481        let err = json!({"code": "deadline_exceeded",
10482                         "metadata": {"error_type": "timeout"}});
10483        let cut = CompletionResp {
10484            model: "m".into(),
10485            text: "partial".into(),
10486            tokens: vec![1, 2],
10487            stop_reason: "Deadline".into(),
10488            error: Some(err.clone()),
10489            n_tokens: 2,
10490            prompt_tokens: 9,
10491            cached_tokens: 0,
10492            elapsed_s: 1.0,
10493        };
10494        let v = serde_json::to_value(&cut).unwrap();
10495        assert_eq!(v["stop_reason"], "Deadline");
10496        assert_eq!(v["error"]["code"], "deadline_exceeded");
10497        assert_eq!(v["error"]["metadata"]["error_type"], "timeout");
10498        // A normal completion must be byte-unchanged: no `error` key at all.
10499        let whole = CompletionResp {
10500            error: None,
10501            stop_reason: "Eos".into(),
10502            ..cut
10503        };
10504        let v = serde_json::to_value(&whole).unwrap();
10505        assert!(
10506            v.get("error").is_none(),
10507            "a complete response must not grow an error key: {v}"
10508        );
10509    }
10510
10511    #[test]
10512    fn a_ctx_bounded_request_is_not_gated_because_context_is_its_only_limit() {
10513        let _l = gate_env_lock();
10514        // Owner ruling 2026-08-26: "or limit is full context". A caller who sent no
10515        // max_tokens has declared no length for the gate to judge; partial delivery covers
10516        // it instead of a refusal the caller cannot act on.
10517        let req = gate_request(worker::MAX_NEW_CTX_BOUNDED, 30_000);
10518        assert!(
10519            nonstream_deadline_gate(
10520                &req,
10521                false,
10522                RequestDeadline::starting_now(TIMEOUT_MS_DEFAULT),
10523                false,
10524                None,
10525            )
10526            .is_ok(),
10527            "an omitted max_tokens is never gated — context is its only limit"
10528        );
10529        // THE BENCH-GATE DEFECT, pinned: a request whose omitted cap has already been
10530        // RESOLVED to the model maximum must still not be gated. Before this, the gate saw
10531        // a concrete 32768 it thought the caller had chosen and 400'd the most common
10532        // customer shape (arm 5, darklanes research/nonstream-deadline-20260826).
10533        let resolved = gate_request(32_768, 30_000);
10534        assert!(
10535            nonstream_deadline_gate(
10536                &resolved,
10537                false,
10538                RequestDeadline::starting_now(TIMEOUT_MS_DEFAULT),
10539                false,
10540                None,
10541            )
10542            .is_ok(),
10543            "a resolved-but-undeclared cap is not the caller's number to be refused over"
10544        );
10545        // And a caller who DID declare that cap on the same prompt IS refused.
10546        assert!(
10547            nonstream_deadline_gate(
10548                &resolved,
10549                false,
10550                RequestDeadline::starting_now(TIMEOUT_MS_DEFAULT),
10551                true,
10552                None,
10553            )
10554            .is_err()
10555        );
10556    }
10557
10558    #[test]
10559    fn the_prompt_estimate_is_exact_for_ids_and_a_proxy_otherwise() {
10560        let req = gate_request(64, 1234);
10561        assert_eq!(prompt_tokens_estimate(&req, None), 1234, "ids are exact");
10562        let mut text = gate_request(64, 0);
10563        text.prompt_ids.clear();
10564        text.prompt_text = "x".repeat(6_000);
10565        assert_eq!(
10566            prompt_tokens_estimate(&text, None),
10567            1_000,
10568            "the fallback under-counts on purpose (bytes/6): an over-count refuses work \
10569             that would have succeeded"
10570        );
10571    }
10572
10573    #[test]
10574    fn vision_memory_reservation_is_bounded_and_released() {
10575        let permit = try_reserve_vision_memory(MAX_VISION_PATCH_BYTES).unwrap();
10576        let Err(capacity) = try_reserve_vision_memory(1) else {
10577            panic!("a full process vision budget admitted another request");
10578        };
10579        assert!(matches!(capacity, VisionMemoryError::Capacity(_)));
10580        let response = vision_memory_error_response(capacity, Some("messages"));
10581        assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
10582        assert_eq!(response.headers()["retry-after"], "5");
10583        assert_eq!(response.headers()["retry-after-ms"], "5000");
10584        drop(permit);
10585        assert!(try_reserve_vision_memory(1).is_ok());
10586        let Err(request) = try_reserve_vision_memory(MAX_VISION_PATCH_BYTES + 1) else {
10587            panic!("an over-limit vision request was admitted");
10588        };
10589        assert!(matches!(request, VisionMemoryError::Request(_)));
10590        let response = vision_memory_error_response(request, Some("messages"));
10591        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
10592        assert_eq!(response.headers()["x-should-retry"], "false");
10593        let _ = try_reserve_vision_memory(1);
10594    }
10595
10596    #[test]
10597    fn header_auth_gate_covers_only_inference_dialects() {
10598        for path in [
10599            "/v1/auth/check",
10600            "/v1/completions",
10601            "/v1/chat/completions",
10602            "/v1/messages",
10603            "/v1/responses",
10604            "/v1/embeddings",
10605            "/v1/rerank",
10606        ] {
10607            assert!(protected_inference_path(path), "{path}");
10608        }
10609        for path in ["/health", "/readyz", "/models", "/v1/models", "/metrics"] {
10610            assert!(!protected_inference_path(path), "{path}");
10611        }
10612    }
10613    /// The serve-shape capture seam: a request driven through the REAL blocking response
10614    /// path (the same consumer the HTTP handler awaits) feeds the armed prompt payload
10615    /// and EVERY completion delta into the receipt, byte-exact — and an unarmed receipt
10616    /// gets nothing. Where the payload is retained, and for whom, is the metering
10617    /// implementation's business (tested with it; the parity battery compares the
10618    /// composed capture files across binaries).
10619    #[tokio::test]
10620    async fn served_completion_capture_is_byte_exact_and_armed_receipts_only() {
10621        use crate::metering::Metering as _;
10622        let prompt = json!([{ "role": "user", "content": "capture me — exactly" }]);
10623
10624        let drive = |receipt: Option<Box<dyn metering::Receipt>>| async {
10625            let (tx, rx) = worker::event_channel();
10626            tx.send(Event::PromptUsage {
10627                n_prompt: 7,
10628                n_cached: 0,
10629            })
10630            .unwrap();
10631            tx.send(Event::Token {
10632                id: 1,
10633                text: "Hel".into(),
10634            })
10635            .unwrap();
10636            tx.send(Event::Token {
10637                id: 2,
10638                text: "lo".into(),
10639            })
10640            .unwrap();
10641            tx.send(Event::Done {
10642                stop_reason: "eos".into(),
10643                n_tokens: 2,
10644                n_prompt: 7,
10645                n_cached: 0,
10646                elapsed_s: 0.05,
10647                spec: None,
10648            })
10649            .unwrap();
10650            drop(tx);
10651            let mut receipt = receipt;
10652            blocking_response_with_receipt(
10653                rx,
10654                "m".into(),
10655                true,
10656                Vec::new(),
10657                None,
10658                Envelope::new(true),
10659                &mut receipt,
10660                None,
10661            )
10662            .await
10663        };
10664
10665        // Unarmed receipt (the unmarked-tenant shape): the seam must not feed it a byte.
10666        let plain = MockMetering::admit_all();
10667        let receipt = plain.open(
10668            &metering::RequestMeta {
10669                request_id: "cap-unmarked",
10670                tenant: "unmarked",
10671                principal: None,
10672                model: "m",
10673                route: "/v1/chat/completions",
10674                lane: "interactive",
10675                stream: false,
10676                max_tokens: None,
10677                reserved_ctx: None,
10678            },
10679            None,
10680        );
10681        let response = drive(Some(receipt)).await;
10682        assert_eq!(response.status(), StatusCode::OK);
10683        assert!(
10684            !plain.events().iter().any(|e| matches!(
10685                e,
10686                MeterEvent::CaptureDelta(_) | MeterEvent::CapturePrompt(_)
10687            )),
10688            "an unarmed receipt must see no capture traffic: {:?}",
10689            plain.events()
10690        );
10691
10692        // Armed receipt: the prompt payload lands byte-exact and the deltas reassemble
10693        // the completion byte-exact, alongside the terminal usage.
10694        let capturing = MockMetering::capturing();
10695        let mut receipt = capturing.open(
10696            &metering::RequestMeta {
10697                request_id: "cap-marked",
10698                tenant: "marked",
10699                principal: None,
10700                model: "m",
10701                route: "/v1/chat/completions",
10702                lane: "interactive",
10703                stream: false,
10704                max_tokens: None,
10705                reserved_ctx: None,
10706            },
10707            None,
10708        );
10709        assert!(receipt.wants_capture());
10710        receipt.arm_capture(prompt.clone());
10711        let response = drive(Some(receipt)).await;
10712        assert_eq!(response.status(), StatusCode::OK);
10713        let body = axum::body::to_bytes(response.into_body(), usize::MAX)
10714            .await
10715            .unwrap();
10716        let body: serde_json::Value = serde_json::from_slice(&body).unwrap();
10717        assert_eq!(body["choices"][0]["message"]["content"], "Hello");
10718
10719        let events = capturing.events();
10720        assert!(
10721            events.contains(&MeterEvent::CapturePrompt(prompt.clone())),
10722            "prompt must arm byte-exact: {events:?}"
10723        );
10724        let completion: String = events
10725            .iter()
10726            .filter_map(|e| match e {
10727                MeterEvent::CaptureDelta(text) => Some(text.as_str()),
10728                _ => None,
10729            })
10730            .collect();
10731        assert_eq!(
10732            completion, "Hello",
10733            "the deltas must reassemble the served completion byte-exact: {events:?}"
10734        );
10735        assert!(
10736            events.contains(&MeterEvent::Complete {
10737                prompt: 7,
10738                cached: 0,
10739                completion: 2,
10740            }),
10741            "worker-truth usage settles alongside the capture: {events:?}"
10742        );
10743    }
10744
10745    fn tool_caps() -> ModelCaps {
10746        ModelCaps {
10747            tools_branch: true,
10748            qwen_think: true,
10749            think_switch: true,
10750            chat_ok: true,
10751            ..Default::default()
10752        }
10753    }
10754
10755    /// A qwen-class model that ALSO carries the qwen3.8 reasoning-effort ladder — the shape of
10756    /// the deployed `qwen/qwen3.8-27b`. Distinct from `tool_caps()` (ornith's shape: the same
10757    /// binary switch, no depth input) because that difference is exactly what decides whether a
10758    /// graded level is honoured or refused.
10759    fn ladder_caps() -> ModelCaps {
10760        ModelCaps {
10761            qwen_effort: true,
10762            ..tool_caps()
10763        }
10764    }
10765
10766    fn gemma_tool_caps() -> ModelCaps {
10767        ModelCaps {
10768            tools_branch: true,
10769            gemma_think: true,
10770            chat_ok: true,
10771            instruct_type: Some("gemma".into()),
10772            ..Default::default()
10773        }
10774    }
10775
10776    fn hy3_tool_caps() -> ModelCaps {
10777        ModelCaps {
10778            tools_branch: true,
10779            hy3: true,
10780            chat_ok: true,
10781            effort_levels: true,
10782            instruct_type: Some("hy3".into()),
10783            ..Default::default()
10784        }
10785    }
10786
10787    fn gemma_template(kind: &str) -> String {
10788        let file = match kind {
10789            "qat" => "qat-trunk-template.jinja",
10790            _ => "official-tooluse-template.jinja",
10791        };
10792        let path = format!(
10793            "{}/../../research/gemma4-tools-20260817/{file}",
10794            env!("CARGO_MANIFEST_DIR")
10795        );
10796        std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {path}: {e}"))
10797    }
10798
10799    /// Translate a fixture request (OpenAI shape + optional Google-native `tool_responses`)
10800    /// into the renderer's inputs, REUSING the real serve helpers (`prepare_tools`,
10801    /// `render_req_tool_call`, `content_to_text`, `json_to_val`, `parse_think`) so this stays
10802    /// a faithful mirror of `build_chat_request`, not a second implementation.
10803    fn render_fixture(request: &serde_json::Value, template: &str) -> String {
10804        let tools_arr = request
10805            .get("tools")
10806            .and_then(|t| t.as_array())
10807            .cloned()
10808            .unwrap_or_default();
10809        let (tools_json, tools_struct, _schemas) = if tools_arr.is_empty() {
10810            (Vec::new(), Vec::new(), HashMap::new())
10811        } else {
10812            prepare_tools(&tools_arr).unwrap()
10813        };
10814        let effort = request
10815            .get("reasoning_effort")
10816            .and_then(|v| v.as_str())
10817            .map(String::from);
10818        let (think, _lvl, _explicit) =
10819            parse_think(&effort, &None, None, None, None, false).unwrap();
10820
10821        let mut turns: Vec<TmplTurn> = Vec::new();
10822        for msg in request["messages"].as_array().unwrap() {
10823            let role = msg["role"].as_str().unwrap();
10824            let role = if role == "developer" { "system" } else { role };
10825            let content =
10826                content_to_text(msg.get("content").unwrap_or(&serde_json::Value::Null)).unwrap();
10827            let tool_calls = msg
10828                .get("tool_calls")
10829                .and_then(|a| a.as_array())
10830                .map(|a| {
10831                    a.iter()
10832                        .map(|tc| {
10833                            let rtc: ReqToolCall = serde_json::from_value(tc.clone()).unwrap();
10834                            render_req_tool_call(&rtc).unwrap()
10835                        })
10836                        .collect()
10837                })
10838                .unwrap_or_default();
10839            let tool_responses = msg
10840                .get("tool_responses")
10841                .and_then(|a| a.as_array())
10842                .map(|a| {
10843                    a.iter()
10844                        .map(|tr| {
10845                            (
10846                                tr.get("name").and_then(|n| n.as_str()).unwrap().to_string(),
10847                                json_to_val(&tr["response"]),
10848                            )
10849                        })
10850                        .collect()
10851                })
10852                .unwrap_or_default();
10853            turns.push(TmplTurn {
10854                role: role.to_string(),
10855                content,
10856                tool_calls,
10857                reasoning: msg
10858                    .get("reasoning")
10859                    .and_then(|r| r.as_str())
10860                    .map(String::from)
10861                    .filter(|s| !s.is_empty()),
10862                tool_call_id: msg
10863                    .get("tool_call_id")
10864                    .and_then(|s| s.as_str())
10865                    .map(String::from),
10866                tool_name: msg.get("name").and_then(|s| s.as_str()).map(String::from),
10867                tool_responses,
10868                task: None,
10869                tools: Vec::new(),
10870            });
10871        }
10872        chat::apply_chat_template_tools_ex(
10873            Some(template),
10874            &turns,
10875            true,
10876            &tools_json,
10877            &tools_struct,
10878            think,
10879            None,
10880            None,
10881        )
10882        .unwrap()
10883    }
10884
10885    /// Byte-parity oracle gate: every research/gemma4-tools-20260817/fixtures/* pair, rendered
10886    /// through the memra gemma4 arm, must equal the bytes the OFFICIAL jinja produced under
10887    /// jinja2 (gen_fixtures.py). The jinja is the LAW; this is what makes it enforceable.
10888    #[test]
10889    fn gemma4_tools_fixtures_match_the_official_jinja() {
10890        let dir = format!(
10891            "{}/../../research/gemma4-tools-20260817/fixtures",
10892            env!("CARGO_MANIFEST_DIR")
10893        );
10894        let mut entries: Vec<_> = std::fs::read_dir(&dir)
10895            .unwrap_or_else(|e| panic!("read fixtures dir {dir}: {e}"))
10896            .map(|e| e.unwrap().path())
10897            .filter(|p| p.is_dir())
10898            .collect();
10899        entries.sort();
10900        assert!(
10901            entries.len() >= 14,
10902            "expected >=14 fixtures, found {}",
10903            entries.len()
10904        );
10905        let (mut official, mut qat) = (0u32, 0u32);
10906        for d in entries {
10907            let input: serde_json::Value =
10908                serde_json::from_str(&std::fs::read_to_string(d.join("input.json")).unwrap())
10909                    .unwrap();
10910            let expected = std::fs::read_to_string(d.join("expected.txt")).unwrap();
10911            let kind = input
10912                .get("template")
10913                .and_then(|t| t.as_str())
10914                .unwrap_or("official");
10915            match kind {
10916                "qat" => qat += 1,
10917                _ => official += 1,
10918            }
10919            let tmpl = gemma_template(kind);
10920            let got = render_fixture(&input["request"], &tmpl);
10921            assert_eq!(
10922                got, expected,
10923                "fixture {:?} diverged from the jinja oracle",
10924                d
10925            );
10926        }
10927        assert!(
10928            official >= 12 && qat >= 2,
10929            "coverage: {official} official, {qat} qat"
10930        );
10931    }
10932
10933    /// The REAL serve pipeline (`build_chat_request`) renders gemma4 tool DEFINITIONS + a
10934    /// tool-call/response cycle byte-identically to the fixture oracle — proving the OpenAI
10935    /// chat surface (and, via the shared path, /v1/messages + /v1/responses) flows tools to
10936    /// the gemma trunk. Native-only fixtures (Google `tool_responses`) are covered by the
10937    /// oracle test above, not here (the OpenAI request shape cannot express them).
10938    #[test]
10939    fn gemma4_tools_flow_through_build_chat_request() {
10940        let tmpl = gemma_template("official");
10941        for name in [
10942            "01-system-tools-basic",
10943            "04-single-call-cycle",
10944            "07-multi-cycle-agentic",
10945        ] {
10946            let path = format!(
10947                "{}/../../research/gemma4-tools-20260817/fixtures/{name}/input.json",
10948                env!("CARGO_MANIFEST_DIR")
10949            );
10950            let input: serde_json::Value =
10951                serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
10952            let expected_path = format!(
10953                "{}/../../research/gemma4-tools-20260817/fixtures/{name}/expected.txt",
10954                env!("CARGO_MANIFEST_DIR")
10955            );
10956            let expected = std::fs::read_to_string(&expected_path).unwrap();
10957            let req: ChatCompletionReq = serde_json::from_value(input["request"].clone()).unwrap();
10958            let (tx, _rx) = worker::event_channel();
10959            let plan = build_chat_request(
10960                req,
10961                Some(&gemma_tool_caps()),
10962                tx,
10963                lanes::Lane::Interactive,
10964                None,
10965            )
10966            .unwrap();
10967            let got = chat::apply_chat_template_tools_ex(
10968                Some(&tmpl),
10969                &plan.request.chat_turns,
10970                true,
10971                &plan.request.tools_json,
10972                &plan.request.tools_struct,
10973                plan.request.think,
10974                plan.request.reasoning_effort.as_deref(),
10975                None,
10976            )
10977            .unwrap();
10978            assert_eq!(got, expected, "pipeline render diverged for {name}");
10979        }
10980    }
10981
10982    // ---- GLM-5.3-Flash (`glm5_next`) surface (lane/glm53-flash-bringup, 2026-08-27) --------
10983    // THE STANDARD-SURFACE LAW for this model: three wire formats plus tools, all through the
10984    // vendor's own template bytes. Before this arm, every glm5 marker was ALSO a qwen marker,
10985    // so `apply_chat_template_tools_ex` fell through to the ChatML arm and served `<|im_start|>`
10986    // turns to a checkpoint whose special vocabulary does not contain them — fluent, because
10987    // GLM follows the qwen tool-format instruction it was handed in-context, and invisible
10988    // without a byte oracle. The oracle is the checkpoint's own chat_template.jinja.
10989
10990    fn glm5_template() -> String {
10991        let path = format!(
10992            "{}/../../research/glm53-flash-bringup-20260827/chat_template.jinja",
10993            env!("CARGO_MANIFEST_DIR")
10994        );
10995        std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {path}: {e}"))
10996    }
10997
10998    /// The caps the worker probes off that template — copied from the live boot line
10999    /// (`tools=true think=true think_switch=false chat_ok=true effort_levels=true
11000    /// qwen_effort=false gemma_think=false dsv4=false ctx=1048576 tok="glm4"`), plus the
11001    /// `glm5` dialect flag this lane added.
11002    fn glm5_caps() -> ModelCaps {
11003        ModelCaps {
11004            tools_branch: true,
11005            qwen_think: true,
11006            think_switch: false,
11007            chat_ok: true,
11008            context_length: 1_048_576,
11009            tokenizer: "glm4".into(),
11010            instruct_type: Some("glm".into()),
11011            effort_levels: true,
11012            glm5: true,
11013            ..Default::default()
11014        }
11015    }
11016
11017    /// One fixture request through the REAL serve pipeline, rendered with the vendor template.
11018    fn glm5_render(body: serde_json::Value) -> Result<String, String> {
11019        let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
11020        let (tx, _rx) = worker::event_channel();
11021        let plan = build_chat_request(req, Some(&glm5_caps()), tx, lanes::Lane::Interactive, None)?;
11022        chat::apply_chat_template_tools_ex(
11023            Some(&glm5_template()),
11024            &plan.request.chat_turns,
11025            true,
11026            &plan.request.tools_json,
11027            &plan.request.tools_struct,
11028            plan.request.think,
11029            plan.request.reasoning_effort.as_deref(),
11030            None,
11031        )
11032    }
11033
11034    /// Byte-parity oracle gate: every research/glm53-flash-bringup-20260827/surface-fixtures/*
11035    /// pair, run through `build_chat_request` + the glm5 arm, must equal the bytes the VENDOR
11036    /// jinja produced under jinja2 (gen_surface_fixtures.py). The jinja is the LAW; this is
11037    /// what makes it enforceable.
11038    #[test]
11039    fn glm5_fixtures_match_the_vendor_jinja() {
11040        let dir = format!(
11041            "{}/../../research/glm53-flash-bringup-20260827/surface-fixtures",
11042            env!("CARGO_MANIFEST_DIR")
11043        );
11044        let mut entries: Vec<_> = std::fs::read_dir(&dir)
11045            .unwrap_or_else(|e| panic!("read fixtures dir {dir}: {e}"))
11046            .map(|e| e.unwrap().path())
11047            .filter(|p| p.is_dir())
11048            .collect();
11049        entries.sort();
11050        assert!(
11051            entries.len() >= 22,
11052            "expected >=22 fixtures, found {}",
11053            entries.len()
11054        );
11055        for d in entries {
11056            let input: serde_json::Value =
11057                serde_json::from_str(&std::fs::read_to_string(d.join("input.json")).unwrap())
11058                    .unwrap();
11059            let expected = std::fs::read_to_string(d.join("expected.txt")).unwrap();
11060            let got = glm5_render(input["request"].clone())
11061                .unwrap_or_else(|e| panic!("fixture {d:?} refused: {e}"));
11062            assert_eq!(
11063                got, expected,
11064                "fixture {d:?} diverged from the jinja oracle"
11065            );
11066        }
11067    }
11068
11069    /// THE DEFECT THIS ARM EXISTS TO CLOSE. The GLM template contains `<think>`,
11070    /// `add_generation_prompt` AND `<tools>`, so every qwen marker check matches it. Without
11071    /// the glm5 dispatch the renderer emitted ChatML — tokens this checkpoint does not carry as
11072    /// specials at all (`extra_special_tokens` is `[gMASK] <sop> <|system|> <|user|>
11073    /// <|assistant|> <|observation|>` …), so the whole frame tokenized as ordinary text.
11074    #[test]
11075    fn glm5_never_renders_chatml() {
11076        let tmpl = glm5_template();
11077        // The markers that used to win the dispatch are all really there.
11078        assert!(tmpl.contains("<think>") && tmpl.contains("add_generation_prompt"));
11079        assert!(tmpl.contains("<tools>"));
11080        assert!(chat::template_is_glm5(&tmpl));
11081        for body in [
11082            json!({"model": "m", "messages": [{"role": "user", "content": "hi"}]}),
11083            json!({"model": "m", "messages": [{"role": "user", "content": "hi"}],
11084                   "tools": [{"type": "function", "function": {"name": "f",
11085                              "parameters": {"type": "object", "properties": {}}}}]}),
11086        ] {
11087            let got = glm5_render(body).unwrap();
11088            assert!(
11089                !got.contains("<|im_start|>") && !got.contains("<|im_end|>"),
11090                "glm5 rendered ChatML frames: {got:?}"
11091            );
11092            assert!(
11093                got.starts_with("[gMASK]<sop><|system|>Reasoning Effort: "),
11094                "{got:?}"
11095            );
11096            assert!(got.ends_with("<|assistant|><think>"), "{got:?}");
11097        }
11098    }
11099
11100    /// `reasoning_effort` must reach the TEMPLATE (a rendered system line), never the sampler,
11101    /// and the model's `max` rung — a real tier ABOVE `high`, and its own default — must
11102    /// survive `canonical_effort_for` instead of clamping into `high`.
11103    #[test]
11104    fn glm5_reasoning_effort_renders_and_keeps_its_max_tier() {
11105        for (sent, line) in [
11106            (None, "Max"),
11107            (Some("low"), "Low"),
11108            // no medium rung in this ladder: clamp DOWN, never through the template's
11109            // `else` arm (which is Max — answering "reason less" with the deepest setting).
11110            (Some("medium"), "Low"),
11111            (Some("high"), "High"),
11112            (Some("xhigh"), "Max"),
11113            (Some("max"), "Max"),
11114            (Some("ultra"), "Max"),
11115        ] {
11116            let mut body = json!({"model": "m", "messages": [{"role": "user", "content": "hi"}]});
11117            if let Some(v) = sent {
11118                body["reasoning_effort"] = json!(v);
11119            }
11120            let got = glm5_render(body).unwrap();
11121            assert!(
11122                got.starts_with(&format!("[gMASK]<sop><|system|>Reasoning Effort: {line}<|")),
11123                "reasoning_effort {sent:?} should render {line:?}: {got:?}"
11124            );
11125        }
11126        // The level is a RENDER input, not a sampler knob: two efforts that render different
11127        // system lines must leave the sampler identical.
11128        let sampler_of = |v: &str| {
11129            let req: ChatCompletionReq = serde_json::from_value(
11130                // seed pinned: it is drawn fresh per request, and this assertion is about
11131                // whether the effort level perturbs the SAMPLER, not about the draw.
11132                json!({"model": "m", "messages": [{"role": "user", "content": "hi"}],
11133                       "reasoning_effort": v, "seed": 7}),
11134            )
11135            .unwrap();
11136            let (tx, _rx) = worker::event_channel();
11137            let plan =
11138                build_chat_request(req, Some(&glm5_caps()), tx, lanes::Lane::Interactive, None)
11139                    .unwrap();
11140            format!("{:?}", plan.request.sampler_cfg)
11141        };
11142        assert_eq!(sampler_of("low"), sampler_of("max"));
11143        // And the canonical table itself keeps the tier for this model's key.
11144        assert_eq!(canonical_effort_for("max", true), Some("max"));
11145        assert_eq!(canonical_effort_for("xhigh", true), Some("max"));
11146        assert_eq!(canonical_effort_for("max", false), Some("high"));
11147    }
11148
11149    /// The off-request this template genuinely cannot honour stays a NAMED 400 (it opens
11150    /// `<think>` unconditionally and has no `enable_thinking`), and an out-of-table level
11151    /// stays a 400 — neither becomes a silent downgrade now that the level is delivered.
11152    #[test]
11153    fn glm5_refuses_what_its_template_cannot_honour() {
11154        for (value, needle) in [
11155            ("none", "cannot disable reasoning"),
11156            ("minimal", "cannot disable reasoning"),
11157            ("bogus", "bad reasoning_effort"),
11158        ] {
11159            let err = glm5_render(json!({"model": "m",
11160                "messages": [{"role": "user", "content": "hi"}],
11161                "reasoning_effort": value}))
11162            .err()
11163            .unwrap_or_else(|| panic!("reasoning_effort {value:?} must be refused"));
11164            assert!(err.contains(needle), "{value}: {err}");
11165        }
11166    }
11167
11168    /// THE STANDARD-SURFACE LAW at the byte level, for this model: the same semantic request
11169    /// expressed in each of the three wire vocabularies — including a tool definition and a
11170    /// full call/result cycle — must render the SAME glm5 prompt bytes.
11171    #[test]
11172    fn one_glm5_request_renders_identical_bytes_on_all_three_surfaces() {
11173        // TWO parallel calls whose results come back in REVERSED order. That shape is what
11174        // makes this test discriminate: the glm5 arm re-orders an `<|observation|>` run onto
11175        // the preceding assistant turn's `tool_calls` order, but ONLY when every result's id
11176        // resolves (`glm5_can_sort`) — otherwise it renders in message order. With one call
11177        // both branches emit identical bytes, so a translation surface that silently dropped
11178        // `tool_call_id` would still pass. With two, reversed, it cannot.
11179        let chat = json!({
11180            "model": "m",
11181            "reasoning_effort": "high",
11182            "messages": [
11183                {"role": "user", "content": "Weather in Paris and Rome?"},
11184                {"role": "assistant", "content": null,
11185                 "tool_calls": [
11186                     {"id": "c1", "type": "function",
11187                      "function": {"name": "get_weather",
11188                                   "arguments": "{\"city\": \"Paris\"}"}},
11189                     {"id": "c2", "type": "function",
11190                      "function": {"name": "get_weather",
11191                                   "arguments": "{\"city\": \"Rome\"}"}}]},
11192                {"role": "tool", "tool_call_id": "c2", "content": "rome:27"},
11193                {"role": "tool", "tool_call_id": "c1", "content": "paris:21"}
11194            ],
11195            "tools": [{"type": "function", "function": {
11196                "name": "get_weather", "description": "Get the current weather for a city",
11197                "parameters": {"type": "object",
11198                               "properties": {"city": {"type": "string"}},
11199                               "required": ["city"]}}}]
11200        });
11201        let responses = responses_api::translate(&json!({
11202            "model": "m",
11203            "reasoning": {"effort": "high"},
11204            "input": [
11205                {"type": "message", "role": "user",
11206                 "content": [{"type": "input_text", "text": "Weather in Paris and Rome?"}]},
11207                {"type": "function_call", "call_id": "c1", "name": "get_weather",
11208                 "arguments": "{\"city\": \"Paris\"}"},
11209                {"type": "function_call", "call_id": "c2", "name": "get_weather",
11210                 "arguments": "{\"city\": \"Rome\"}"},
11211                {"type": "function_call_output", "call_id": "c2", "output": "rome:27"},
11212                {"type": "function_call_output", "call_id": "c1", "output": "paris:21"}
11213            ],
11214            "tools": [{"type": "function", "name": "get_weather",
11215                       "description": "Get the current weather for a city",
11216                       "parameters": {"type": "object",
11217                                      "properties": {"city": {"type": "string"}},
11218                                      "required": ["city"]}}]
11219        }))
11220        .expect("/v1/responses translate");
11221        let messages = anthropic::translate(&json!({
11222            "model": "m",
11223            "max_tokens": 256,
11224            "output_config": {"effort": "high"},
11225            "messages": [
11226                {"role": "user", "content": "Weather in Paris and Rome?"},
11227                {"role": "assistant", "content": [
11228                    {"type": "tool_use", "id": "c1", "name": "get_weather",
11229                     "input": {"city": "Paris"}},
11230                    {"type": "tool_use", "id": "c2", "name": "get_weather",
11231                     "input": {"city": "Rome"}}]},
11232                {"role": "user", "content": [
11233                    {"type": "tool_result", "tool_use_id": "c2", "content": "rome:27"},
11234                    {"type": "tool_result", "tool_use_id": "c1", "content": "paris:21"}]}
11235            ],
11236            "tools": [{"name": "get_weather",
11237                       "description": "Get the current weather for a city",
11238                       "input_schema": {"type": "object",
11239                                        "properties": {"city": {"type": "string"}},
11240                                        "required": ["city"]}}]
11241        }))
11242        .expect("/v1/messages translate");
11243        let want = glm5_render(chat).expect("chat");
11244        // The tool cycle really did render the native dialect, not a qwen-shaped fallback.
11245        assert!(
11246            want.contains(
11247                "<tool_call>get_weather<arg_key>city</arg_key><arg_value>Paris</arg_value>\
11248                 </tool_call><tool_call>get_weather<arg_key>city</arg_key>\
11249                 <arg_value>Rome</arg_value></tool_call>"
11250            ),
11251            "{want:?}"
11252        );
11253        // The ids resolved, so the run was re-ordered onto CALL order (Paris, Rome), not the
11254        // message order the client sent (Rome, Paris). That is the byte this test discriminates
11255        // on: any surface that loses `tool_call_id` renders the pair the other way round.
11256        assert!(
11257            want.contains(
11258                "<|observation|><tool_response>paris:21</tool_response>\
11259                 <tool_response>rome:27</tool_response>"
11260            ),
11261            "{want:?}"
11262        );
11263        assert!(
11264            want.contains("<|system|>Reasoning Effort: High"),
11265            "{want:?}"
11266        );
11267        for (surface, body) in [
11268            ("/v1/responses", responses),
11269            ("/v1/messages", messages.clone()),
11270        ] {
11271            let got = glm5_render(body).unwrap_or_else(|e| panic!("{surface}: {e}"));
11272            assert_eq!(
11273                got, want,
11274                "{surface} rendered DIFFERENT glm5 prompt bytes than /v1/chat/completions"
11275            );
11276        }
11277        // NEGATIVE CONTROL — the equality above only means something if losing the ids really
11278        // changes the bytes. Strip `tool_call_id` from the result turns (what a translation
11279        // surface that dropped it would hand the renderer) and the run must fall back to
11280        // MESSAGE order, diverging. Without this, a `can_sort` that silently answered `false`
11281        // everywhere would keep the whole test green.
11282        let mut idless = messages;
11283        for m in idless["messages"].as_array_mut().unwrap() {
11284            if m["role"] == "tool" {
11285                m.as_object_mut().unwrap().remove("tool_call_id");
11286            }
11287        }
11288        let got = glm5_render(idless).expect("id-less render");
11289        assert_ne!(
11290            got, want,
11291            "dropping tool_call_id must change the rendered order — this test cannot detect \
11292             a surface that loses ids otherwise"
11293        );
11294        assert!(
11295            got.contains(
11296                "<|observation|><tool_response>rome:27</tool_response>\
11297                 <tool_response>paris:21</tool_response>"
11298            ),
11299            "{got:?}"
11300        );
11301    }
11302
11303    /// The chat path must arm the GLM parser, not the qwen `<function=` scanner — otherwise
11304    /// every native call surfaces VERBATIM as content behind a 200.
11305    #[test]
11306    fn glm5_chat_arms_the_native_tool_parser() {
11307        let req: ChatCompletionReq = serde_json::from_value(json!({
11308            "model": "m", "messages": [{"role": "user", "content": "weather?"}],
11309            "tools": [{"type": "function", "function": {"name": "get_weather",
11310                       "parameters": {"type": "object",
11311                                      "properties": {"city": {"type": "string"}}}}}]}))
11312        .unwrap();
11313        let (tx, _rx) = worker::event_channel();
11314        let plan = build_chat_request(req, Some(&glm5_caps()), tx, lanes::Lane::Interactive, None)
11315            .unwrap();
11316        let mut parser = plan.parser.expect("glm5 tools request must carry a parser");
11317        let pieces = parser.push(
11318            "reasoning here</think><tool_call>get_weather<arg_key>city</arg_key>\
11319             <arg_value>Paris</arg_value></tool_call>",
11320        );
11321        let calls: Vec<_> = pieces
11322            .iter()
11323            .filter_map(|p| match p {
11324                toolcall::Piece::Call(c) => Some((c.name.as_str(), c.arguments.as_str())),
11325                _ => None,
11326            })
11327            .collect();
11328        assert_eq!(
11329            calls,
11330            vec![("get_weather", r#"{"city":"Paris"}"#)],
11331            "{pieces:?}"
11332        );
11333        assert!(
11334            pieces
11335                .iter()
11336                .any(|p| matches!(p, toolcall::Piece::Reasoning(r) if r == "reasoning here")),
11337            "{pieces:?}"
11338        );
11339        // and nothing leaked into content.
11340        assert!(
11341            !pieces
11342                .iter()
11343                .any(|p| matches!(p, toolcall::Piece::Content(_))),
11344            "{pieces:?}"
11345        );
11346        // A NON-tools glm5 request must still carry a parser: this template's `<think>` tail is
11347        // unconditional, so without one the whole reasoning block lands in `content` with the
11348        // `</think>` tag in it. (The wiring half of `glm5_without_tools_is_a_reasoning_splitter_only`.)
11349        let req: ChatCompletionReq = serde_json::from_value(
11350            json!({"model": "m", "messages": [{"role": "user", "content": "hi"}]}),
11351        )
11352        .unwrap();
11353        let (tx, _rx) = worker::event_channel();
11354        let plan = build_chat_request(req, Some(&glm5_caps()), tx, lanes::Lane::Interactive, None)
11355            .unwrap();
11356        let mut parser = plan
11357            .parser
11358            .expect("glm5 non-tools request must still split reasoning");
11359        let pieces = parser.push("weighing it</think>The answer.");
11360        assert!(
11361            pieces
11362                .iter()
11363                .any(|p| matches!(p, toolcall::Piece::Reasoning(r) if r == "weighing it")),
11364            "{pieces:?}"
11365        );
11366        assert!(
11367            pieces
11368                .iter()
11369                .any(|p| matches!(p, toolcall::Piece::Content(c) if c == "The answer.")),
11370            "{pieces:?}"
11371        );
11372    }
11373
11374    /// The worker's PLAIN fast path maps turns to `(role, content)` tuples and drops
11375    /// `reasoning` — so on a dialect that replays prior reasoning into the prompt it would
11376    /// render different bytes than the tools path for the same request. GLM-5.3-Flash is such a
11377    /// dialect (`<think>{reasoning}</think>` on every assistant turn, unconditionally), and the
11378    /// two paths must never disagree: a re-render that does not match its own live stream is
11379    /// also what stops a parked session from ever resuming (lane/dflash2-session-reuse).
11380    #[test]
11381    fn glm5_plain_fast_path_never_drops_replayed_reasoning() {
11382        let with_reasoning = vec![
11383            chat::Turn {
11384                role: "user".into(),
11385                content: "a".into(),
11386                ..Default::default()
11387            },
11388            chat::Turn {
11389                role: "assistant".into(),
11390                content: "A".into(),
11391                reasoning: Some("I considered a.".into()),
11392                ..Default::default()
11393            },
11394            chat::Turn {
11395                role: "user".into(),
11396                content: "b".into(),
11397                ..Default::default()
11398            },
11399        ];
11400        // The predicate must refuse the fast path for this shape...
11401        assert!(!worker::plain_chat_render_path(
11402            &[],
11403            &chat::ThinkMode::Default,
11404            None,
11405            &with_reasoning,
11406            false,
11407        ));
11408        // ...and the same turns WITHOUT reasoning still take it (the fast path is not disabled
11409        // wholesale — only for the shape it cannot render faithfully).
11410        let plain_turns: Vec<chat::Turn> = with_reasoning
11411            .iter()
11412            .cloned()
11413            .map(|mut t| {
11414                t.reasoning = None;
11415                t
11416            })
11417            .collect();
11418        assert!(worker::plain_chat_render_path(
11419            &[],
11420            &chat::ThinkMode::Default,
11421            None,
11422            &plain_turns,
11423            false,
11424        ));
11425        // And the bytes the two paths would produce really do differ on this dialect, so the
11426        // predicate above is load-bearing rather than defensive.
11427        let tmpl = glm5_template();
11428        let via_tools = chat::apply_chat_template_tools_ex(
11429            Some(&tmpl),
11430            &with_reasoning,
11431            true,
11432            &[],
11433            &[],
11434            chat::ThinkMode::Default,
11435            None,
11436            None,
11437        )
11438        .unwrap();
11439        let msgs: Vec<(&str, &str)> = with_reasoning
11440            .iter()
11441            .map(|t| (t.role.as_str(), t.content.as_str()))
11442            .collect();
11443        let via_plain = chat::apply_chat_template_str(Some(&tmpl), &msgs, true);
11444        assert!(
11445            via_tools.contains("<think>I considered a.</think>"),
11446            "{via_tools:?}"
11447        );
11448        assert_ne!(via_tools, via_plain);
11449        // On the no-reasoning shape the two paths are byte-identical, which is what makes
11450        // keeping the fast path there safe.
11451        let plain_msgs: Vec<(&str, &str)> = plain_turns
11452            .iter()
11453            .map(|t| (t.role.as_str(), t.content.as_str()))
11454            .collect();
11455        assert_eq!(
11456            chat::apply_chat_template_tools_ex(
11457                Some(&tmpl),
11458                &plain_turns,
11459                true,
11460                &[],
11461                &[],
11462                chat::ThinkMode::Default,
11463                None,
11464                None,
11465            )
11466            .unwrap(),
11467            chat::apply_chat_template_str(Some(&tmpl), &plain_msgs, true)
11468        );
11469    }
11470
11471    /// `/v1/models` must not advertise a capability the server refuses by name. A template
11472    /// whose `<think>` tail opens unconditionally with no `enable_thinking` switch cannot take
11473    /// constrained decoding at all — the request 400s — so the row says `false`.
11474    #[test]
11475    fn glm5_model_row_does_not_claim_structured_output() {
11476        let caps = glm5_caps();
11477        let row = model_entry_v1("zai/glm-5.3-flash", Some(&caps), None);
11478        assert_eq!(row["capabilities"]["structured_output"], json!(false));
11479        assert_eq!(row["capabilities"]["tools"], json!(true));
11480        assert_eq!(row["capabilities"]["reasoning"], json!(true));
11481        // and the refusal the row now matches is real.
11482        let err = glm5_render(json!({"model": "m",
11483            "messages": [{"role": "user", "content": "hi"}],
11484            "response_format": {"type": "json_object"}}))
11485        .expect_err("response_format must be refused on a switchless think template");
11486        // Post-think constrained decoding (lane/step37-postthink-grammar) widened the refusal
11487        // text: glm5's template has neither the switch nor a derivable think-close contract,
11488        // so the refusal (and the false row) stand; only the message grew.
11489        assert!(
11490            err.contains("neither an enable_thinking switch nor a recognizable"),
11491            "{err}"
11492        );
11493        // A model that CAN close its think tail keeps the true claim.
11494        let switchable = model_entry_v1("q", Some(&tool_caps()), None);
11495        assert_eq!(switchable["capabilities"]["structured_output"], json!(true));
11496        // The OpenRouter catalog must not disagree with the contract-v2 row about one model:
11497        // it advertised `json_mode` + `structured_outputs` unconditionally.
11498        let glm_params = openrouter_supported_parameters(Some(&caps), None, true);
11499        assert!(
11500            glm_params.get("structured_outputs").is_none(),
11501            "{glm_params}"
11502        );
11503        // THE step37 SHAPE (v0.123.0 regression, found by the 2026-09-01 claim re-seal):
11504        // switchless force-open think WITH a derivable think-close contract is SERVED via
11505        // post-think constrained decoding, so both catalogs must say true. v0.123.0's
11506        // heuristic predicate advertised false here while the live server returned
11507        // schema-valid response_format output on the same model.
11508        let step_like = ModelCaps {
11509            chat_ok: true,
11510            qwen_think: true,
11511            think_switch: false,
11512            think_close: vec![128799],
11513            ..caps.clone()
11514        };
11515        let step_row = model_entry_v1("stepfun/step-3.7-flash", Some(&step_like), None);
11516        assert_eq!(step_row["capabilities"]["structured_output"], json!(true));
11517        let step_params = openrouter_supported_parameters(Some(&step_like), None, true);
11518        assert!(
11519            step_params.get("structured_outputs").is_some(),
11520            "{step_params}"
11521        );
11522        assert!(glm_params.get("json_mode").is_none(), "{glm_params}");
11523        assert!(glm_params.get("tools").is_some(), "{glm_params}");
11524        let qwen_params = openrouter_supported_parameters(Some(&tool_caps()), None, true);
11525        assert!(
11526            qwen_params.get("structured_outputs").is_some(),
11527            "{qwen_params}"
11528        );
11529        assert!(qwen_params.get("json_mode").is_some(), "{qwen_params}");
11530    }
11531
11532    /// The catalog must not advertise the checkpoint's trained context as a serving claim.
11533    /// glm5 declares 1,048,576 trained, and the 3-card resident shape measurably cannot prime
11534    /// it (`research/glm5-prefix-latent-20260830/box-window/WINDOW-STATUS.md`: the 1M deep
11535    /// prime died `layer 31: DSA k-pool selection failed: CUDA_ERROR_OUT_OF_MEMORY`). When the
11536    /// deployment pins its operational envelope (`max_prompt_length` + `max_output_length`),
11537    /// every catalog body publishes that envelope, not the trained figure; with no envelope
11538    /// pinned the trained value stands.
11539    #[test]
11540    fn catalog_context_claim_is_capped_by_the_deployment_envelope() {
11541        let caps = glm5_caps();
11542        assert_eq!(caps.context_length, 1_048_576);
11543        let metadata = OpenRouterModelMetadata {
11544            max_prompt_length: Some(126_976),
11545            max_output_length: Some(4_096),
11546            ..Default::default()
11547        };
11548        // Envelope pinned below trained -> the envelope is the claim, on all three bodies.
11549        let row = model_entry_v1("zai/glm-5.3-flash", Some(&caps), Some(&metadata));
11550        assert_eq!(row["context_length"], json!(131_072));
11551        let or_row = model_entry_openrouter("zai/glm-5.3-flash", Some(&caps), Some(&metadata));
11552        assert_eq!(
11553            or_row["input_modalities"][0]["supported_inputs"]["max_context_length"]["value"],
11554            json!(131_072)
11555        );
11556        assert_eq!(
11557            published_context_length(Some(&caps), Some(&metadata)),
11558            Some(131_072)
11559        );
11560        // No envelope (or half an envelope) -> the trained value stands unchanged.
11561        assert_eq!(published_context_length(Some(&caps), None), Some(1_048_576));
11562        let half = OpenRouterModelMetadata {
11563            max_output_length: Some(4_096),
11564            ..Default::default()
11565        };
11566        assert_eq!(
11567            published_context_length(Some(&caps), Some(&half)),
11568            Some(1_048_576)
11569        );
11570        // An envelope above trained never inflates the claim.
11571        let wide = OpenRouterModelMetadata {
11572            max_prompt_length: Some(2_000_000),
11573            max_output_length: Some(2_000_000),
11574            ..Default::default()
11575        };
11576        assert_eq!(
11577            published_context_length(Some(&caps), Some(&wide)),
11578            Some(1_048_576)
11579        );
11580    }
11581
11582    // ---- deepseek-v4 (encoding_dsv4) template arm (lane 5, 2026-08-18) --------------------
11583    // The oracle IS encoding_dsv4.py. Byte parity is the only acceptance (GGUF template-mint
11584    // law). Two gates: the generated matrix (research/dsv4-template-20260818/gen_fixtures.py,
11585    // 25 cases across 3 modes x {single,multi,system,tools,tool-results,tasks,reminder}) and
11586    // the artifact's AUTHORITATIVE encoding/tests/test_output_{1..4}. Plus a tokenization
11587    // cross-check: rendered bytes -> memra token ids == the official HF tokenizer ids.
11588
11589    fn dsv4_sentinel() -> String {
11590        let path = format!(
11591            "{}/../../research/dsv4-template-20260818/dsv4-chat-template.sentinel.jinja",
11592            env!("CARGO_MANIFEST_DIR")
11593        );
11594        std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {path}: {e}"))
11595    }
11596
11597    /// Build a dsv4 `TmplTurn` from a serve-shape (`reasoning`) OR OpenAI-shape
11598    /// (`reasoning_content`) message value, reusing the real serve helpers so this mirrors
11599    /// `build_chat_request`, not a second implementation. Per-turn `tools` (search-pipeline
11600    /// developer tools) are read from the message; the `task` head is read too.
11601    fn dsv4_turn(msg: &serde_json::Value) -> TmplTurn {
11602        let role = msg["role"].as_str().unwrap().to_string();
11603        let content =
11604            content_to_text(msg.get("content").unwrap_or(&serde_json::Value::Null)).unwrap();
11605        let reasoning = msg
11606            .get("reasoning")
11607            .or_else(|| msg.get("reasoning_content"))
11608            .and_then(|r| r.as_str())
11609            .map(String::from)
11610            .filter(|s| !s.is_empty());
11611        let tool_calls = msg
11612            .get("tool_calls")
11613            .and_then(|a| a.as_array())
11614            .map(|a| {
11615                a.iter()
11616                    .map(|tc| {
11617                        let rtc: ReqToolCall = serde_json::from_value(tc.clone()).unwrap();
11618                        render_req_tool_call(&rtc).unwrap()
11619                    })
11620                    .collect()
11621            })
11622            .unwrap_or_default();
11623        let tools = msg
11624            .get("tools")
11625            .and_then(|a| a.as_array())
11626            .map(|a| {
11627                a.iter()
11628                    .filter_map(|t| t.get("function").map(json_to_val))
11629                    .collect()
11630            })
11631            .unwrap_or_default();
11632        TmplTurn {
11633            role,
11634            content,
11635            tool_calls,
11636            reasoning,
11637            tool_call_id: msg
11638                .get("tool_call_id")
11639                .and_then(|s| s.as_str())
11640                .map(String::from),
11641            tool_name: msg.get("name").and_then(|s| s.as_str()).map(String::from),
11642            tool_responses: Vec::new(),
11643            task: msg.get("task").and_then(|s| s.as_str()).map(String::from),
11644            tools,
11645        }
11646    }
11647
11648    fn dsv4_req_tools(v: Option<&serde_json::Value>) -> Vec<chat::Val> {
11649        v.and_then(|t| t.as_array())
11650            .map(|a| {
11651                a.iter()
11652                    .filter_map(|t| t.get("function").map(json_to_val))
11653                    .collect()
11654            })
11655            .unwrap_or_default()
11656    }
11657
11658    /// Byte-parity runner over one generated fixture dir (gen_fixtures.py), rendered under
11659    /// the given encoding revision. Both revisions' matrices run through the SAME arm —
11660    /// only the `Dsv4Encoding` differs (0731 re-gate, ENCODING-DIFF.md).
11661    fn dsv4_run_fixture_dir(subdir: &str, encoding: chat::Dsv4Encoding, min_fixtures: usize) {
11662        let dir = format!(
11663            "{}/../../research/dsv4-template-20260818/{subdir}",
11664            env!("CARGO_MANIFEST_DIR")
11665        );
11666        let tmpl = dsv4_sentinel();
11667        let mut entries: Vec<_> = std::fs::read_dir(&dir)
11668            .unwrap_or_else(|e| panic!("read fixtures dir {dir}: {e}"))
11669            .map(|e| e.unwrap().path())
11670            .filter(|p| p.is_dir())
11671            .collect();
11672        entries.sort();
11673        assert!(
11674            entries.len() >= min_fixtures,
11675            "expected >={min_fixtures} fixtures, found {}",
11676            entries.len()
11677        );
11678        for d in &entries {
11679            let input: serde_json::Value =
11680                serde_json::from_str(&std::fs::read_to_string(d.join("input.json")).unwrap())
11681                    .unwrap();
11682            let expected = std::fs::read_to_string(d.join("expected.txt")).unwrap();
11683            let turns: Vec<TmplTurn> = input["turns"]
11684                .as_array()
11685                .unwrap()
11686                .iter()
11687                .map(dsv4_turn)
11688                .collect();
11689            let think = match input["think"].as_str().unwrap() {
11690                "chat" => ThinkMode::NoThink,
11691                _ => ThinkMode::Think,
11692            };
11693            let effort = input
11694                .get("reasoning_effort")
11695                .and_then(|v| v.as_str())
11696                .map(String::from);
11697            let req_tools = dsv4_req_tools(input.get("req_tools"));
11698            let agp = input["add_generation_prompt"].as_bool().unwrap_or(true);
11699            let got = chat::apply_chat_template_tools_ex(
11700                Some(&tmpl),
11701                &turns,
11702                agp,
11703                &[],
11704                &req_tools,
11705                think,
11706                effort.as_deref(),
11707                Some(encoding),
11708            )
11709            .unwrap();
11710            assert_eq!(got, expected, "fixture {:?} diverged from the oracle", d);
11711        }
11712    }
11713
11714    #[test]
11715    fn dsv4_template_fixtures_match_the_oracle() {
11716        dsv4_run_fixture_dir("fixtures", chat::Dsv4Encoding::Preview, 20);
11717    }
11718
11719    /// 0731 re-gate (support-checklist item 3): the full mode x effort x shape matrix
11720    /// generated from the OFFICIAL 0731 encoding_dsv4.py (ref-0731/encoding/), including
11721    /// explicit low/high/max rungs of the remapped ladder — "high" is a REAL prefix here
11722    /// (the preview's "max" text) and "max" is the new stronger text. The preview matrix
11723    /// above keeps passing untouched (regression: both encodings stay supported).
11724    #[test]
11725    fn dsv4_0731_fixtures_match_the_oracle() {
11726        dsv4_run_fixture_dir("fixtures-0731", chat::Dsv4Encoding::V0731, 40);
11727    }
11728
11729    #[test]
11730    fn dsv4_artifact_fixtures_are_byte_identical() {
11731        // The NVFP4 artifact's encoding/tests are AUTHORITATIVE (SEMANTICS.md §6). Case 1 has
11732        // a top-level `tools` merged onto messages[0] (test_encoding_dsv4.py); case 3 carries
11733        // tools on its developer message; think mode is thinking for 1-3, chat for 4.
11734        let base = format!(
11735            "{}/../../research/dsv4-template-20260818/ref/artifact-encoding/tests",
11736            env!("CARGO_MANIFEST_DIR")
11737        );
11738        let tmpl = dsv4_sentinel();
11739        for (n, think) in [
11740            (1u32, ThinkMode::Think),
11741            (2, ThinkMode::Think),
11742            (3, ThinkMode::Think),
11743            (4, ThinkMode::NoThink),
11744        ] {
11745            let td: serde_json::Value = serde_json::from_str(
11746                &std::fs::read_to_string(format!("{base}/test_input_{n}.json")).unwrap(),
11747            )
11748            .unwrap();
11749            let (messages, tools) = if td.is_object() {
11750                (td["messages"].clone(), td.get("tools").cloned())
11751            } else {
11752                (td.clone(), None)
11753            };
11754            let mut turns: Vec<TmplTurn> = Vec::new();
11755            for (i, msg) in messages.as_array().unwrap().iter().enumerate() {
11756                let mut t = dsv4_turn(msg);
11757                if i == 0
11758                    && let Some(tl) = &tools
11759                {
11760                    t.tools = tl
11761                        .as_array()
11762                        .unwrap()
11763                        .iter()
11764                        .filter_map(|x| x.get("function").map(json_to_val))
11765                        .collect();
11766                }
11767                turns.push(t);
11768            }
11769            let expected = std::fs::read_to_string(format!("{base}/test_output_{n}.txt")).unwrap();
11770            // The 4 authoritative fixtures are byte-identical between the preview and 0731
11771            // artifacts (verified by diff, ENCODING-DIFF.md) and carry no reasoning_effort,
11772            // so they must render identically under BOTH encoding revisions.
11773            for encoding in [chat::Dsv4Encoding::Preview, chat::Dsv4Encoding::V0731] {
11774                let got = chat::apply_chat_template_tools_ex(
11775                    Some(&tmpl),
11776                    &turns,
11777                    true,
11778                    &[],
11779                    &[],
11780                    think,
11781                    None,
11782                    Some(encoding),
11783                )
11784                .unwrap();
11785                assert_eq!(
11786                    got, expected,
11787                    "artifact fixture {n} diverged from the oracle under {encoding:?}"
11788                );
11789            }
11790        }
11791    }
11792
11793    #[test]
11794    fn dsv4_default_thinkmode_renders_thinking() {
11795        // Default == Think for dsv4 (the model has no template-own chat default; thinking is
11796        // the honest serve default — TEMPLATE-SEMANTICS.md finding #1). NoThink == chat.
11797        let tmpl = dsv4_sentinel();
11798        let turns = vec![TmplTurn {
11799            role: "user".into(),
11800            content: "Hi".into(),
11801            ..Default::default()
11802        }];
11803        let dflt = chat::apply_chat_template_tools_ex(
11804            Some(&tmpl),
11805            &turns,
11806            true,
11807            &[],
11808            &[],
11809            ThinkMode::Default,
11810            None,
11811            None,
11812        )
11813        .unwrap();
11814        let think = chat::apply_chat_template_tools_ex(
11815            Some(&tmpl),
11816            &turns,
11817            true,
11818            &[],
11819            &[],
11820            ThinkMode::Think,
11821            None,
11822            None,
11823        )
11824        .unwrap();
11825        assert_eq!(dflt, think);
11826        assert!(
11827            dflt.ends_with("<\u{ff5c}Assistant\u{ff5c}><think>"),
11828            "{dflt:?}"
11829        );
11830        let chat_mode = chat::apply_chat_template_tools_ex(
11831            Some(&tmpl),
11832            &turns,
11833            true,
11834            &[],
11835            &[],
11836            ThinkMode::NoThink,
11837            None,
11838            None,
11839        )
11840        .unwrap();
11841        assert!(
11842            chat_mode.ends_with("<\u{ff5c}Assistant\u{ff5c}></think>"),
11843            "{chat_mode:?}"
11844        );
11845    }
11846
11847    /// Rendered bytes -> memra token ids must equal the official HF tokenizer ids banked
11848    /// next to the fixtures (gen: HF `tokenizers` over ref/tokenizer.json — one sha across
11849    /// preview/0731 source/mint, so ONE ref dir serves both matrices). Proves the
11850    /// deepseek-v3 pre-tokenizer detection + BPE are integer-exact for dsv4.
11851    fn dsv4_run_tokenization_crosscheck(subdir: &str) {
11852        let base = format!(
11853            "{}/../../research/dsv4-template-20260818",
11854            env!("CARGO_MANIFEST_DIR")
11855        );
11856        let refdir = std::path::Path::new(&base).join("ref");
11857        let tok = memra_tokenizer::Tokenizer::from_hf_dir(&refdir)
11858            .expect("load dsv4 tokenizer from ref dir");
11859        assert_eq!(tok.pre(), "deepseek-v3", "pre-tokenizer family detection");
11860        let banked: serde_json::Value = serde_json::from_str(
11861            &std::fs::read_to_string(format!("{base}/{subdir}/tokenization-crosscheck.json"))
11862                .unwrap(),
11863        )
11864        .unwrap();
11865        let obj = banked.as_object().unwrap();
11866        assert!(obj.len() >= 3, "expected >=3 cross-check fixtures");
11867        for (name, ids_v) in obj {
11868            let rendered =
11869                std::fs::read_to_string(format!("{base}/{subdir}/{name}/expected.txt")).unwrap();
11870            let want: Vec<u32> = ids_v
11871                .as_array()
11872                .unwrap()
11873                .iter()
11874                .map(|v| v.as_u64().unwrap() as u32)
11875                .collect();
11876            let got = tok.encode(&rendered, true);
11877            assert_eq!(got, want, "tokenization diverged for {name}");
11878        }
11879    }
11880
11881    #[test]
11882    fn dsv4_tokenization_crosscheck_matches_official_ids() {
11883        dsv4_run_tokenization_crosscheck("fixtures");
11884    }
11885
11886    /// 0731 re-gate: id parity on fixtures that carry the REMAPPED effort prefixes (the
11887    /// new "Beyond maximum" text and the high rung's prefix) — the only new bytes 0731's
11888    /// encoding introduces to the rendered surface.
11889    #[test]
11890    fn dsv4_0731_tokenization_crosscheck_matches_official_ids() {
11891        dsv4_run_tokenization_crosscheck("fixtures-0731");
11892    }
11893
11894    #[test]
11895    fn dsv4_tool_result_long_runs_render_tokenize_roundtrip() {
11896        // Regression guard for llama.cpp #26965 (recon: research/deepseek-flash-20260818/
11897        // RECON.md): upstream's deepseek-v3-class pre-tokenizer runs through backtracking
11898        // std::regex and stack-overflows on long uniform ASCII runs inside tool results
11899        // ('Z' x 131072). memra's port (unicode::split_deepseek_v3) is an iterative scan —
11900        // no regex engine, no recursion — so a dsv4 chat whose tool RESULT carries a giant
11901        // uniform run must render, tokenize, and round-trip (decode(encode(x)) == x)
11902        // within a sane bound. Id parity vs the official HF tokenizer on the 131k case is
11903        // a receipts-time cross-check (see RECEIPTS.md), not a gate here: the gate is our
11904        // own crash-safety + round-trip.
11905        let base = format!(
11906            "{}/../../research/dsv4-template-20260818",
11907            env!("CARGO_MANIFEST_DIR")
11908        );
11909        let refdir = std::path::Path::new(&base).join("ref");
11910        let tok = memra_tokenizer::Tokenizer::from_hf_dir(&refdir)
11911            .expect("load dsv4 tokenizer from ref dir");
11912        assert_eq!(tok.pre(), "deepseek-v3", "pre-tokenizer family detection");
11913        let tmpl = dsv4_sentinel();
11914        let req_tools = dsv4_req_tools(Some(&serde_json::json!([
11915            {"type": "function", "function": {
11916                "name": "get_data",
11917                "description": "Fetch a blob",
11918                "parameters": {"type": "object", "properties": {"key": {"type": "string"}},
11919                               "required": ["key"]}
11920            }}
11921        ])));
11922
11923        let cases: Vec<(&str, String)> = vec![
11924            ("ascii-letter-131k", "Z".repeat(131_072)), // the issue's exact reproducer
11925            ("ascii-letter-1m", "Z".repeat(1_048_576)),
11926            ("space-131k", " ".repeat(131_072)),
11927            ("digit-131k", "7".repeat(131_072)),
11928            (
11929                "mixed-runs",
11930                format!(
11931                    "{}{}{}{}",
11932                    "Z".repeat(65_536),
11933                    " ".repeat(65_536),
11934                    "7".repeat(65_536),
11935                    "\n".repeat(65_536)
11936                ),
11937            ),
11938            ("cjk-64k", "中".repeat(65_536)),
11939            ("accented-letter-64k", "é".repeat(65_536)),
11940        ];
11941        for (name, blob) in &cases {
11942            let msgs = serde_json::json!([
11943                {"role": "system", "content": "You are a tool-using assistant."},
11944                {"role": "user", "content": "Fetch the blob."},
11945                {"role": "assistant", "reasoning": "Use get_data.", "content": "",
11946                 "tool_calls": [{"id": "call_001", "type": "function",
11947                                 "function": {"name": "get_data",
11948                                              "arguments": "{\"key\": \"blob\"}"}}]},
11949                {"role": "tool", "tool_call_id": "call_001", "content": blob}
11950            ]);
11951            let turns: Vec<TmplTurn> = msgs.as_array().unwrap().iter().map(dsv4_turn).collect();
11952            let rendered = chat::apply_chat_template_tools_ex(
11953                Some(&tmpl),
11954                &turns,
11955                true,
11956                &[],
11957                &req_tools,
11958                ThinkMode::Think,
11959                None,
11960                None,
11961            )
11962            .unwrap_or_else(|e| panic!("{name}: render failed: {e}"));
11963            assert!(
11964                rendered.contains(blob.as_str()),
11965                "{name}: tool result missing from render"
11966            );
11967            let t0 = std::time::Instant::now();
11968            let ids = tok.encode(&rendered, true);
11969            let encode_dt = t0.elapsed();
11970            assert!(!ids.is_empty(), "{name}: empty encode");
11971            let back = tok.decode(&ids);
11972            assert_eq!(back, rendered, "{name}: decode(encode(x)) != x");
11973            // linear-ish, not the quadratic/backtracking blowup: debug builds land in
11974            // single-digit seconds even for the 1M case; 60s catches a blowup without
11975            // flaking a loaded box.
11976            assert!(
11977                encode_dt < std::time::Duration::from_secs(60),
11978                "{name}: encode took {encode_dt:?}"
11979            );
11980            // receipts-time HF cross-check bridge: dump rendered bytes + memra ids for the
11981            // 131k reproducer so a scratch `tokenizers` venv can verify id parity
11982            // (research/dsv4-template-20260818/RECEIPTS.md, long-run hardening section).
11983            if *name == "ascii-letter-131k"
11984                && let Ok(dir) = std::env::var("DSV4_LONGRUN_DUMP_DIR")
11985            {
11986                std::fs::write(format!("{dir}/rendered-131k.txt"), &rendered).unwrap();
11987                let csv: Vec<String> = ids.iter().map(|i| i.to_string()).collect();
11988                std::fs::write(format!("{dir}/memra-ids-131k.csv"), csv.join(",")).unwrap();
11989            }
11990        }
11991    }
11992
11993    #[test]
11994    fn models_v1_entry_advertises_thinking_support() {
11995        // Thinking model (step35 dialect: effort_levels): reasoning must be discoverable
11996        // from the contract-v2 capability booleans.
11997        let step_caps = ModelCaps {
11998            effort_levels: true,
11999            ..tool_caps()
12000        };
12001        let entry = model_entry_v1("stepfun/step-3.7-flash", Some(&step_caps), None);
12002        assert_eq!(entry["capabilities"]["reasoning"], true);
12003        assert_eq!(entry["capabilities"]["tools"], true);
12004
12005        // Non-thinking, non-tools model: neither capability may be advertised.
12006        let plain = ModelCaps {
12007            chat_ok: true,
12008            ..Default::default()
12009        };
12010        let entry = model_entry_v1("plain", Some(&plain), None);
12011        assert_eq!(entry["capabilities"]["reasoning"], false);
12012        assert_eq!(entry["capabilities"]["tools"], false);
12013        // Caps-unknown model: honest falses, streaming always true.
12014        let entry = model_entry_v1("unknown", None, None);
12015        assert_eq!(entry["capabilities"]["reasoning"], false);
12016        assert_eq!(entry["capabilities"]["streaming"], true);
12017    }
12018
12019    #[test]
12020    fn chat_request_preserves_turns_and_openai_stop_forms() {
12021        let payload = serde_json::json!({
12022            "model": "plain_quant",
12023            "messages": [
12024                {"role": "system", "content": "rules"},
12025                {"role": "developer", "content": "dev rules"},
12026                {"role": "user", "content": "task"},
12027                {"role": "assistant", "content": "work"}
12028            ],
12029            "max_tokens": 64,
12030            "temperature": 0.0,
12031            "stop": "<stop>"
12032        });
12033        let req: ChatCompletionReq = serde_json::from_value(payload).unwrap();
12034        let (tx, _rx) = worker::event_channel();
12035        let plan = build_chat_request(req, None, tx, lanes::Lane::Interactive, None).unwrap();
12036        let request = plan.request;
12037        assert!(
12038            plan.parser.is_none(),
12039            "no tools -> no parser (isolation contract)"
12040        );
12041        assert!(request.tools_json.is_empty());
12042        assert_eq!(request.think, ThinkMode::Default);
12043        assert_eq!(request.model, "plain_quant");
12044        assert_eq!(request.params.max_new, 64);
12045        // OMITTED max_tokens (gap-scan F2): the context-bounded sentinel, not 128.
12046        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
12047            "model": "plain_quant", "messages": [{"role": "user", "content": "task"}]
12048        }))
12049        .unwrap();
12050        let (tx, _rx) = worker::event_channel();
12051        let plan = build_chat_request(req, None, tx, lanes::Lane::Interactive, None).unwrap();
12052        assert_eq!(plan.request.params.max_new, worker::MAX_NEW_CTX_BOUNDED);
12053        // max_completion_tokens alias still honored exactly.
12054        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
12055            "model": "plain_quant", "messages": [{"role": "user", "content": "task"}],
12056            "max_completion_tokens": 7
12057        }))
12058        .unwrap();
12059        let (tx, _rx) = worker::event_channel();
12060        assert_eq!(
12061            build_chat_request(req, None, tx, lanes::Lane::Interactive, None)
12062                .unwrap()
12063                .request
12064                .params
12065                .max_new,
12066            7
12067        );
12068        // completions body: same omission law.
12069        let req: CompletionReq = serde_json::from_value(serde_json::json!({
12070            "model": "plain_quant", "prompt": "task"
12071        }))
12072        .unwrap();
12073        let (tx, _rx) = worker::event_channel();
12074        assert_eq!(
12075            build_request(&req, tx, lanes::Lane::Interactive, None)
12076                .params
12077                .max_new,
12078            worker::MAX_NEW_CTX_BOUNDED
12079        );
12080        let turns: Vec<(String, String)> = request
12081            .chat_turns
12082            .iter()
12083            .map(|t| (t.role.clone(), t.content.clone()))
12084            .collect();
12085        assert_eq!(
12086            turns,
12087            vec![
12088                ("system".into(), "rules".into()),
12089                ("system".into(), "dev rules".into()), // developer -> system normalization
12090                ("user".into(), "task".into()),
12091                ("assistant".into(), "work".into()),
12092            ]
12093        );
12094        assert!(request.chat_turns.iter().all(|t| t.tool_calls.is_empty()));
12095        assert_eq!(request.stop_strings, vec!["<stop>"]);
12096
12097        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
12098            "model": "plain_quant", "messages": [{"role": "user", "content": "task"}],
12099            "stop": ["a", "b"]
12100        }))
12101        .unwrap();
12102        assert_eq!(req.stop.into_vec(), vec!["a", "b"]);
12103
12104        // TOOTH (hermes finding, fixed 2026-08-23): an empty stop element matches every
12105        // decode ("".contains == always true; find("") == Some(0) truncated the whole
12106        // completion). Empties drop at ingestion; real elements survive.
12107        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
12108            "model": "plain_quant", "messages": [{"role": "user", "content": "task"}],
12109            "stop": ["", "real", ""]
12110        }))
12111        .unwrap();
12112        assert_eq!(req.stop.into_vec(), vec!["real"]);
12113        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
12114            "model": "plain_quant", "messages": [{"role": "user", "content": "task"}],
12115            "stop": ""
12116        }))
12117        .unwrap();
12118        assert!(req.stop.into_vec().is_empty());
12119
12120        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
12121            "model": "plain_quant", "messages": [{"role": "user", "content": "task"}],
12122            "stop": null
12123        }))
12124        .unwrap();
12125        assert!(req.stop.into_vec().is_empty());
12126    }
12127
12128    #[test]
12129    fn stop_sequence_limits_bound_count_individual_and_aggregate_work() {
12130        let at_limit = StopSequences::Many(vec!["x".repeat(256); MAX_STOP_SEQUENCES]);
12131        assert!(at_limit.validate().is_ok());
12132        assert!(
12133            StopSequences::Many(vec![String::new(); MAX_STOP_SEQUENCES + 1])
12134                .validate()
12135                .unwrap_err()
12136                .contains("at most")
12137        );
12138        assert!(
12139            StopSequences::One("x".repeat(MAX_STOP_SEQUENCE_BYTES + 1))
12140                .validate()
12141                .unwrap_err()
12142                .contains("each stop")
12143        );
12144        assert!(
12145            StopSequences::Many(vec!["x".repeat(300); MAX_STOP_SEQUENCES])
12146                .validate()
12147                .unwrap_err()
12148                .contains("total at most")
12149        );
12150    }
12151
12152    #[tokio::test]
12153    async fn chat_response_has_openai_message_shape() {
12154        let (tx, rx) = worker::event_channel();
12155        tx.send(Event::Token {
12156            id: 1,
12157            text: "hello".into(),
12158        })
12159        .unwrap();
12160        tx.send(Event::Done {
12161            stop_reason: "Eos".into(),
12162            n_tokens: 1,
12163            n_prompt: 42,
12164            n_cached: 30,
12165            elapsed_s: 0.5,
12166            spec: None,
12167        })
12168        .unwrap();
12169        drop(tx);
12170        let response = blocking_response(
12171            rx,
12172            "plain_quant".into(),
12173            true,
12174            Vec::new(),
12175            None,
12176            Envelope::new(true),
12177        )
12178        .await;
12179        assert_eq!(response.status(), StatusCode::OK);
12180        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
12181            .await
12182            .unwrap();
12183        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
12184        assert_eq!(payload["object"], "chat.completion");
12185        // OpenAI envelope (gap-scan F1): the official SDK pydantic-REQUIRES id + created.
12186        assert!(payload["id"].as_str().unwrap().starts_with("chatcmpl-"));
12187        assert!(payload["created"].as_u64().unwrap() > 1_700_000_000);
12188        // Shape, not prefix: `starts_with("memra-")` is what this line used to assert, and
12189        // `memra-unknown` passes that, which is how a meaningless fingerprint sat inside a
12190        // tested surface all the way to prod.
12191        let fingerprint = payload["system_fingerprint"].as_str().unwrap();
12192        assert!(
12193            build_id::fingerprint_is_well_formed(fingerprint),
12194            "system_fingerprint {fingerprint:?} is not memra-<version>-<12 hex>"
12195        );
12196        assert_eq!(payload["choices"][0]["message"]["role"], "assistant");
12197        assert_eq!(payload["choices"][0]["message"]["content"], "hello");
12198        assert_eq!(payload["choices"][0]["finish_reason"], "stop");
12199        // OpenAI prompt-caching usage schema (worker-truth cached vs computed split).
12200        assert_eq!(payload["usage"]["prompt_tokens"], 42);
12201        assert_eq!(payload["usage"]["completion_tokens"], 1);
12202        assert_eq!(payload["usage"]["total_tokens"], 43);
12203        assert_eq!(
12204            payload["usage"]["prompt_tokens_details"]["cached_tokens"],
12205            30
12206        );
12207        // ADDITIVE contract (lane/accept-telemetry): a non-spec request carries NO usage.spec
12208        // — the pre-lane usage object byte-for-byte.
12209        assert!(payload["usage"].get("spec").is_none());
12210    }
12211
12212    #[tokio::test]
12213    async fn native_response_uses_terminal_token_snapshot_for_coalesced_events() {
12214        let (tx, rx) = worker::event_channel();
12215        // A speculative round may commit four ids but expose one detokenized text delta.
12216        tx.send(Event::Token {
12217            id: 4,
12218            text: "hello".into(),
12219        })
12220        .unwrap();
12221        tx.send(Event::TokenSnapshot(vec![1, 2, 3, 4])).unwrap();
12222        tx.send(Event::Done {
12223            stop_reason: "MaxNew".into(),
12224            n_tokens: 4,
12225            n_prompt: 2,
12226            n_cached: 0,
12227            elapsed_s: 0.5,
12228            spec: None,
12229        })
12230        .unwrap();
12231        drop(tx);
12232
12233        let response = blocking_response(
12234            rx,
12235            "plain_quant".into(),
12236            false,
12237            Vec::new(),
12238            None,
12239            Envelope::new(false),
12240        )
12241        .await;
12242        assert_eq!(response.status(), StatusCode::OK);
12243        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
12244            .await
12245            .unwrap();
12246        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
12247        assert_eq!(payload["text"], "hello");
12248        assert_eq!(payload["tokens"], serde_json::json!([1, 2, 3, 4]));
12249        assert_eq!(payload["n_tokens"], 4);
12250    }
12251
12252    /// usage.spec (lane/accept-telemetry): spec-decode requests carry this request's own
12253    /// acceptance summary as an additive usage extension; every existing field is untouched.
12254    #[tokio::test]
12255    async fn chat_usage_carries_spec_acceptance_summary() {
12256        let (tx, rx) = worker::event_channel();
12257        tx.send(Event::Token {
12258            id: 1,
12259            text: "hello".into(),
12260        })
12261        .unwrap();
12262        tx.send(Event::Done {
12263            stop_reason: "Eos".into(),
12264            n_tokens: 1,
12265            n_prompt: 42,
12266            n_cached: 0,
12267            elapsed_s: 0.5,
12268            spec: Some(worker::SpecUsage {
12269                rounds: 10,
12270                drafted: 30,
12271                accepted: 21,
12272            }),
12273        })
12274        .unwrap();
12275        drop(tx);
12276        let response = blocking_response(
12277            rx,
12278            "plain_quant".into(),
12279            true,
12280            Vec::new(),
12281            None,
12282            Envelope::new(true),
12283        )
12284        .await;
12285        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
12286            .await
12287            .unwrap();
12288        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
12289        let sp = &payload["usage"]["spec"];
12290        assert_eq!(sp["rounds"], 10);
12291        assert_eq!(sp["drafted"], 30);
12292        assert_eq!(sp["accepted"], 21);
12293        assert!((sp["acceptance_rate"].as_f64().unwrap() - 0.7).abs() < 1e-9);
12294        // existing fields untouched next to the extension.
12295        assert_eq!(payload["usage"]["total_tokens"], 43);
12296    }
12297
12298    fn weather_request(extra: serde_json::Value) -> ChatCompletionReq {
12299        let mut payload = serde_json::json!({
12300            "model": "m",
12301            "messages": [{"role": "user", "content": "Weather in Paris?"}],
12302            "tools": [{"type": "function", "function": {
12303                "name": "get_weather",
12304                "description": "Get current weather",
12305                "parameters": {"type": "object",
12306                               "properties": {"city": {"type": "string"},
12307                                              "days": {"type": "integer"}},
12308                               "required": ["city"]}}}],
12309        });
12310        if let Some(obj) = extra.as_object() {
12311            for (k, v) in obj {
12312                payload[k] = v.clone();
12313            }
12314        }
12315        serde_json::from_value(payload).unwrap()
12316    }
12317
12318    /// glm5 twin of `vision_decode_is_deferred_and_grid_pinned`: the placeholder run is
12319    /// rendered from the header-planned grid; the decoded grid must equal it, and a
12320    /// mismatch refuses instead of desyncing runs from units (lane/glm5-vision).
12321    #[test]
12322    fn glm5_vision_decode_is_deferred_and_grid_pinned() {
12323        let (tx, _rx) = worker::event_channel();
12324        let req: ChatCompletionReq = serde_json::from_value(json!({
12325            "model": "m", "messages": [{"role": "user", "content": "hi"}],
12326        }))
12327        .unwrap();
12328        let mut plan = build_chat_request(
12329            req,
12330            Some(&ModelCaps {
12331                chat_ok: true,
12332                ..Default::default()
12333            }),
12334            tx,
12335            lanes::Lane::Interactive,
12336            None,
12337        )
12338        .unwrap();
12339        // 112x112 BMP: identity smart_resize (28-aligned, inside the 16..3072 budget) ->
12340        // grid 8x8 patches, 16 merged tokens (the det112 fixture geometry).
12341        let bmp = |w: u32, h: u32| -> Vec<u8> {
12342            let row = (w * 3).div_ceil(4) * 4;
12343            let size = 54 + row * h;
12344            let mut b = vec![0x42u8, 0x4d];
12345            b.extend_from_slice(&size.to_le_bytes());
12346            b.extend_from_slice(&[0; 4]);
12347            b.extend_from_slice(&54u32.to_le_bytes());
12348            b.extend_from_slice(&40u32.to_le_bytes());
12349            b.extend_from_slice(&w.to_le_bytes());
12350            b.extend_from_slice(&h.to_le_bytes());
12351            b.extend_from_slice(&1u16.to_le_bytes());
12352            b.extend_from_slice(&24u16.to_le_bytes());
12353            b.extend_from_slice(&[0u8; 24]);
12354            b.extend(std::iter::repeat_n(0x7fu8, (row * h) as usize));
12355            b
12356        };
12357        let bytes = bmp(112, 112);
12358        let (gh, gw) = memra_engine::vision_glm5::glm5_plan_image(&bytes).unwrap();
12359        assert_eq!((gh, gw), (8, 8), "identity resize grid");
12360        assert_eq!(memra_engine::vision_glm5::n_merged_for_grid(gh, gw), 16);
12361        plan.pending_glm5.push(PendingGlm5Image {
12362            bytes: bytes.clone(),
12363            gh,
12364            gw,
12365        });
12366        decode_pending_vision(&mut plan).unwrap();
12367        assert_eq!(plan.request.glm5_images.len(), 1);
12368        let unit = &plan.request.glm5_images[0];
12369        assert_eq!((unit.gh, unit.gw), (gh, gw));
12370        assert_eq!(
12371            unit.patches.len(),
12372            gh * gw * memra_engine::vision_glm5::G5V_PATCH_IN
12373        );
12374        // A grid mismatch refuses instead of desyncing placeholder runs from units.
12375        plan.request.glm5_images.clear();
12376        plan.pending_glm5.push(PendingGlm5Image {
12377            bytes,
12378            gh: gh + 2,
12379            gw,
12380        });
12381        let err = decode_pending_vision(&mut plan).unwrap_err();
12382        assert!(err.contains("header-planned"), "got: {err}");
12383    }
12384
12385    #[test]
12386    fn vision_decode_is_deferred_and_grid_pinned() {
12387        // TOOTH (hermes decode-bomb findings, fixed 2026-08-23): the plan phase renders
12388        // pad runs from HEADER dims only; canvases expand in decode_pending_vision,
12389        // which runs after admit_tenant_budget in chat_completions/admit_translated.
12390        // Build a plain plan, then drive phase 2 directly.
12391        let (tx, _rx) = worker::event_channel();
12392        let req: ChatCompletionReq = serde_json::from_value(json!({
12393            "model": "m", "messages": [{"role": "user", "content": "hi"}],
12394        }))
12395        .unwrap();
12396        let mut plan = build_chat_request(
12397            req,
12398            Some(&ModelCaps {
12399                chat_ok: true,
12400                ..Default::default()
12401            }),
12402            tx,
12403            lanes::Lane::Interactive,
12404            None,
12405        )
12406        .unwrap();
12407        // A planned still decodes into request.images when its grid matches the plan.
12408        // Hand-built 64x64 24bpp BMP (no image-crate dep in this crate): 54-byte header
12409        // + 64*64*3 pixel bytes (row stride 192 is 4-aligned, no padding).
12410        let bmp = |w: i32, h: i32, with_pixels: bool| -> Vec<u8> {
12411            let mut b = Vec::new();
12412            b.extend_from_slice(b"BM");
12413            b.extend_from_slice(&54u32.to_le_bytes());
12414            b.extend_from_slice(&0u32.to_le_bytes());
12415            b.extend_from_slice(&54u32.to_le_bytes());
12416            b.extend_from_slice(&40u32.to_le_bytes());
12417            b.extend_from_slice(&w.to_le_bytes());
12418            b.extend_from_slice(&h.to_le_bytes());
12419            b.extend_from_slice(&1u16.to_le_bytes());
12420            b.extend_from_slice(&24u16.to_le_bytes());
12421            b.extend_from_slice(&[0u8; 24]);
12422            if with_pixels {
12423                b.extend(std::iter::repeat_n(0x7fu8, (w * h * 3) as usize));
12424            }
12425            b
12426        };
12427        let bytes = bmp(64, 64, true);
12428        let (gh, gw) = memra_engine::vision_pre::plan_image_bytes(&bytes).unwrap();
12429        plan.pending_images.push(PendingVisionUnit::Still {
12430            bytes: bytes.clone(),
12431            gh,
12432            gw,
12433        });
12434        decode_pending_vision(&mut plan).unwrap();
12435        assert_eq!(plan.request.images.len(), 1);
12436        assert_eq!(
12437            (
12438                plan.request.images[0].prep.gh,
12439                plan.request.images[0].prep.gw
12440            ),
12441            (gh, gw),
12442            "decoded grid must equal the header-planned grid the pad run was rendered from"
12443        );
12444        // A grid mismatch refuses instead of desyncing pad runs from units.
12445        plan.request.images.clear();
12446        plan.pending_images.push(PendingVisionUnit::Still {
12447            bytes,
12448            gh: gh + 2,
12449            gw,
12450        });
12451        let err = decode_pending_vision(&mut plan).unwrap_err();
12452        assert!(err.contains("header-planned"), "got: {err}");
12453        // Defense in depth: even if a bomb reached phase 2, the decode re-admits the
12454        // header budget and refuses pre-decode with the named error.
12455        let bomb = bmp(16_000, 16_000, false);
12456        plan.pending_images.clear();
12457        plan.pending_images.push(PendingVisionUnit::Still {
12458            bytes: bomb,
12459            gh: 2,
12460            gw: 2,
12461        });
12462        let err = decode_pending_vision(&mut plan).unwrap_err();
12463        assert!(err.contains("exceeds the decode budget"), "got: {err}");
12464    }
12465
12466    #[test]
12467    fn tools_request_renders_client_key_order_and_arms_parser() {
12468        let (tx, _rx) = worker::event_channel();
12469        let plan = build_chat_request(
12470            weather_request(json!({})),
12471            Some(&tool_caps()),
12472            tx,
12473            lanes::Lane::Interactive,
12474            None,
12475        )
12476        .unwrap();
12477        assert!(plan.parser.is_some());
12478        assert_eq!(plan.request.tools_json.len(), 1);
12479        // client key order preserved + python-dumps separators (the template's tojson law).
12480        assert_eq!(
12481            plan.request.tools_json[0],
12482            "{\"type\": \"function\", \"function\": {\"name\": \"get_weather\", \
12483             \"description\": \"Get current weather\", \"parameters\": {\"type\": \"object\", \
12484             \"properties\": {\"city\": {\"type\": \"string\"}, \"days\": {\"type\": \
12485             \"integer\"}}, \"required\": [\"city\"]}}}"
12486        );
12487    }
12488
12489    #[test]
12490    fn hy3_tools_and_reasoning_flow_through_the_real_chat_plan() {
12491        let (tx, _rx) = worker::event_channel();
12492        let plan = build_chat_request(
12493            weather_request(json!({"reasoning_effort": "high"})),
12494            Some(&hy3_tool_caps()),
12495            tx,
12496            lanes::Lane::Interactive,
12497            None,
12498        )
12499        .unwrap();
12500        assert_eq!(plan.request.think, ThinkMode::Think);
12501        assert_eq!(plan.request.reasoning_effort.as_deref(), Some("high"));
12502        assert!(
12503            plan.request
12504                .stop_strings
12505                .iter()
12506                .any(|stop| stop == "</tool_calls:opensource>")
12507        );
12508        let rendered = chat::apply_chat_template_tools_ex(
12509            Some("... hy_User ... <tools> ..."),
12510            &plan.request.chat_turns,
12511            true,
12512            &plan.request.tools_json,
12513            &plan.request.tools_struct,
12514            plan.request.think,
12515            plan.request.reasoning_effort.as_deref(),
12516            None,
12517        )
12518        .unwrap();
12519        assert!(rendered.contains("<tool_calls:opensource>"));
12520        assert!(rendered.ends_with("<think:opensource>"));
12521
12522        let mut parser = plan.parser.expect("HY3 tools arm its native parser");
12523        let pieces = parser.push(concat!(
12524            "Need weather.</think:opensource>",
12525            "<tool_calls:opensource><tool_call:opensource>get_weather",
12526            "<tool_sep:opensource>\n<arg_key:opensource>city</arg_key:opensource>\n",
12527            "<arg_value:opensource>Paris</arg_value:opensource>\n",
12528            "</tool_call:opensource></tool_calls:opensource>",
12529        ));
12530        assert!(pieces.contains(&Piece::Reasoning("Need weather.".into())));
12531        assert!(pieces.iter().any(|piece| matches!(piece, Piece::Call(call)
12532            if call.name == "get_weather" && call.arguments == r#"{"city":"Paris"}"#)));
12533    }
12534
12535    #[test]
12536    fn tool_choice_none_strips_tools_and_parser() {
12537        let (tx, _rx) = worker::event_channel();
12538        let plan = build_chat_request(
12539            weather_request(json!({"tool_choice": "none"})),
12540            Some(&tool_caps()),
12541            tx,
12542            lanes::Lane::Interactive,
12543            None,
12544        )
12545        .unwrap();
12546        // tools stripped: no tool-call scanning; the think-open prompt still arms the
12547        // reasoning-only splitter (F13) — a <tool_call> in post-think prose stays prose.
12548        let mut p = plan
12549            .parser
12550            .expect("think-open chat arms the reasoning splitter");
12551        let pieces = p.push("x</think>\n\n<tool_call> stays prose");
12552        assert_eq!(
12553            pieces,
12554            vec![
12555                Piece::Reasoning("x".into()),
12556                Piece::Content("<tool_call> stays prose".into()),
12557            ]
12558        );
12559        assert!(plan.request.tools_json.is_empty());
12560        // unsupported tool_choice forms are clean 400s, not silent downgrades.
12561        let (tx, _rx) = worker::event_channel();
12562        assert!(
12563            build_chat_request(
12564                weather_request(json!({"tool_choice": "required"})),
12565                Some(&tool_caps()),
12566                tx,
12567                lanes::Lane::Interactive,
12568                None
12569            )
12570            .is_err()
12571        );
12572        let (tx, _rx) = worker::event_channel();
12573        assert!(
12574            build_chat_request(
12575                weather_request(json!({"tool_choice":
12576            {"type": "function", "function": {"name": "get_weather"}}})),
12577                Some(&tool_caps()),
12578                tx,
12579                lanes::Lane::Interactive,
12580                None
12581            )
12582            .is_err()
12583        );
12584    }
12585
12586    #[test]
12587    fn model_plan_accepts_st_dir_and_rejects_bogus_dir() {
12588        let root = std::env::temp_dir().join(format!("memra_plan_test_{}", std::process::id()));
12589        let _ = std::fs::remove_dir_all(&root);
12590
12591        // (a) single-file ST checkpoint dir: config.json + model.safetensors.
12592        let st = root.join("st_single");
12593        std::fs::create_dir_all(&st).unwrap();
12594        std::fs::write(st.join("config.json"), "{}").unwrap();
12595        std::fs::write(st.join("model.safetensors"), b"x").unwrap();
12596        assert!(validate_model_path(st.to_str().unwrap()).is_ok());
12597
12598        // (b) sharded ST checkpoint dir: config.json + model.safetensors.index.json.
12599        let sh = root.join("st_sharded");
12600        std::fs::create_dir_all(&sh).unwrap();
12601        std::fs::write(sh.join("config.json"), "{}").unwrap();
12602        std::fs::write(sh.join("model.safetensors.index.json"), "{}").unwrap();
12603        assert!(validate_model_path(sh.to_str().unwrap()).is_ok());
12604
12605        // (c) repack dir: manifest.json alone qualifies.
12606        let rp = root.join("repack");
12607        std::fs::create_dir_all(&rp).unwrap();
12608        std::fs::write(rp.join("manifest.json"), "{}").unwrap();
12609        assert!(validate_model_path(rp.to_str().unwrap()).is_ok());
12610
12611        // (d) bogus dir (no weights): clear error naming what was expected.
12612        let bogus = root.join("bogus");
12613        std::fs::create_dir_all(&bogus).unwrap();
12614        let err = validate_model_path(bogus.to_str().unwrap()).unwrap_err();
12615        assert!(
12616            err.contains("model.safetensors"),
12617            "error should say what is missing: {err}"
12618        );
12619        assert!(
12620            err.contains("manifest.json"),
12621            "error should mention the repack form: {err}"
12622        );
12623
12624        // (e) ST weights but no config.json: distinct clear error.
12625        let nc = root.join("no_config");
12626        std::fs::create_dir_all(&nc).unwrap();
12627        std::fs::write(nc.join("model.safetensors"), b"x").unwrap();
12628        let err = validate_model_path(nc.to_str().unwrap()).unwrap_err();
12629        assert!(
12630            err.contains("config.json"),
12631            "error should name config.json: {err}"
12632        );
12633
12634        // (f) nonexistent path.
12635        let err = validate_model_path(root.join("nowhere").to_str().unwrap()).unwrap_err();
12636        assert!(err.contains("does not exist"), "{err}");
12637
12638        // (g) plain file = GGUF branch, accepted as-is.
12639        let f = root.join("model.gguf");
12640        std::fs::write(&f, b"g").unwrap();
12641        assert!(validate_model_path(f.to_str().unwrap()).is_ok());
12642
12643        let _ = std::fs::remove_dir_all(&root);
12644    }
12645
12646    #[test]
12647    fn chat_on_templateless_dir_checkpoint_is_rejected_with_clear_message() {
12648        // serve-st v1 honesty gate: a dir checkpoint whose tokenizer carries no chat
12649        // template probes chat_ok=false -> every chat request 400s BEFORE the worker.
12650        let caps = ModelCaps {
12651            tools_branch: false,
12652            qwen_think: false,
12653            think_switch: false,
12654            chat_ok: false,
12655            ..Default::default()
12656        };
12657        let payload = serde_json::json!({
12658            "model": "st_model",
12659            "messages": [{"role": "user", "content": "hello"}],
12660        });
12661        let req: ChatCompletionReq = serde_json::from_value(payload).unwrap();
12662        let (tx, _rx) = worker::event_channel();
12663        let err = match build_chat_request(req, Some(&caps), tx, lanes::Lane::Interactive, None) {
12664            Err(e) => e,
12665            Ok(_) => panic!("templateless dir checkpoint must reject chat"),
12666        };
12667        assert!(
12668            err.contains("no chat template"),
12669            "message should name the cause: {err}"
12670        );
12671        assert!(
12672            err.contains("/v1/completions"),
12673            "message should point at the raw-prompt escape hatch: {err}"
12674        );
12675    }
12676
12677    #[test]
12678    fn tools_on_model_without_tools_branch_is_rejected() {
12679        let (tx, _rx) = worker::event_channel();
12680        let caps = ModelCaps {
12681            chat_ok: true,
12682            ..Default::default()
12683        };
12684        assert!(
12685            build_chat_request(
12686                weather_request(json!({})),
12687                Some(&caps),
12688                tx,
12689                lanes::Lane::Interactive,
12690                None
12691            )
12692            .is_err()
12693        );
12694        let (tx, _rx) = worker::event_channel();
12695        assert!(
12696            build_chat_request(
12697                weather_request(json!({})),
12698                None,
12699                tx,
12700                lanes::Lane::Interactive,
12701                None
12702            )
12703            .is_err()
12704        );
12705    }
12706
12707    #[test]
12708    fn reasoning_effort_maps_to_think_switch() {
12709        // The reasoning-capable-model convention (owner directive 2026-08-07):
12710        // low|medium|high = thinking ON at that budget; none|minimal = thinking OFF;
12711        // absent = the model's own default. `low` used to map to NoThink — that read the
12712        // OpenAI field as a "how much" dial with off at the bottom, which contradicts how
12713        // reasoning models ship (low IS a reasoning mode).
12714        for (extra, want) in [
12715            (json!({}), ThinkMode::Default),
12716            (json!({"reasoning_effort": "low"}), ThinkMode::Think),
12717            (json!({"reasoning_effort": "none"}), ThinkMode::NoThink),
12718            (json!({"reasoning_effort": "minimal"}), ThinkMode::NoThink),
12719            (json!({"reasoning_effort": "high"}), ThinkMode::Think),
12720            (json!({"reasoning_effort": "medium"}), ThinkMode::Think),
12721            (json!({"reasoning": {"enabled": false}}), ThinkMode::NoThink),
12722            (json!({"reasoning": {"effort": "low"}}), ThinkMode::Think),
12723            (json!({"reasoning": {"enabled": true}}), ThinkMode::Think),
12724            // Clamp aliases (issue #31): levels above "high" mean thinking ON at the
12725            // highest level any loaded template distinguishes. Real default-config
12726            // clients send these (codex xhigh; Claude Code xhigh via /v1/messages).
12727            (json!({"reasoning_effort": "xhigh"}), ThinkMode::Think),
12728            (json!({"reasoning_effort": "max"}), ThinkMode::Think),
12729            (json!({"reasoning_effort": "ultra"}), ThinkMode::Think),
12730            // Explicit-switch precedence (issue #31): enabled/disabled — the field
12731            // Anthropic thinking.type translates onto — wins over the switch the
12732            // effort level implies.
12733            (
12734                json!({"reasoning": {"enabled": true, "effort": "none"}}),
12735                ThinkMode::Think,
12736            ),
12737            (
12738                json!({"reasoning": {"enabled": false, "effort": "high"}}),
12739                ThinkMode::NoThink,
12740            ),
12741        ] {
12742            let (tx, _rx) = worker::event_channel();
12743            let plan = build_chat_request(
12744                weather_request(extra.clone()),
12745                // A LADDER-carrying model (qwen3.8 shape), so every rung of the table is
12746                // exercised as a real render input here. On a model with no depth input the
12747                // same rungs TRANSLATE onto the binary axis as reasoning ON — that mapping has
12748                // its own test (`a_graded_level_on_a_binary_model_translates_to_reasoning_on`).
12749                Some(&ladder_caps()),
12750                tx,
12751                lanes::Lane::Interactive,
12752                None,
12753            )
12754            .unwrap();
12755            assert_eq!(plan.request.think, want, "extra={extra}");
12756        }
12757        // An out-of-table value is a 400 on EVERY expression of the field — including
12758        // next to an explicit switch (the old enabled==false early-return skipped
12759        // validation, the same silent-accept class /v1/messages had in issue #31).
12760        for extra in [
12761            json!({"reasoning_effort": "extreme"}),
12762            json!({"reasoning": {"effort": "banana"}}),
12763            json!({"reasoning": {"enabled": false, "effort": "banana"}}),
12764            json!({"reasoning": {"enabled": true, "effort": ""}}),
12765        ] {
12766            let (tx, _rx) = worker::event_channel();
12767            assert!(
12768                build_chat_request(
12769                    weather_request(extra.clone()),
12770                    Some(&tool_caps()),
12771                    tx,
12772                    lanes::Lane::Interactive,
12773                    None
12774                )
12775                .is_err(),
12776                "extra={extra} must be rejected by the one allowlist"
12777            );
12778        }
12779        // The clamp really lands on "high" for level-consuming templates, and the
12780        // whole canonical table is what `canonical_effort` says it is.
12781        for (raw, want) in [
12782            ("none", Some("none")),
12783            ("minimal", Some("minimal")),
12784            ("low", Some("low")),
12785            ("medium", Some("medium")),
12786            ("high", Some("high")),
12787            ("xhigh", Some("high")),
12788            ("max", Some("high")),
12789            ("ultra", Some("high")),
12790            ("banana", None),
12791            ("", None),
12792            ("HIGH", None),
12793        ] {
12794            assert_eq!(canonical_effort(raw), want, "canonical_effort({raw:?})");
12795        }
12796        // dsv4 exemption (hermes 2026-08-23): the one template with a rung above "high"
12797        // gets the above-high aliases as "max"; the rest of the table is identical.
12798        for (raw, want) in [
12799            ("none", Some("none")),
12800            ("minimal", Some("minimal")),
12801            ("low", Some("low")),
12802            ("medium", Some("medium")),
12803            ("high", Some("high")),
12804            ("xhigh", Some("max")),
12805            ("max", Some("max")),
12806            ("ultra", Some("max")),
12807            ("banana", None),
12808            ("", None),
12809            ("MAX", None),
12810        ] {
12811            assert_eq!(
12812                canonical_effort_for(raw, true),
12813                want,
12814                "canonical_effort_for({raw:?}, dsv4)"
12815            );
12816        }
12817    }
12818
12819    #[test]
12820    fn dsv4_reasoning_effort_max_survives_canonicalization() {
12821        // TOOTH (hermes finding e98463…/parse_think-collapse, fixed 2026-08-23): dsv4's
12822        // 0731 encoding renders DIFFERENT prompt prefixes for "high" (ABSOLUTE_MAX) and
12823        // "max" (BEYOND_MAX) — collapsing max->high at the server silently discarded the
12824        // top tier. A dsv4-caps plan must carry "max" through to the renderer; every
12825        // non-dsv4 template still clamps to "high".
12826        let dsv4_caps = ModelCaps {
12827            chat_ok: true,
12828            dsv4: true,
12829            ..Default::default()
12830        };
12831        let build = |caps: &ModelCaps, effort: &str| {
12832            let (tx, _rx) = worker::event_channel();
12833            let req: ChatCompletionReq = serde_json::from_value(json!({
12834                "model": "m",
12835                "messages": [{"role": "user", "content": "hi"}],
12836                "reasoning_effort": effort,
12837            }))
12838            .unwrap();
12839            build_chat_request(req, Some(caps), tx, lanes::Lane::Interactive, None)
12840        };
12841        for raw in ["max", "xhigh", "ultra"] {
12842            let plan = build(&dsv4_caps, raw).unwrap();
12843            assert_eq!(
12844                plan.request.reasoning_effort.as_deref(),
12845                Some("max"),
12846                "dsv4 {raw:?} must reach the renderer as the max rung"
12847            );
12848            assert_eq!(plan.request.think, chat::ThinkMode::Think);
12849        }
12850        // "high" stays "high" on dsv4 (a distinct rung, not an alias).
12851        let plan = build(&dsv4_caps, "high").unwrap();
12852        assert_eq!(plan.request.reasoning_effort.as_deref(), Some("high"));
12853        // Non-dsv4 level-consuming template: above-high still clamps to "high".
12854        let step_caps = ModelCaps {
12855            chat_ok: true,
12856            effort_levels: true,
12857            ..Default::default()
12858        };
12859        let plan = build(&step_caps, "max").unwrap();
12860        assert_eq!(plan.request.reasoning_effort.as_deref(), Some("high"));
12861    }
12862
12863    #[test]
12864    fn default_reasoning_effort_flips_only_the_unset_request() {
12865        // Owner ruling 2026-08-19 (darklanes gemma GPQA recovery board, step 2): gemma-4
12866        // serves think-ON by default — 80.81 GPQA think-on vs 76.26 think-off on the
12867        // served mint. Mechanism: a per-model MEMRA_MODEL_METADATA knob
12868        // (`default_reasoning_effort`) resolved at plan build. ONLY a request that
12869        // expressed no reasoning preference flips; every explicit client choice is
12870        // honored unchanged.
12871        let build = |extra: serde_json::Value, default_effort: Option<&str>| {
12872            let (tx, _rx) = worker::event_channel();
12873            build_chat_request_with_trace(
12874                weather_request(extra),
12875                Some(&ladder_caps()),
12876                tx,
12877                lanes::Lane::Interactive,
12878                None,
12879                None,
12880                default_effort,
12881                &ModelSamplingDefaults::default(),
12882            )
12883            .unwrap()
12884        };
12885        for (extra, want) in [
12886            // the ONE case the knob owns: nothing expressed on either surface.
12887            (json!({}), ThinkMode::Think),
12888            // `reasoning.exclude:true` is no longer "unset" and no longer a display flag: it
12889            // is an OFF-switch (owner ruling 2026-08-23 — not delivering reasoning means not
12890            // generating it), so it beats the operator default exactly like reasoning.enabled.
12891            (json!({"reasoning": {"exclude": true}}), ThinkMode::NoThink),
12892            (json!({"include_reasoning": false}), ThinkMode::NoThink),
12893            // ...and the "deliver it" direction expresses no switch, so the default still wins.
12894            (json!({"reasoning": {"exclude": false}}), ThinkMode::Think),
12895            (json!({"include_reasoning": true}), ThinkMode::Think),
12896            // explicit OFF stays off, on both surfaces.
12897            (json!({"reasoning_effort": "none"}), ThinkMode::NoThink),
12898            (json!({"reasoning_effort": "minimal"}), ThinkMode::NoThink),
12899            (json!({"reasoning": {"enabled": false}}), ThinkMode::NoThink),
12900            // explicit ON stays exactly the client's request.
12901            (json!({"reasoning_effort": "low"}), ThinkMode::Think),
12902            (json!({"reasoning_effort": "high"}), ThinkMode::Think),
12903            (json!({"reasoning": {"enabled": true}}), ThinkMode::Think),
12904        ] {
12905            let plan = build(extra.clone(), Some("high"));
12906            assert_eq!(plan.request.think, want, "extra={extra}");
12907        }
12908        // the knob can also pin thinking OFF by default; explicit ON still wins over it.
12909        assert_eq!(
12910            build(json!({}), Some("none")).request.think,
12911            ThinkMode::NoThink
12912        );
12913        assert_eq!(
12914            build(json!({"reasoning_effort": "high"}), Some("none"))
12915                .request
12916                .think,
12917            ThinkMode::Think
12918        );
12919        // no knob (every model without a metadata entry — qwen etc.): unset stays the
12920        // template's own default. Together with `reasoning_effort_maps_to_think_switch`
12921        // above, this is the byte-identical regression guard for knobless deployments.
12922        assert_eq!(build(json!({}), None).request.think, ThinkMode::Default);
12923    }
12924
12925    /// A qwen-class template that carries all three markers the renderer keys on:
12926    /// `<think>` + `add_generation_prompt` (think tail), `enable_thinking` (the switch),
12927    /// `<tools>` (tools branch). Shape-equivalent to the deployed q38 / ornith15 GGUF
12928    /// templates, whose live `think_switch=true` is receipted in darklanes
12929    /// research/reasoning-control-20260823/THINKING.md.
12930    const SWITCHED_QWEN_TMPL: &str = "<tools> ... add_generation_prompt ... \
12931         {%- if enable_thinking is defined and enable_thinking is false %}'<think>\\n\\n</think>\\n\\n'\
12932         {%- else %}'<think>\\n'{%- endif %}";
12933
12934    #[test]
12935    fn vllm_enable_thinking_switch_is_wired_not_ignored() {
12936        // THE DEFECT THIS CLOSES (lane/reasoning-control-20260823): `ChatCompletionReq` has
12937        // no `deny_unknown_fields`, so the whole vLLM-shaped ecosystem's thinking switch —
12938        // top-level `enable_thinking` and `chat_template_kwargs.enable_thinking` — was
12939        // deserialized away and the request served with reasoning ON behind a 200. Measured
12940        // on the live endpoint against both served models before the fix.
12941        let build = |extra: serde_json::Value| {
12942            let (tx, _rx) = worker::event_channel();
12943            build_chat_request(
12944                weather_request(extra),
12945                Some(&tool_caps()),
12946                tx,
12947                lanes::Lane::Interactive,
12948                None,
12949            )
12950        };
12951        for (extra, want) in [
12952            (json!({"enable_thinking": false}), ThinkMode::NoThink),
12953            (json!({"enable_thinking": true}), ThinkMode::Think),
12954            (
12955                json!({"chat_template_kwargs": {"enable_thinking": false}}),
12956                ThinkMode::NoThink,
12957            ),
12958            (
12959                json!({"chat_template_kwargs": {"enable_thinking": true}}),
12960                ThinkMode::Think,
12961            ),
12962            // the vLLM switch is an EXPLICIT switch, so it beats the switch an effort level
12963            // implies — the same precedence `reasoning.enabled` already had (issue #31).
12964            (
12965                json!({"enable_thinking": false, "reasoning_effort": "high"}),
12966                ThinkMode::NoThink,
12967            ),
12968            // agreement between the two spellings is fine.
12969            (
12970                json!({"enable_thinking": false,
12971                       "chat_template_kwargs": {"enable_thinking": false}}),
12972                ThinkMode::NoThink,
12973            ),
12974        ] {
12975            let plan = build(extra.clone()).unwrap_or_else(|e| {
12976                panic!("{extra} must be accepted and honored, got 400: {e}");
12977            });
12978            assert_eq!(
12979                plan.request.think, want,
12980                "{extra} was ACCEPTED AND IGNORED — the banned silent-accept class"
12981            );
12982        }
12983        // and it reaches the PROMPT BYTES, not just the plan: the closed think pair is what
12984        // the template's `enable_thinking is false` branch emits.
12985        let render = |extra: serde_json::Value| -> String {
12986            let plan = build(extra).unwrap();
12987            chat::apply_chat_template_tools_ex(
12988                Some(SWITCHED_QWEN_TMPL),
12989                &plan.request.chat_turns,
12990                true,
12991                &plan.request.tools_json,
12992                &plan.request.tools_struct,
12993                plan.request.think,
12994                plan.request.reasoning_effort.as_deref(),
12995                None,
12996            )
12997            .unwrap()
12998        };
12999        let off = render(json!({"enable_thinking": false}));
13000        assert!(
13001            off.ends_with("<|im_start|>assistant\n<think>\n\n</think>\n\n"),
13002            "enable_thinking:false must render the CLOSED think pair: {off:?}"
13003        );
13004        let on = render(json!({}));
13005        assert!(
13006            on.ends_with("<|im_start|>assistant\n<think>\n"),
13007            "an unset request must still render the template's OPEN think tail: {on:?}"
13008        );
13009        assert_eq!(
13010            off,
13011            render(json!({"chat_template_kwargs": {"enable_thinking": false}})),
13012            "both vLLM spellings must render byte-identically"
13013        );
13014        assert_eq!(
13015            off,
13016            render(json!({"reasoning_effort": "none"})),
13017            "the vLLM spelling must render byte-identically to the OpenAI spelling"
13018        );
13019    }
13020
13021    #[test]
13022    fn unknown_chat_template_kwarg_refuses_by_name() {
13023        // This renderer is Rust, not jinja: a kwarg it does not implement changes nothing
13024        // about the prompt, so accepting it with 200 is the same defect one level down.
13025        let build = |extra: serde_json::Value| {
13026            let (tx, _rx) = worker::event_channel();
13027            build_chat_request(
13028                weather_request(extra),
13029                Some(&tool_caps()),
13030                tx,
13031                lanes::Lane::Interactive,
13032                None,
13033            )
13034        };
13035        let refusal = |extra: serde_json::Value, why: &str| -> String {
13036            build(extra).err().unwrap_or_else(|| panic!("{why}"))
13037        };
13038        let err = refusal(
13039            json!({"chat_template_kwargs": {"add_generation_prompt": false}}),
13040            "an unimplementable template kwarg must not be accepted",
13041        );
13042        assert!(
13043            err.contains("add_generation_prompt") && err.contains("enable_thinking"),
13044            "the refusal must name the offending key AND the supported one: {err}"
13045        );
13046        let err = refusal(
13047            json!({"chat_template_kwargs": "enable_thinking=false"}),
13048            "a non-object chat_template_kwargs must not be accepted",
13049        );
13050        assert!(
13051            err.contains("must be an object"),
13052            "refusal must say what shape is expected: {err}"
13053        );
13054        let err = refusal(
13055            json!({"chat_template_kwargs": {"enable_thinking": "false"}}),
13056            "a stringly-typed switch must not be accepted",
13057        );
13058        assert!(
13059            err.contains("true or false"),
13060            "refusal must name the expected type: {err}"
13061        );
13062        // an explicitly-null kwargs bag is "nothing expressed", not an error.
13063        let plan = build(json!({"chat_template_kwargs": null}))
13064            .expect("null chat_template_kwargs is the unset case");
13065        assert_eq!(plan.request.think, ThinkMode::Default);
13066    }
13067
13068    // ============ THE ONE REASONING SCHEMA (lane/reasoning-schema-20260823) ===============
13069    //
13070    // Owner rulings this section enforces, in their order of severity:
13071    //   1. a reasoning parameter that returns 200 must have an EFFECT — measured on prompt bytes;
13072    //   2. every surface spelling maps into ONE internal schema, identically on all three APIs;
13073    //   3. asking for non-reasoning and getting reasoning is impossible — off is a real
13074    //      generation decision, and where it cannot be honoured it is a named 400;
13075    //   4. reasoning is compute and output, so it is never withheld after being billed.
13076    //
13077    // The lab is the authority on each model's controls (never inferred from lineage or a shared
13078    // loader): Qwen/Qwen3.8-27B's card documents `reasoning_effort` = xhigh (default) | medium |
13079    // low; Ornith AI documents `enable_thinking` and nothing else.
13080
13081    /// The DEPLOYED qwen3.8 template, byte-identical in the BF16 and NVFP4-Q5K mints.
13082    const Q38_TMPL: &str =
13083        include_str!("../../../research/reasoning-schema-20260823/qwen38-27b.chat_template.jinja");
13084
13085    /// Build a plan and render it through the template the caps describe — the only assertion
13086    /// that cannot lie about whether a parameter had an effect.
13087    fn render_with(
13088        tmpl: &str,
13089        caps: &ModelCaps,
13090        extra: serde_json::Value,
13091        default_effort: Option<&str>,
13092    ) -> Result<String, String> {
13093        let mut payload = serde_json::json!({
13094            "model": "m",
13095            "messages": [{"role": "user", "content": "hi"}],
13096        });
13097        if let Some(obj) = extra.as_object() {
13098            for (k, v) in obj {
13099                payload[k] = v.clone();
13100            }
13101        }
13102        let req: ChatCompletionReq = serde_json::from_value(payload).unwrap();
13103        let (tx, _rx) = worker::event_channel();
13104        let plan = build_chat_request_with_trace(
13105            req,
13106            Some(caps),
13107            tx,
13108            lanes::Lane::Interactive,
13109            None,
13110            None,
13111            default_effort,
13112            &ModelSamplingDefaults::default(),
13113        )?;
13114        Ok(chat::apply_chat_template_tools_ex(
13115            Some(tmpl),
13116            &plan.request.chat_turns,
13117            true,
13118            &plan.request.tools_json,
13119            &plan.request.tools_struct,
13120            plan.request.think,
13121            plan.request.reasoning_effort.as_deref(),
13122            None,
13123        )
13124        .unwrap())
13125    }
13126
13127    #[test]
13128    fn qwen38_effort_ladder_reaches_prompt_bytes_through_the_whole_api() {
13129        // THE HEADLINE DEFECT. `reasoning_effort: low|medium|high` was parsed, validated, and
13130        // then DISCARDED on every qwen3.8 request: the delivery gate asked for
13131        // `effort_levels || dsv4`, and `effort_levels` probes the substring
13132        // `reasoning_effort is defined`, which this template does not contain (it spells its
13133        // input `reasoning_effort|default('xhigh')`). So the level never reached the render and
13134        // the template's own `xhigh` default never rendered either.
13135        let r = |extra: serde_json::Value| render_with(Q38_TMPL, &ladder_caps(), extra, None);
13136        let xhigh = "Reasoning effort is set to xhigh.";
13137        let low = "Reasoning effort is set to low.";
13138        // Each rung lands on the sentence the VENDOR's template defines for it.
13139        assert!(r(json!({"reasoning_effort": "low"})).unwrap().contains(low));
13140        assert!(
13141            r(json!({"reasoning_effort": "high"}))
13142                .unwrap()
13143                .contains(xhigh)
13144        );
13145        // `medium` is the vendor's zero-steering rung: it injects nothing at all. That is the
13146        // template's own choice, and it is ALSO the byte history of every pre-lane q38 request.
13147        let medium = r(json!({"reasoning_effort": "medium"})).unwrap();
13148        assert!(!medium.contains("Reasoning effort is set to"), "{medium:?}");
13149        // ...so the three rungs are three DIFFERENT prompts. Effect, proven on bytes.
13150        let low_p = r(json!({"reasoning_effort": "low"})).unwrap();
13151        let high_p = r(json!({"reasoning_effort": "high"})).unwrap();
13152        assert_ne!(low_p, high_p);
13153        assert_ne!(low_p, medium);
13154        assert_ne!(high_p, medium);
13155        // The clamp aliases are ONE rung by the vendor's own hosted-API mapping (high/max/xhigh
13156        // -> xhigh), so they must not become a fourth prompt.
13157        for alias in ["xhigh", "max", "ultra"] {
13158            assert_eq!(r(json!({"reasoning_effort": alias})).unwrap(), high_p);
13159        }
13160        // THE SERVING-BEHAVIOUR CHANGE, pinned so it cannot land unnoticed: an UNSET request
13161        // now renders the vendor's xhigh default, where before it rendered nothing.
13162        assert_eq!(r(json!({})).unwrap(), high_p);
13163        // ...and the documented no-op migration: an operator default of "medium" restores the
13164        // exact pre-lane bytes without touching a line of code.
13165        assert_eq!(
13166            render_with(Q38_TMPL, &ladder_caps(), json!({}), Some("medium")).unwrap(),
13167            medium
13168        );
13169        // Thinking OFF carries no effort sentence even with a level named — the vendor wraps the
13170        // whole instruction block in `enable_thinking is undefined or is true`.
13171        let off = r(json!({"reasoning_effort": "none"})).unwrap();
13172        assert!(off.ends_with("<think>\n\n</think>\n\n"), "{off:?}");
13173        assert!(!off.contains("Reasoning effort is set to"), "{off:?}");
13174    }
13175
13176    #[test]
13177    fn the_effort_sentence_is_measurable_on_the_deployed_binary_without_a_deploy() {
13178        // METHODOLOGY GATE for the live cell in darklanes
13179        // research/reasoning-schema-20260823/SCHEMA.md §5. That measurement had to answer "does
13180        // each rung change what the model DOES" against a binary that predates this branch, so it
13181        // sent each rung's instruction sentence as a SYSTEM MESSAGE instead. That is only a valid
13182        // substitute if the two render the same bytes — otherwise the numbers describe a prompt no
13183        // customer will ever get and the whole cell is decoration.
13184        //
13185        // Note WHERE the ladder is keyed, because a first attempt at this test got it wrong: the
13186        // renderer probes the TEMPLATE (`template_has_qwen_effort`), while `ModelCaps::qwen_effort`
13187        // only decides whether the level STRING is handed to it. So "the deployed binary" cannot be
13188        // modelled by clearing the cap — it is modelled by a template that carries no ladder at
13189        // all, which is what the pre-lane renderer effectively was.
13190        const LOW_SENTENCE: &str = "Reasoning effort is set to low. Keep your thinking brief and \
13191focused, moving directly to the conclusion without unnecessary elaboration.";
13192        let expected = format!(
13193            "<|im_start|>system\n{LOW_SENTENCE}<|im_end|>\n\
13194             <|im_start|>user\nhi<|im_end|>\n<|im_start|>assistant\n<think>\n"
13195        );
13196        // RIGHT SIDE — this branch: the level, no system message.
13197        let after_fix = render_with(
13198            Q38_TMPL,
13199            &ladder_caps(),
13200            json!({"reasoning_effort": "low"}),
13201            None,
13202        )
13203        .unwrap();
13204        assert_eq!(
13205            after_fix, expected,
13206            "the shipped prompt for reasoning_effort:\"low\""
13207        );
13208        // LEFT SIDE — a ladder-less template, sentence carried in a system message: byte-identical,
13209        // and this is exactly the request the live cell sent to the deployed endpoint.
13210        const ORNITH_TMPL: &str = include_str!(
13211            "../../../research/reasoning-schema-20260823/ornith15.chat_template.jinja"
13212        );
13213        let on_deployed_binary = render_with(
13214            ORNITH_TMPL,
13215            &tool_caps(),
13216            json!({"messages": [{"role": "system", "content": LOW_SENTENCE},
13217                                {"role": "user", "content": "hi"}]}),
13218            None,
13219        )
13220        .unwrap();
13221        assert_eq!(
13222            on_deployed_binary, expected,
13223            "the live cell's system-message stand-in must render the SAME bytes as the post-fix \
13224             level, or its reasoning-volume numbers do not describe the shipped prompt"
13225        );
13226        // And the baseline the cell measured against: a ladder-less template injects no instruction
13227        // at all, which is why `medium` — the vendor's zero-steering rung — is the pre-lane bytes.
13228        let ladderless_unset = render_with(ORNITH_TMPL, &tool_caps(), json!({}), None).unwrap();
13229        assert!(
13230            !ladderless_unset.contains("Reasoning effort is set to"),
13231            "pre-lane q38 injected no effort instruction at any level: {ladderless_unset:?}"
13232        );
13233        assert_eq!(
13234            ladderless_unset,
13235            render_with(
13236                Q38_TMPL,
13237                &ladder_caps(),
13238                json!({"reasoning_effort": "medium"}),
13239                None
13240            )
13241            .unwrap(),
13242            "medium is the vendor's zero-steering rung and therefore the pre-lane byte baseline"
13243        );
13244    }
13245
13246    #[test]
13247    fn include_reasoning_false_stops_reasoning_it_does_not_hide_it() {
13248        // OWNER RULING 2026-08-23: *"we have to actually reason or not reason"*. Reasoning is
13249        // compute and output, billed as output, so a flag that only withheld the text charged
13250        // the customer for output we never sent. `include_reasoning:false` and
13251        // `reasoning.exclude:true` are now spellings of reasoning-OFF, and the proof is that the
13252        // PROMPT closes the think pair — a test that only checked a response-shaping flag would
13253        // have passed against the old, banned behaviour.
13254        let off = render_with(
13255            Q38_TMPL,
13256            &ladder_caps(),
13257            json!({"reasoning_effort": "none"}),
13258            None,
13259        )
13260        .unwrap();
13261        for extra in [
13262            json!({"include_reasoning": false}),
13263            json!({"reasoning": {"exclude": true}}),
13264        ] {
13265            let got = render_with(Q38_TMPL, &ladder_caps(), extra.clone(), None).unwrap();
13266            assert!(
13267                got.ends_with("<think>\n\n</think>\n\n"),
13268                "{extra} must render the CLOSED think pair, not a hidden reasoning block: {got:?}"
13269            );
13270            assert_eq!(got, off, "{extra} must be byte-identical to reasoning-off");
13271        }
13272        // A suppression request that CONTRADICTS an on-switch refuses, and the message names the
13273        // field the caller actually sent — the two folds are ordered so that
13274        // `enable_thinking:true` + `include_reasoning:false` is reported against
13275        // include_reasoning, not against a `reasoning.enabled` that was never in the body.
13276        for extra in [
13277            json!({"enable_thinking": true, "include_reasoning": false}),
13278            json!({"reasoning": {"enabled": true}, "include_reasoning": false}),
13279            json!({"reasoning": {"enabled": true, "exclude": true}}),
13280        ] {
13281            let e = render_with(Q38_TMPL, &ladder_caps(), extra.clone(), None)
13282                .err()
13283                .unwrap_or_else(|| panic!("{extra} must be refused as contradictory"));
13284            assert!(e.contains("contradictory"), "{extra}: {e}");
13285            assert!(
13286                e.contains("include_reasoning") || e.contains("exclude"),
13287                "{extra}: the refusal must name the suppression field the caller sent: {e}"
13288            );
13289        }
13290        // The "deliver it" direction is the only behaviour, so it expresses no switch at all and
13291        // leaves the model's own default alone.
13292        let dflt = render_with(Q38_TMPL, &ladder_caps(), json!({}), None).unwrap();
13293        for extra in [
13294            json!({"include_reasoning": true}),
13295            json!({"reasoning": {"exclude": false}}),
13296        ] {
13297            assert_eq!(
13298                render_with(Q38_TMPL, &ladder_caps(), extra.clone(), None).unwrap(),
13299                dflt,
13300                "{extra} must not perturb the model's default"
13301            );
13302        }
13303        // And on a model that CANNOT turn reasoning off, hiding is not a fallback — it is the
13304        // same named refusal as any other off-request, instead of a 200 that billed for a
13305        // reasoning block the caller never saw.
13306        let switchless = ModelCaps {
13307            think_switch: false,
13308            ..tool_caps()
13309        };
13310        let err = render_with(
13311            Q38_TMPL,
13312            &switchless,
13313            json!({"include_reasoning": false}),
13314            None,
13315        )
13316        .expect_err("include_reasoning:false must not silently bill for hidden reasoning");
13317        assert!(err.contains("cannot disable reasoning"), "{err}");
13318    }
13319
13320    #[test]
13321    fn the_reasoning_object_refuses_every_key_it_cannot_honour() {
13322        let build = |extra: serde_json::Value| {
13323            let (tx, _rx) = worker::event_channel();
13324            build_chat_request(
13325                weather_request(extra),
13326                Some(&ladder_caps()),
13327                tx,
13328                lanes::Lane::Interactive,
13329                None,
13330            )
13331        };
13332        let err = |extra: serde_json::Value, why: &str| -> String {
13333            build(extra).err().unwrap_or_else(|| panic!("{why}"))
13334        };
13335        // `reasoning.max_tokens` is a REAL OpenRouter field that was accepted and never read.
13336        // It is unhonourable by owner ruling, not merely unimplemented: reasoning tokens are
13337        // output tokens under the single `max_tokens` budget, so there is no second budget.
13338        let e = err(
13339            json!({"reasoning": {"max_tokens": 1024}}),
13340            "reasoning.max_tokens must not be accepted-and-ignored",
13341        );
13342        assert!(e.contains("reasoning.max_tokens"), "{e}");
13343        assert!(e.contains("ONE output budget"), "{e}");
13344        // ...and NULLING an unhonourable key must not smuggle it past its own refusal. A first cut
13345        // of the null-as-unset convention applied the skip before the key match, so these two
13346        // returned 200 and changed nothing — the exact class this function closes, reintroduced by
13347        // the fix for a different divergence.
13348        for extra in [
13349            json!({"reasoning": {"max_tokens": null}}),
13350            json!({"reasoning": {"banana": null}}),
13351        ] {
13352            let e = err(
13353                extra.clone(),
13354                "a null-valued unhonourable key must still refuse",
13355            );
13356            assert!(
13357                e.contains("max_tokens") || e.contains("banana"),
13358                "{extra}: {e}"
13359            );
13360        }
13361        // Any other unknown key: named, like the chat_template_kwargs law one level up.
13362        let e = err(
13363            json!({"reasoning": {"budget": 5}}),
13364            "an unknown reasoning key must not be accepted",
13365        );
13366        assert!(
13367            e.contains("reasoning.budget") && e.contains("enabled"),
13368            "{e}"
13369        );
13370        // WRONG TYPES are refusals too — and this removes a cross-surface divergence: these
13371        // used to fall through `as_bool()`/`as_str()` to None and be silently ignored on chat,
13372        // while /v1/messages already 400'd on the same mistake.
13373        for (extra, want) in [
13374            (json!({"reasoning": {"enabled": "false"}}), "true or false"),
13375            (json!({"reasoning": {"exclude": 1}}), "true or false"),
13376            (json!({"reasoning": {"effort": 3}}), "must be a string"),
13377        ] {
13378            let e = err(
13379                extra.clone(),
13380                "a wrong-typed reasoning key must not be ignored",
13381            );
13382            assert!(e.contains(want), "{extra}: {e}");
13383        }
13384        // The three keys we DO implement still work, and an explicit null is "unset" — for a KEY
13385        // as well as for the whole object. That last part closes the final cross-surface
13386        // divergence: `{"effort": null}` used to 400 here while /v1/responses and /v1/messages
13387        // both read it as unset, so the same body got two answers.
13388        for extra in [
13389            json!({"reasoning": {"enabled": true}}),
13390            json!({"reasoning": {"effort": "low"}}),
13391            json!({"reasoning": {"exclude": false}}),
13392            json!({"reasoning": null}),
13393            json!({"reasoning": {"effort": null}}),
13394            json!({"reasoning": {"enabled": null, "exclude": null}}),
13395        ] {
13396            build(extra.clone()).unwrap_or_else(|e| panic!("{extra} must be served: {e}"));
13397        }
13398    }
13399
13400    #[test]
13401    fn a_graded_level_on_a_binary_model_translates_to_reasoning_on() {
13402        // THE TRANSLATION RULING (coordinator, 2026-08-23). On ornith's shape — the same binary
13403        // `enable_thinking` guard as qwen, no depth input, thinking ON by default — a graded
13404        // level folds onto the binary axis as reasoning ON. A first cut REFUSED it (the
13405        // construction proof below shows the level cannot move this template's bytes), but the
13406        // refusal broke stock codex and Claude Code sessions, both of which send `xhigh` on
13407        // every request; the owner authorised translation into the one schema, and a caller who
13408        // asked for reasoning and gets reasoning has their promise kept.
13409        const ORNITH_TMPL: &str = include_str!(
13410            "../../../research/reasoning-schema-20260823/ornith15.chat_template.jinja"
13411        );
13412        // The construction fact the translation documents (and the old refusal rested on): a
13413        // level cannot move this template's bytes, so translated requests render byte-identical
13414        // to an explicit boolean ON.
13415        let explicit_on = render_with(
13416            ORNITH_TMPL,
13417            &tool_caps(),
13418            json!({"reasoning": {"enabled": true}}),
13419            None,
13420        )
13421        .unwrap();
13422        assert!(explicit_on.ends_with("<think>\n"), "{explicit_on:?}");
13423        for extra in [
13424            json!({"reasoning_effort": "low"}),
13425            json!({"reasoning_effort": "medium"}),
13426            json!({"reasoning_effort": "high"}),
13427            // the stock-CLI spellings the first cut's refusal would have broken:
13428            json!({"reasoning_effort": "xhigh"}),
13429            json!({"reasoning": {"effort": "xhigh"}}),
13430        ] {
13431            let got = render_with(ORNITH_TMPL, &tool_caps(), extra.clone(), None)
13432                .unwrap_or_else(|e| panic!("{extra} must TRANSLATE to reasoning-on, got 400: {e}"));
13433            assert_eq!(
13434                got, explicit_on,
13435                "{extra} must render byte-identical to reasoning:{{enabled:true}} — the \
13436                 documented translation, not a decorative accept"
13437            );
13438        }
13439        // The binary controls this model's lab defines keep working: off, on, unset.
13440        for extra in [
13441            json!({}),
13442            json!({"reasoning_effort": "none"}),
13443            json!({"reasoning_effort": "minimal"}),
13444            json!({"enable_thinking": false}),
13445        ] {
13446            render_with(ORNITH_TMPL, &tool_caps(), extra.clone(), None)
13447                .unwrap_or_else(|e| panic!("{extra} must still be served: {e}"));
13448        }
13449        // ...and `minimal` stays OFF — our schema's deliberate divergence from Qwen's
13450        // minimal->low, decided 2026-08-23: the no-reasoning side of our schema is real.
13451        let minimal = render_with(
13452            ORNITH_TMPL,
13453            &tool_caps(),
13454            json!({"reasoning_effort": "minimal"}),
13455            None,
13456        )
13457        .unwrap();
13458        assert!(
13459            minimal.ends_with("<think>\n\n</think>\n\n"),
13460            "minimal must close the think pair (OFF), not clamp to a reasoning level: {minimal:?}"
13461        );
13462        // A model WITH the ladder still gets its real rungs — the translation is keyed on the
13463        // template's capability, never on the field being present.
13464        let ladder_low = render_with(
13465            Q38_TMPL,
13466            &ladder_caps(),
13467            json!({"reasoning_effort": "low"}),
13468            None,
13469        )
13470        .unwrap();
13471        assert!(
13472            ladder_low.contains("Reasoning effort is set to low."),
13473            "{ladder_low:?}"
13474        );
13475        assert_ne!(
13476            ladder_low,
13477            render_with(
13478                Q38_TMPL,
13479                &ladder_caps(),
13480                json!({"reasoning_effort": "high"}),
13481                None
13482            )
13483            .unwrap(),
13484            "the ladder model's rungs stay distinct prompts"
13485        );
13486    }
13487
13488    #[test]
13489    fn one_semantic_reasoning_request_renders_identical_bytes_on_all_three_surfaces() {
13490        // THE STANDARD-SURFACE LAW, at the byte level. `/v1/responses` and `/v1/messages` are
13491        // translation surfaces over the chat core, so "the same request" means: each surface's
13492        // OWN vocabulary for a semantic intent must land on the same internal schema and
13493        // therefore the same prompt. A parameter honoured on one format and ignored on another is
13494        // the same defect wearing a different hat — and issue #31 was exactly that.
13495        //
13496        // This is the byte half. The schema half (surface -> `(ThinkMode, effort_level)` as the
13497        // WORKER sees it, through the real handlers) is
13498        // `same_effort_value_resolves_identically_on_every_surface`. Together they close the
13499        // chain surface -> schema -> bytes.
13500        let render_chat = |body: serde_json::Value| -> Result<String, String> {
13501            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
13502            let (tx, _rx) = worker::event_channel();
13503            let plan = build_chat_request(
13504                req,
13505                Some(&ladder_caps()),
13506                tx,
13507                lanes::Lane::Interactive,
13508                None,
13509            )?;
13510            Ok(chat::apply_chat_template_tools_ex(
13511                Some(Q38_TMPL),
13512                &plan.request.chat_turns,
13513                true,
13514                &plan.request.tools_json,
13515                &plan.request.tools_struct,
13516                plan.request.think,
13517                plan.request.reasoning_effort.as_deref(),
13518                None,
13519            )
13520            .unwrap())
13521        };
13522        // Each row: one semantic intent, spelled the way each surface's own clients spell it.
13523        //   chat            = OpenAI / OpenRouter / vLLM
13524        //   /v1/responses   = OpenAI Responses (what codex speaks)
13525        //   /v1/messages    = Anthropic Messages (what Claude Code speaks)
13526        for (intent, chat_body, responses_body, messages_body) in [
13527            (
13528                "reasoning OFF",
13529                json!({"model": "m", "messages": [{"role": "user", "content": "hi"}],
13530                       "reasoning_effort": "none"}),
13531                json!({"model": "m", "input": "hi", "reasoning": {"effort": "none"}}),
13532                json!({"model": "m", "max_tokens": 16,
13533                       "messages": [{"role": "user", "content": "hi"}],
13534                       "thinking": {"type": "disabled"}}),
13535            ),
13536            (
13537                "reasoning ON at the top rung",
13538                json!({"model": "m", "messages": [{"role": "user", "content": "hi"}],
13539                       "reasoning_effort": "xhigh"}),
13540                json!({"model": "m", "input": "hi", "reasoning": {"effort": "xhigh"}}),
13541                json!({"model": "m", "max_tokens": 16,
13542                       "messages": [{"role": "user", "content": "hi"}],
13543                       "output_config": {"effort": "xhigh"}}),
13544            ),
13545            (
13546                "reasoning ON at the bottom rung",
13547                json!({"model": "m", "messages": [{"role": "user", "content": "hi"}],
13548                       "reasoning_effort": "low"}),
13549                json!({"model": "m", "input": "hi", "reasoning": {"effort": "low"}}),
13550                json!({"model": "m", "max_tokens": 16,
13551                       "messages": [{"role": "user", "content": "hi"}],
13552                       "output_config": {"effort": "low"}}),
13553            ),
13554            (
13555                "the model's own default",
13556                json!({"model": "m", "messages": [{"role": "user", "content": "hi"}]}),
13557                json!({"model": "m", "input": "hi"}),
13558                json!({"model": "m", "max_tokens": 16,
13559                       "messages": [{"role": "user", "content": "hi"}]}),
13560            ),
13561        ] {
13562            let chat = render_chat(chat_body).unwrap_or_else(|e| panic!("{intent} on chat: {e}"));
13563            let via_responses = responses_api::translate(&responses_body)
13564                .unwrap_or_else(|e| panic!("{intent} on /v1/responses: {e:?}"));
13565            let via_messages = anthropic::translate(&messages_body)
13566                .unwrap_or_else(|e| panic!("{intent} on /v1/messages: {e}"));
13567            for (surface, translated) in [
13568                ("/v1/responses", via_responses),
13569                ("/v1/messages", via_messages),
13570            ] {
13571                let got = render_chat(translated)
13572                    .unwrap_or_else(|e| panic!("{intent} via {surface}: {e}"));
13573                assert_eq!(
13574                    got, chat,
13575                    "{intent}: {surface} rendered DIFFERENT prompt bytes than \
13576                     /v1/chat/completions — the parameter is honoured on one format and not \
13577                     the other"
13578                );
13579            }
13580        }
13581        // And the refusals agree too: an intent no model can honour must not be a 400 on one
13582        // surface and a 200 on another.
13583        let switchless = ModelCaps {
13584            think_switch: false,
13585            ..ladder_caps()
13586        };
13587        let render_switchless = |body: serde_json::Value| -> Result<String, String> {
13588            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
13589            let (tx, _rx) = worker::event_channel();
13590            let plan =
13591                build_chat_request(req, Some(&switchless), tx, lanes::Lane::Interactive, None)?;
13592            Ok(format!("{:?}", plan.request.think))
13593        };
13594        for (surface, body) in [
13595            (
13596                "/v1/responses",
13597                responses_api::translate(&json!({
13598                    "model": "m", "input": "hi", "reasoning": {"effort": "none"}}))
13599                .unwrap(),
13600            ),
13601            (
13602                "/v1/messages",
13603                anthropic::translate(&json!({
13604                    "model": "m", "max_tokens": 16,
13605                    "messages": [{"role": "user", "content": "hi"}],
13606                    "thinking": {"type": "disabled"}}))
13607                .unwrap(),
13608            ),
13609        ] {
13610            let err = render_switchless(body)
13611                .err()
13612                .unwrap_or_else(|| panic!("{surface} must refuse an unhonourable off-request"));
13613            assert!(err.contains("cannot disable reasoning"), "{surface}: {err}");
13614        }
13615    }
13616
13617    #[test]
13618    fn preserve_thinking_true_is_the_implemented_default_and_false_refuses() {
13619        // Qwen3.8's THIRD official thinking kwarg (its own quickstart sends
13620        // `{"enable_thinking": True, "preserve_thinking": True}`). The ladder renderer now
13621        // implements the vendor DEFAULT (replay every prior assistant turn's <think> block;
13622        // lane/dflash2-session-reuse), so `true` names exactly what the server renders and
13623        // must be ACCEPTED — Qwen's own quickstart payload has to serve. `false` (the strip
13624        // arm, with its last_query_index walk) stays unimplemented and refuses: serving
13625        // replay bytes under a strip request would misdescribe the prompt.
13626        let build = |extra: serde_json::Value| {
13627            let (tx, _rx) = worker::event_channel();
13628            build_chat_request(
13629                weather_request(extra),
13630                Some(&ladder_caps()),
13631                tx,
13632                lanes::Lane::Interactive,
13633                None,
13634            )
13635        };
13636        build(json!({"chat_template_kwargs": {"preserve_thinking": true}}))
13637            .expect("preserve_thinking:true is the vendor default the renderer implements");
13638        let e = build(json!({"chat_template_kwargs": {"preserve_thinking": false}}))
13639            .err()
13640            .expect("preserve_thinking:false (the strip arm) must refuse");
13641        assert!(e.contains("preserve_thinking"), "{e}");
13642        assert!(e.contains("strip"), "{e}");
13643        // Omitting it still serves — refusing the absent case would refuse every multi-turn
13644        // request — and the switch in the same bag keeps working.
13645        assert_eq!(
13646            build(json!({"chat_template_kwargs": {"enable_thinking": false}}))
13647                .unwrap()
13648                .request
13649                .think,
13650            ThinkMode::NoThink
13651        );
13652        // a non-bool is still a type error, not a silent drop.
13653        let e = build(json!({"chat_template_kwargs": {"preserve_thinking": "false"}}))
13654            .err()
13655            .expect("a stringly-typed preserve_thinking must not be accepted");
13656        assert!(e.contains("true or false"), "{e}");
13657    }
13658
13659    #[test]
13660    fn dsv4_is_exempt_from_the_switchless_off_refusal() {
13661        // The dsv4 renderer honours reasoning-off through its own `chat` thinking mode, so it
13662        // needs no `enable_thinking` marker to turn reasoning off. PR #33's marker pair
13663        // (`qwen_think && !think_switch`) would have refused it — latent only because
13664        // encoding-keyed artifacts carry no template string. Keyed explicitly so it cannot
13665        // become live by accident.
13666        let dsv4_caps = ModelCaps {
13667            qwen_think: true,
13668            think_switch: false,
13669            dsv4: true,
13670            ..tool_caps()
13671        };
13672        for extra in [
13673            json!({"reasoning_effort": "none"}),
13674            json!({"reasoning": {"enabled": false}}),
13675            json!({"enable_thinking": false}),
13676            json!({"include_reasoning": false}),
13677        ] {
13678            let (tx, _rx) = worker::event_channel();
13679            let plan = build_chat_request(
13680                weather_request(extra.clone()),
13681                Some(&dsv4_caps),
13682                tx,
13683                lanes::Lane::Interactive,
13684                None,
13685            )
13686            .unwrap_or_else(|e| panic!("{extra} must be served on dsv4: {e}"));
13687            assert_eq!(plan.request.think, ThinkMode::NoThink, "extra={extra}");
13688        }
13689    }
13690
13691    #[test]
13692    fn contradictory_think_switches_refuse_instead_of_picking_one() {
13693        // Two explicit switches that disagree: silently honoring one makes the other an
13694        // accepted-and-ignored parameter, which is the whole class this lane removes.
13695        let build = |extra: serde_json::Value| {
13696            let (tx, _rx) = worker::event_channel();
13697            build_chat_request(
13698                weather_request(extra),
13699                Some(&tool_caps()),
13700                tx,
13701                lanes::Lane::Interactive,
13702                None,
13703            )
13704        };
13705        for extra in [
13706            json!({"enable_thinking": true, "reasoning": {"enabled": false}}),
13707            json!({"enable_thinking": false, "reasoning": {"enabled": true}}),
13708            json!({"enable_thinking": false, "chat_template_kwargs": {"enable_thinking": true}}),
13709        ] {
13710            match build(extra.clone()) {
13711                Err(err) => assert!(
13712                    err.contains("contradictory"),
13713                    "the refusal must say the switches contradict: {err}"
13714                ),
13715                Ok(plan) => panic!(
13716                    "{extra} must be rejected as contradictory; it silently resolved to {:?}",
13717                    plan.request.think
13718                ),
13719            }
13720        }
13721        // agreeing switches, and a switch next to an EFFORT LEVEL, are not contradictions.
13722        for extra in [
13723            json!({"enable_thinking": false, "reasoning": {"enabled": false}}),
13724            json!({"enable_thinking": true, "reasoning": {"enabled": true}}),
13725            json!({"enable_thinking": false, "reasoning": {"effort": "high"}}),
13726        ] {
13727            build(extra.clone())
13728                .unwrap_or_else(|e| panic!("{extra} is not a contradiction, but got 400: {e}"));
13729        }
13730    }
13731
13732    #[test]
13733    fn explicit_reasoning_off_on_a_switchless_template_refuses_loudly() {
13734        // The latent twin of the vLLM defect: on a template whose think tail is
13735        // UNCONDITIONAL (`qwen_think` with no `enable_thinking`), NoThink has always been a
13736        // documented no-op — which at the API boundary means 200 + a full reasoning block
13737        // for a caller who asked for none. Now a named 400.
13738        let switchless = ModelCaps {
13739            tools_branch: true,
13740            qwen_think: true,
13741            think_switch: false,
13742            chat_ok: true,
13743            ..Default::default()
13744        };
13745        let build = |extra: serde_json::Value, caps: &ModelCaps, default_effort: Option<&str>| {
13746            let (tx, _rx) = worker::event_channel();
13747            build_chat_request_with_trace(
13748                weather_request(extra),
13749                Some(caps),
13750                tx,
13751                lanes::Lane::Interactive,
13752                None,
13753                None,
13754                default_effort,
13755                &ModelSamplingDefaults::default(),
13756            )
13757        };
13758        for extra in [
13759            json!({"reasoning_effort": "none"}),
13760            json!({"reasoning_effort": "minimal"}),
13761            json!({"reasoning": {"enabled": false}}),
13762            json!({"enable_thinking": false}),
13763            json!({"chat_template_kwargs": {"enable_thinking": false}}),
13764        ] {
13765            let err = build(extra.clone(), &switchless, None)
13766                .err()
13767                .unwrap_or_else(|| {
13768                    panic!(
13769                        "{extra} on a switchless think template must not be accepted-and-ignored"
13770                    )
13771                });
13772            assert!(
13773                err.contains("cannot disable reasoning"),
13774                "the refusal must say the model cannot disable reasoning: {err}"
13775            );
13776        }
13777        // Everything else on the same model is untouched: thinking-ON requests, unset
13778        // requests, and — critically — an OPERATOR default of "none", which must never turn
13779        // into a 400 for a caller who expressed nothing.
13780        for (extra, default_effort) in [
13781            (json!({}), None),
13782            // a client-named LEVEL translates onto the binary axis as reasoning ON (coordinator
13783            // ruling 2026-08-23) — this template reasons by default, so the promise is kept.
13784            (json!({"reasoning_effort": "high"}), None),
13785            (json!({"reasoning": {"enabled": true}}), None),
13786            (json!({"enable_thinking": true}), None),
13787            (json!({}), Some("none")),
13788            (json!({}), Some("minimal")),
13789            (json!({}), Some("high")),
13790        ] {
13791            build(extra.clone(), &switchless, default_effort).unwrap_or_else(|e| {
13792                panic!("{extra} (default={default_effort:?}) must still be served: {e}")
13793            });
13794        }
13795        // A model WITH the switch serves the same off-request normally — the refusal is
13796        // keyed on the template, never on the field being present.
13797        assert_eq!(
13798            build(json!({"enable_thinking": false}), &tool_caps(), None)
13799                .unwrap()
13800                .request
13801                .think,
13802            ThinkMode::NoThink
13803        );
13804    }
13805
13806    #[test]
13807    fn gemma4_default_think_on_renders_byte_identical_to_explicit_think_on() {
13808        // Template-render identity gate: with the knob active, an UNSET request's
13809        // rendered prompt equals the explicit think-on request's prompt byte-for-byte —
13810        // the knob substitutes into the SAME parse_think mapping before the plan is
13811        // built; it does not grow a second render path. The vendor template's own
13812        // rendering semantics are untouched: explicit-off and knobless deployments still
13813        // render the CLOSED thought channel.
13814        let gemma_caps = ModelCaps {
13815            tools_branch: true,
13816            chat_ok: true,
13817            gemma_think: true,
13818            instruct_type: Some("gemma".into()),
13819            ..Default::default()
13820        };
13821        let render =
13822            |tmpl: &str, extra: serde_json::Value, default_effort: Option<&str>| -> String {
13823                let mut payload = serde_json::json!({
13824                    "model": "google/gemma-4-31b-it",
13825                    "messages": [{"role": "user", "content": "Weather in Paris?"}],
13826                });
13827                if let Some(obj) = extra.as_object() {
13828                    for (k, v) in obj {
13829                        payload[k] = v.clone();
13830                    }
13831                }
13832                let req: ChatCompletionReq = serde_json::from_value(payload).unwrap();
13833                let (tx, _rx) = worker::event_channel();
13834                let plan = build_chat_request_with_trace(
13835                    req,
13836                    Some(&gemma_caps),
13837                    tx,
13838                    lanes::Lane::Interactive,
13839                    None,
13840                    None,
13841                    default_effort,
13842                    &ModelSamplingDefaults::default(),
13843                )
13844                .unwrap();
13845                chat::apply_chat_template_tools_ex(
13846                    Some(tmpl),
13847                    &plan.request.chat_turns,
13848                    true,
13849                    &plan.request.tools_json,
13850                    &plan.request.tools_struct,
13851                    plan.request.think,
13852                    plan.request.reasoning_effort.as_deref(),
13853                    None, // gemma template — no dsv4 encoding revision
13854                )
13855                .unwrap()
13856            };
13857        let official = gemma_template("official");
13858        let unset_with_knob = render(&official, json!({}), Some("high"));
13859        let explicit_on = render(&official, json!({"reasoning_effort": "high"}), None);
13860        assert_eq!(
13861            unset_with_knob, explicit_on,
13862            "knob render must be byte-identical to the explicit think-on render"
13863        );
13864        assert!(
13865            unset_with_knob.starts_with("<|turn>system\n<|think|>\n"),
13866            "think-on injects the <|think|> system token: {unset_with_knob:?}"
13867        );
13868        assert!(
13869            unset_with_knob.ends_with("<|turn>model\n"),
13870            "think-on generation turn is OPEN: {unset_with_knob:?}"
13871        );
13872        // explicit off under the knob = byte-identical to explicit off without it. On the
13873        // OFFICIAL tooluse trunk the vendor law for thinking-off is a bare open model
13874        // turn with NO <|think|> system token (closed_tail is the QAT-trunk variant).
13875        let explicit_off_with_knob =
13876            render(&official, json!({"reasoning_effort": "none"}), Some("high"));
13877        let explicit_off = render(&official, json!({"reasoning_effort": "none"}), None);
13878        assert_eq!(explicit_off_with_knob, explicit_off);
13879        assert!(
13880            !explicit_off_with_knob.contains("<|think|>")
13881                && explicit_off_with_knob.ends_with("<|turn>model\n"),
13882            "explicit off keeps the official template's thinking-off bytes: \
13883             {explicit_off_with_knob:?}"
13884        );
13885        // knobless unset = the template's own default (today's serving bytes).
13886        let unset_no_knob = render(&official, json!({}), None);
13887        assert_eq!(
13888            unset_no_knob, explicit_off,
13889            "knobless unset stays the template's own thinking-off default"
13890        );
13891        assert_ne!(unset_no_knob, unset_with_knob);
13892        // QAT-trunk variant: its thinking-off generation prompt appends the CLOSED
13893        // thought channel — the knob must not perturb that vendor law either.
13894        let qat = gemma_template("qat");
13895        assert!(
13896            render(&qat, json!({}), None).ends_with("<|turn>model\n<|channel>thought\n<channel|>"),
13897            "QAT knobless unset keeps the closed-channel default"
13898        );
13899        assert_eq!(
13900            render(&qat, json!({}), Some("high")),
13901            render(&qat, json!({"reasoning_effort": "high"}), None),
13902            "QAT knob render must equal the explicit think-on render"
13903        );
13904    }
13905
13906    #[test]
13907    fn default_reasoning_effort_is_validated_at_metadata_load() {
13908        // A typo'd knob fails at BOOT (metadata parse), never per-request.
13909        let parsed = OpenRouterMetadataFile::from_toml(
13910            r#"
13911[models.g]
13912default_reasoning_effort = "high"
13913"#,
13914        )
13915        .unwrap();
13916        assert_eq!(
13917            parsed.get("g").unwrap().default_reasoning_effort.as_deref(),
13918            Some("high")
13919        );
13920        let err = OpenRouterMetadataFile::from_toml(
13921            r#"
13922[models.g]
13923default_reasoning_effort = "always"
13924"#,
13925        )
13926        .unwrap_err();
13927        assert!(err.contains("default_reasoning_effort"), "{err}");
13928    }
13929
13930    #[test]
13931    fn reasoning_effort_maps_to_effort_level_on_step35_class_templates() {
13932        // ModelCaps::effort_levels=true (the step35 dialect): the SAME client field becomes
13933        // a render input (Request::reasoning_effort) — low/medium/high pass through, absent
13934        // stays None (the template's own default: no `Reasoning:` line).
13935        //
13936        // THE REAL CAPS INTERSECTION (lane/reasoning-schema-20260823, found by review of PR #33
13937        // before release). This used to inherit `think_switch: true` from `tool_caps()` — a
13938        // combination NO real step35 template can produce, since its `<think>` tail is
13939        // unconditional and it carries no `enable_thinking`. Probing the shipped template
13940        // (research/step37-bringup-20260802/raw/chat_template.jinja) gives
13941        // `qwen_think=true, think_switch=false, effort_levels=true`, so that is what the test
13942        // asserts against — otherwise CI is blind to what a live step35 actually does.
13943        let effort_caps = ModelCaps {
13944            effort_levels: true,
13945            think_switch: false,
13946            ..tool_caps()
13947        };
13948        for (extra, want) in [
13949            (json!({}), None),
13950            (json!({"reasoning_effort": "low"}), Some("low")),
13951            (json!({"reasoning_effort": "medium"}), Some("medium")),
13952            (json!({"reasoning_effort": "high"}), Some("high")),
13953            (json!({"reasoning": {"effort": "high"}}), Some("high")),
13954            // clamp aliases render as the highest level the template distinguishes
13955            (json!({"reasoning_effort": "xhigh"}), Some("high")),
13956            (json!({"reasoning": {"effort": "max"}}), Some("high")),
13957        ] {
13958            let (tx, _rx) = worker::event_channel();
13959            let plan = build_chat_request(
13960                weather_request(extra.clone()),
13961                Some(&effort_caps),
13962                tx,
13963                lanes::Lane::Interactive,
13964                None,
13965            )
13966            .unwrap();
13967            assert_eq!(
13968                plan.request.reasoning_effort.as_deref(),
13969                want,
13970                "extra={extra}"
13971            );
13972        }
13973        // AN OFF-REQUEST ON STEP35 IS NOW A NAMED 400, NOT A CLAMP TO THE LOWEST RUNG.
13974        // It used to resolve `none`/`minimal`/`reasoning.enabled:false` to `Reasoning: low` —
13975        // i.e. a caller who asked for NO reasoning was served reasoning at the lowest level,
13976        // behind a 200. That is the owner's named unacceptable case (2026-08-23: asking for
13977        // non-reasoning and getting reasoning must be impossible), and step35's `<think>` tail
13978        // is unconditional, so the honest answer is a refusal naming the model.
13979        for extra in [
13980            json!({"reasoning_effort": "none"}),
13981            json!({"reasoning_effort": "minimal"}),
13982            json!({"reasoning": {"enabled": false}}),
13983            json!({"enable_thinking": false}),
13984            json!({"include_reasoning": false}),
13985        ] {
13986            let (tx, _rx) = worker::event_channel();
13987            let err = build_chat_request(
13988                weather_request(extra.clone()),
13989                Some(&effort_caps),
13990                tx,
13991                lanes::Lane::Interactive,
13992                None,
13993            )
13994            .err()
13995            .unwrap_or_else(|| panic!("{extra} must not be clamped to a reasoning level"));
13996            assert!(
13997                err.contains("cannot disable reasoning"),
13998                "extra={extra}: {err}"
13999            );
14000        }
14001        // effort_levels=false AND the template reasons by default (the ornith/qwen-class shape):
14002        // a client-named level TRANSLATES onto the binary axis as reasoning ON (coordinator
14003        // ruling 2026-08-23 — a first cut refused these, which broke stock codex/Claude Code
14004        // sessions against ornith). The level string is dropped by the delivery gate, so the
14005        // prompt is byte-identical to explicit-ON by construction; the byte proof lives in
14006        // `a_graded_level_on_a_binary_model_translates_to_reasoning_on`.
14007        for extra in [
14008            json!({"reasoning_effort": "high"}),
14009            json!({"reasoning": {"effort": "low"}}),
14010        ] {
14011            let (tx, _rx) = worker::event_channel();
14012            let plan = build_chat_request(
14013                weather_request(extra.clone()),
14014                Some(&tool_caps()),
14015                tx,
14016                lanes::Lane::Interactive,
14017                None,
14018            )
14019            .unwrap_or_else(|e| panic!("{extra} must translate, not refuse: {e}"));
14020            assert_eq!(plan.request.think, ThinkMode::Think, "extra={extra}");
14021            assert_eq!(plan.request.reasoning_effort, None, "extra={extra}");
14022        }
14023        // and an unset request on that class still renders the template's own default.
14024        let (tx, _rx) = worker::event_channel();
14025        let plan = build_chat_request(
14026            weather_request(json!({})),
14027            Some(&tool_caps()),
14028            tx,
14029            lanes::Lane::Interactive,
14030            None,
14031        )
14032        .unwrap();
14033        assert_eq!(plan.request.reasoning_effort, None);
14034    }
14035
14036    #[test]
14037    fn assistant_history_tool_calls_and_tool_role_render_into_turns() {
14038        let payload = serde_json::json!({
14039            "model": "m",
14040            "messages": [
14041                {"role": "user", "content": "Weather in Paris?"},
14042                {"role": "assistant", "content": null, "tool_calls": [
14043                    {"id": "call_x", "type": "function", "function": {
14044                        "name": "get_weather",
14045                        "arguments": "{\"city\": \"Paris\", \"days\": 3}"}}]},
14046                {"role": "tool", "tool_call_id": "call_x", "content": "{\"temp_c\": 21}"}
14047            ],
14048        });
14049        let req: ChatCompletionReq = serde_json::from_value(payload).unwrap();
14050        let (tx, _rx) = worker::event_channel();
14051        let plan = build_chat_request(req, Some(&tool_caps()), tx, lanes::Lane::Interactive, None)
14052            .unwrap();
14053        let turns = &plan.request.chat_turns;
14054        assert_eq!(turns[1].tool_calls.len(), 1);
14055        assert_eq!(turns[1].tool_calls[0].name, "get_weather");
14056        assert_eq!(
14057            turns[1].tool_calls[0].params,
14058            vec![("city".into(), "Paris".into()), ("days".into(), "3".into())]
14059        );
14060        assert_eq!(turns[2].role, "tool");
14061        assert_eq!(turns[2].content, "{\"temp_c\": 21}");
14062        // no tools field on this follow-up turn: no tool-call scanning — but the think-open
14063        // prompt still arms the reasoning-only splitter (gap-scan F13).
14064        let mut p = plan
14065            .parser
14066            .expect("think-open chat arms the reasoning splitter");
14067        let pieces = p.push("thought</think>\n\nanswer <tool_call> is prose here");
14068        assert_eq!(
14069            pieces,
14070            vec![
14071                Piece::Reasoning("thought".into()),
14072                Piece::Content("answer <tool_call> is prose here".into()),
14073            ]
14074        );
14075    }
14076
14077    #[tokio::test]
14078    async fn blocking_tools_response_carries_tool_calls_and_finish_reason() {
14079        let (tx, rx) = worker::event_channel();
14080        tx.send(Event::Token {
14081            id: 1,
14082            text: "plan</think>\n\n".into(),
14083        })
14084        .unwrap();
14085        tx.send(Event::Token {
14086            id: 2,
14087            text: "<tool_call>\n<function=get_weather>\n\
14088<parameter=city>\nParis\n</parameter>\n</function>\n</tool_call>"
14089                .into(),
14090        })
14091        .unwrap();
14092        tx.send(Event::Done {
14093            stop_reason: "Eos".into(),
14094            n_tokens: 2,
14095            n_prompt: 40,
14096            n_cached: 0,
14097            elapsed_s: 0.5,
14098            spec: None,
14099        })
14100        .unwrap();
14101        drop(tx);
14102        let parser = ToolStreamParser::new(HashMap::new(), true);
14103        let response = blocking_response(
14104            rx,
14105            "m".into(),
14106            true,
14107            Vec::new(),
14108            Some(parser),
14109            Envelope::new(true),
14110        )
14111        .await;
14112        assert_eq!(response.status(), StatusCode::OK);
14113        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
14114            .await
14115            .unwrap();
14116        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
14117        assert_eq!(payload["choices"][0]["finish_reason"], "tool_calls");
14118        // reasoning separation (gap-scan F13): think text -> message.reasoning (+details),
14119        // content is post-think only (null here — a pure tool-call turn).
14120        assert_eq!(
14121            payload["choices"][0]["message"]["content"],
14122            serde_json::Value::Null
14123        );
14124        assert_eq!(payload["choices"][0]["message"]["reasoning"], "plan");
14125        assert_eq!(
14126            payload["choices"][0]["message"]["reasoning_details"][0]["text"],
14127            "plan"
14128        );
14129        let call = &payload["choices"][0]["message"]["tool_calls"][0];
14130        assert_eq!(call["type"], "function");
14131        assert_eq!(call["function"]["name"], "get_weather");
14132        assert_eq!(call["function"]["arguments"], "{\"city\":\"Paris\"}");
14133        // THE INTERSECTION (integrate-cache): a tools response's usage carries the same
14134        // worker-truth prompt/cached split as any other shape — one source of truth.
14135        assert_eq!(payload["usage"]["prompt_tokens"], 40);
14136        assert_eq!(payload["usage"]["completion_tokens"], 2);
14137        assert_eq!(payload["usage"]["total_tokens"], 42);
14138        assert_eq!(
14139            payload["usage"]["prompt_tokens_details"]["cached_tokens"],
14140            0
14141        );
14142    }
14143
14144    #[test]
14145    fn cache_salt_plumbs_to_the_worker_namespace() {
14146        // PC-ISO: explicit cache_salt -> the request's cache namespace, on BOTH bodies.
14147        let req: CompletionReq = serde_json::from_value(serde_json::json!({
14148            "model": "m", "prompt": "task", "cache_salt": "tenant-a"
14149        }))
14150        .unwrap();
14151        let (tx, _rx) = worker::event_channel();
14152        assert_eq!(
14153            build_request(&req, tx, lanes::Lane::Interactive, None).cache_ns,
14154            "tenant-a"
14155        );
14156
14157        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
14158            "model": "m", "messages": [{"role": "user", "content": "task"}],
14159            "cache_salt": "tenant-b"
14160        }))
14161        .unwrap();
14162        let (tx, _rx) = worker::event_channel();
14163        assert_eq!(
14164            build_chat_request(req, None, tx, lanes::Lane::Interactive, None)
14165                .unwrap()
14166                .request
14167                .cache_ns,
14168            "tenant-b"
14169        );
14170
14171        // no salt -> "" (the default single-tenant namespace; pre-PC-ISO behavior).
14172        let req: CompletionReq = serde_json::from_value(serde_json::json!({
14173            "model": "m", "prompt": "task"
14174        }))
14175        .unwrap();
14176        let (tx, _rx) = worker::event_channel();
14177        assert_eq!(
14178            build_request(&req, tx, lanes::Lane::Interactive, None).cache_ns,
14179            ""
14180        );
14181        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
14182            "model": "m", "messages": [{"role": "user", "content": "task"}]
14183        }))
14184        .unwrap();
14185        let (tx, _rx) = worker::event_channel();
14186        assert_eq!(
14187            build_chat_request(req, None, tx, lanes::Lane::Interactive, None)
14188                .unwrap()
14189                .request
14190                .cache_ns,
14191            ""
14192        );
14193    }
14194
14195    #[test]
14196    fn cache_salt_validation_rejects_oversized_value() {
14197        let salt = Some("a".repeat(CACHE_SALT_MAX_BYTES + 1));
14198        assert_eq!(
14199            validate_cache_namespace(&salt, false),
14200            Err("cache_salt must be at most 64 bytes")
14201        );
14202    }
14203
14204    #[test]
14205    fn cache_salt_validation_rejects_reserved_open_namespace() {
14206        let salt = Some("t:acme\u{1f}private".to_string());
14207        assert_eq!(
14208            validate_cache_namespace(&salt, false),
14209            Err("cache_salt must not use the reserved t: prefix without a keyring")
14210        );
14211    }
14212
14213    #[test]
14214    fn cache_salt_validation_accepts_normal_value() {
14215        let raw = "tenant-A_7.c2VjcmV0LXNjb3Bl+/=";
14216        let salt = Some(raw.to_string());
14217        assert_eq!(validate_cache_namespace(&salt, false).unwrap(), raw);
14218        assert_eq!(validate_cache_namespace(&None, false).unwrap(), "");
14219        let max_raw = "a".repeat(CACHE_SALT_MAX_BYTES);
14220        let max = Some(max_raw.clone());
14221        assert_eq!(validate_cache_namespace(&max, false).unwrap(), max_raw);
14222    }
14223
14224    #[test]
14225    fn cache_salt_validation_rejects_unsupported_characters() {
14226        let salt = Some("tenant salt".to_string());
14227        assert_eq!(
14228            validate_cache_namespace(&salt, false),
14229            Err("cache_salt contains unsupported characters")
14230        );
14231    }
14232
14233    #[test]
14234    fn affinity_key_honors_both_client_conventions_in_priority_order() {
14235        use axum::http::HeaderMap;
14236        let hdr = |v: &str| {
14237            let mut h = HeaderMap::new();
14238            h.insert("x-session-id", v.parse().unwrap());
14239            h
14240        };
14241        let empty = HeaderMap::new();
14242        let s = |v: &str| Some(v.to_string());
14243        // each convention alone.
14244        assert_eq!(
14245            affinity_key(&s("explicit"), &None, &empty).unwrap(),
14246            s("explicit")
14247        );
14248        assert_eq!(
14249            affinity_key(&None, &s("openai-user"), &empty).unwrap(),
14250            s("openai-user")
14251        );
14252        assert_eq!(
14253            affinity_key(&None, &None, &hdr("hdr-id")).unwrap(),
14254            s("hdr-id")
14255        );
14256        // priority: session_id > user > header. Body beats header because a header can be
14257        // rewritten by an intermediary.
14258        assert_eq!(affinity_key(&s("a"), &s("b"), &hdr("c")).unwrap(), s("a"));
14259        assert_eq!(affinity_key(&None, &s("b"), &hdr("c")).unwrap(), s("b"));
14260        // blank/whitespace is ABSENT, not a key — a client sending "user": "" must not
14261        // collapse every conversation onto one shared session.
14262        assert_eq!(affinity_key(&s("  "), &s(""), &hdr("  ")).unwrap(), None);
14263        assert_eq!(affinity_key(&s(""), &s("real"), &empty).unwrap(), s("real"));
14264        // trimmed.
14265        assert_eq!(
14266            affinity_key(&s(" padded "), &None, &empty).unwrap(),
14267            s("padded")
14268        );
14269        // nothing supplied -> implicit tier (fingerprint) in the worker.
14270        assert_eq!(affinity_key(&None, &None, &empty).unwrap(), None);
14271        assert!(
14272            affinity_key(
14273                &s(&"x".repeat(MAX_CLIENT_IDENTIFIER_BYTES + 1)),
14274                &None,
14275                &empty,
14276            )
14277            .unwrap_err()
14278            .contains("at most")
14279        );
14280        assert!(
14281            affinity_key(&s("forged\nlog"), &None, &empty)
14282                .unwrap_err()
14283                .contains("control")
14284        );
14285    }
14286
14287    #[test]
14288    fn affinity_key_plumbs_to_the_worker_request_on_both_bodies() {
14289        let req: CompletionReq = serde_json::from_value(serde_json::json!({
14290            "model": "m", "prompt": "task", "session_id": "conv-1"
14291        }))
14292        .unwrap();
14293        let (tx, _rx) = worker::event_channel();
14294        let key = affinity_key(&req.session_id, &req.user, &axum::http::HeaderMap::new()).unwrap();
14295        assert_eq!(
14296            build_request(&req, tx, lanes::Lane::Interactive, key)
14297                .affinity
14298                .as_deref(),
14299            Some("conv-1")
14300        );
14301        // OpenAI `user` on the chat body.
14302        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
14303            "model": "m", "messages": [{"role": "user", "content": "task"}],
14304            "user": "conv-2"
14305        }))
14306        .unwrap();
14307        let (tx, _rx) = worker::event_channel();
14308        let key = affinity_key(&req.session_id, &req.user, &axum::http::HeaderMap::new()).unwrap();
14309        assert_eq!(
14310            build_chat_request(req, None, tx, lanes::Lane::Interactive, key)
14311                .unwrap()
14312                .request
14313                .affinity
14314                .as_deref(),
14315            Some("conv-2")
14316        );
14317        // absent on both -> None (implicit tier).
14318        let req: CompletionReq = serde_json::from_value(serde_json::json!({
14319            "model": "m", "prompt": "task"
14320        }))
14321        .unwrap();
14322        let (tx, _rx) = worker::event_channel();
14323        assert!(
14324            build_request(&req, tx, lanes::Lane::Interactive, None)
14325                .affinity
14326                .is_none()
14327        );
14328    }
14329
14330    /// Drain an Sse response into its `data:` payload lines (keep-alive comments skipped).
14331    async fn sse_data_lines(resp: Response) -> Vec<String> {
14332        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
14333            .await
14334            .unwrap();
14335        String::from_utf8(bytes.to_vec())
14336            .unwrap()
14337            .lines()
14338            .filter_map(|l| l.strip_prefix("data: ").map(str::to_string))
14339            .collect()
14340    }
14341
14342    #[tokio::test]
14343    async fn chat_returns_reasoning_text_when_on_and_no_field_when_off() {
14344        // OWNER ACCEPTANCE GATE (2026-08-23, "also thinking content should be returned, not only
14345        // the content itself"): on the chat surface reasoning is delivered — non-streaming as
14346        // `message.reasoning` (+ `message.reasoning_details`), streaming as `delta.reasoning` —
14347        // and a reasoning-off generation carries NO reasoning field rather than an empty one.
14348        // Billing unchanged either way: reasoning tokens are output tokens.
14349        let feed = |think: bool| {
14350            let (tx, rx) = worker::event_channel();
14351            let body = if think {
14352                "a plan</think>\n\nanswer"
14353            } else {
14354                "answer"
14355            };
14356            tx.send(Event::Token {
14357                id: 1,
14358                text: body.into(),
14359            })
14360            .unwrap();
14361            tx.send(Event::Done {
14362                stop_reason: "Eos".into(),
14363                n_tokens: 3,
14364                n_prompt: 10,
14365                n_cached: 0,
14366                elapsed_s: 0.1,
14367                spec: None,
14368            })
14369            .unwrap();
14370            drop(tx);
14371            rx
14372        };
14373        // NON-STREAMING, reasoning on (the think-open prompt arms the splitter).
14374        let resp = blocking_response(
14375            feed(true),
14376            "m".into(),
14377            true,
14378            Vec::new(),
14379            Some(ToolStreamParser::reasoning_only()),
14380            Envelope::new(true),
14381        )
14382        .await;
14383        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
14384            .await
14385            .unwrap();
14386        let v: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
14387        assert_eq!(v["choices"][0]["message"]["reasoning"], "a plan");
14388        assert_eq!(
14389            v["choices"][0]["message"]["reasoning_details"][0]["text"],
14390            "a plan"
14391        );
14392        assert_eq!(v["choices"][0]["message"]["content"], "answer");
14393        // NON-STREAMING, reasoning off: the NoThink path builds no parser, and the response
14394        // carries no reasoning field at all.
14395        let resp = blocking_response(
14396            feed(false),
14397            "m".into(),
14398            true,
14399            Vec::new(),
14400            None,
14401            Envelope::new(true),
14402        )
14403        .await;
14404        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
14405            .await
14406            .unwrap();
14407        let v: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
14408        assert!(
14409            v["choices"][0]["message"].get("reasoning").is_none(),
14410            "a reasoning-off response must carry no reasoning field: {v}"
14411        );
14412        assert_eq!(v["choices"][0]["message"]["content"], "answer");
14413        // STREAMING, reasoning on: think text arrives as delta.reasoning, never as content.
14414        let resp = sse_response(
14415            feed(true),
14416            "m".into(),
14417            true,
14418            Some(ToolStreamParser::reasoning_only()),
14419            Envelope::new(true),
14420            Vec::new(),
14421            None,
14422        )
14423        .into_response();
14424        let lines = sse_data_lines(resp).await;
14425        let chunks: Vec<serde_json::Value> = lines[..lines.len() - 1]
14426            .iter()
14427            .map(|l| serde_json::from_str(l).unwrap())
14428            .collect();
14429        let reasoning: String = chunks
14430            .iter()
14431            .filter_map(|c| c["choices"][0]["delta"]["reasoning"].as_str())
14432            .collect();
14433        assert_eq!(
14434            reasoning, "a plan",
14435            "think text must stream as delta.reasoning"
14436        );
14437        let content: String = chunks
14438            .iter()
14439            .filter_map(|c| c["choices"][0]["delta"]["content"].as_str())
14440            .collect();
14441        assert_eq!(content, "answer", "content must exclude the think segment");
14442        // STREAMING, reasoning off: no delta carries a reasoning key.
14443        let resp = sse_response(
14444            feed(false),
14445            "m".into(),
14446            true,
14447            None,
14448            Envelope::new(true),
14449            Vec::new(),
14450            None,
14451        )
14452        .into_response();
14453        let lines = sse_data_lines(resp).await;
14454        for l in &lines[..lines.len() - 1] {
14455            let c: serde_json::Value = serde_json::from_str(l).unwrap();
14456            assert!(
14457                c["choices"][0]["delta"].get("reasoning").is_none(),
14458                "a reasoning-off stream must carry no reasoning deltas: {c}"
14459            );
14460        }
14461    }
14462
14463    #[tokio::test]
14464    async fn stream_chunks_carry_envelope_and_first_delta_role() {
14465        let (tx, rx) = worker::event_channel();
14466        tx.send(Event::Token {
14467            id: 1,
14468            text: "he".into(),
14469        })
14470        .unwrap();
14471        tx.send(Event::Token {
14472            id: 2,
14473            text: "llo".into(),
14474        })
14475        .unwrap();
14476        tx.send(Event::Done {
14477            stop_reason: "Eos".into(),
14478            n_tokens: 2,
14479            n_prompt: 10,
14480            n_cached: 0,
14481            elapsed_s: 0.1,
14482            spec: None,
14483        })
14484        .unwrap();
14485        drop(tx);
14486        let resp = sse_response(
14487            rx,
14488            "m".into(),
14489            true,
14490            None,
14491            Envelope::new(true),
14492            Vec::new(),
14493            None,
14494        )
14495        .into_response();
14496        let lines = sse_data_lines(resp).await;
14497        assert_eq!(lines.last().map(String::as_str), Some("[DONE]"));
14498        let chunks: Vec<serde_json::Value> = lines[..lines.len() - 1]
14499            .iter()
14500            .map(|l| serde_json::from_str(l).unwrap())
14501            .collect();
14502        // every chunk: id (chatcmpl-, SAME id) + created + system_fingerprint + object.
14503        let id = chunks[0]["id"].as_str().unwrap().to_string();
14504        assert!(id.starts_with("chatcmpl-"));
14505        for c in &chunks {
14506            assert_eq!(c["id"], id.as_str());
14507            assert!(c["created"].as_u64().unwrap() > 1_700_000_000);
14508            let fingerprint = c["system_fingerprint"].as_str().unwrap();
14509            assert!(
14510                build_id::fingerprint_is_well_formed(fingerprint),
14511                "chunk system_fingerprint {fingerprint:?} is not memra-<version>-<12 hex>"
14512            );
14513            assert_eq!(c["object"], "chat.completion.chunk");
14514        }
14515        // FIRST delta carries role:"assistant" (SDK accumulator contract); later ones don't.
14516        assert_eq!(chunks[0]["choices"][0]["delta"]["role"], "assistant");
14517        assert_eq!(chunks[0]["choices"][0]["delta"]["content"], "he");
14518        assert!(chunks[1]["choices"][0]["delta"].get("role").is_none());
14519        // final chunk: finish_reason + usage.
14520        let fin = chunks.last().unwrap();
14521        assert_eq!(fin["choices"][0]["finish_reason"], "stop");
14522        assert_eq!(fin["usage"]["prompt_tokens"], 10);
14523    }
14524
14525    #[tokio::test]
14526    async fn stream_token_events_equal_usage_on_every_finish_path() {
14527        for (stop_reason, expected_finish) in [
14528            ("Eos", "stop"),
14529            ("Callback", "stop"),
14530            ("MaxNew", "length"),
14531            ("ContextFull", "length"),
14532        ] {
14533            let (tx, rx) = worker::event_channel();
14534            // EOS deliberately has empty text: it is still one generated, streamed, and
14535            // accounted token id. This is the exact Q35 sellgate terminal-token case.
14536            tx.send(Event::Token {
14537                id: 248_046,
14538                text: String::new(),
14539            })
14540            .unwrap();
14541            tx.send(Event::Done {
14542                stop_reason: stop_reason.into(),
14543                n_tokens: 1,
14544                n_prompt: 8,
14545                n_cached: 8,
14546                elapsed_s: 0.1,
14547                spec: None,
14548            })
14549            .unwrap();
14550            drop(tx);
14551
14552            let resp = sse_response(
14553                rx,
14554                "m".into(),
14555                true,
14556                None,
14557                Envelope::new(true),
14558                Vec::new(),
14559                None,
14560            )
14561            .into_response();
14562            let lines = sse_data_lines(resp).await;
14563            assert_eq!(lines.last().map(String::as_str), Some("[DONE]"));
14564            let chunks: Vec<serde_json::Value> = lines[..lines.len() - 1]
14565                .iter()
14566                .map(|line| serde_json::from_str(line).unwrap())
14567                .collect();
14568            let token_events = chunks
14569                .iter()
14570                .filter(|chunk| chunk["choices"][0]["finish_reason"].is_null())
14571                .count();
14572            let terminal = chunks.last().unwrap();
14573            assert_eq!(token_events, 1, "{stop_reason} SSE token count");
14574            assert_eq!(terminal["usage"]["completion_tokens"], token_events);
14575            assert_eq!(terminal["choices"][0]["finish_reason"], expected_finish);
14576        }
14577    }
14578
14579    #[tokio::test]
14580    async fn stream_excludes_stop_text_like_non_stream_does() {
14581        // gap-scan F9: the worker emits the delta BEFORE its stop check — the stream
14582        // shape must still exclude the stop text (and same-token overshoot) exactly
14583        // like the non-stream truncate. Stop spans two token events here.
14584        let (tx, rx) = worker::event_channel();
14585        tx.send(Event::Token {
14586            id: 1,
14587            text: "answer\nPro".into(),
14588        })
14589        .unwrap();
14590        tx.send(Event::Token {
14591            id: 2,
14592            text: "blem: leaked prompt".into(),
14593        })
14594        .unwrap();
14595        tx.send(Event::Done {
14596            stop_reason: "Callback".into(),
14597            n_tokens: 2,
14598            n_prompt: 8,
14599            n_cached: 0,
14600            elapsed_s: 0.1,
14601            spec: None,
14602        })
14603        .unwrap();
14604        drop(tx);
14605        let resp = sse_response(
14606            rx,
14607            "m".into(),
14608            true,
14609            None,
14610            Envelope::new(true),
14611            vec!["Problem:".into()],
14612            None,
14613        )
14614        .into_response();
14615        let lines = sse_data_lines(resp).await;
14616        let content: String = lines
14617            .iter()
14618            .filter(|l| *l != "[DONE]")
14619            .filter_map(|l| serde_json::from_str::<serde_json::Value>(l).ok())
14620            .filter_map(|c| {
14621                c["choices"][0]["delta"]["content"]
14622                    .as_str()
14623                    .map(str::to_string)
14624            })
14625            .collect();
14626        assert_eq!(content, "answer\n");
14627
14628        // held-back text that never becomes a stop is flushed at Done.
14629        let (tx, rx) = worker::event_channel();
14630        tx.send(Event::Token {
14631            id: 1,
14632            text: "ends in Pro".into(),
14633        })
14634        .unwrap();
14635        tx.send(Event::Done {
14636            stop_reason: "Eos".into(),
14637            n_tokens: 1,
14638            n_prompt: 8,
14639            n_cached: 0,
14640            elapsed_s: 0.1,
14641            spec: None,
14642        })
14643        .unwrap();
14644        drop(tx);
14645        let resp = sse_response(
14646            rx,
14647            "m".into(),
14648            true,
14649            None,
14650            Envelope::new(true),
14651            vec!["Problem:".into()],
14652            None,
14653        )
14654        .into_response();
14655        let lines = sse_data_lines(resp).await;
14656        let content: String = lines
14657            .iter()
14658            .filter(|l| *l != "[DONE]")
14659            .filter_map(|l| serde_json::from_str::<serde_json::Value>(l).ok())
14660            .filter_map(|c| {
14661                c["choices"][0]["delta"]["content"]
14662                    .as_str()
14663                    .map(str::to_string)
14664            })
14665            .collect();
14666        assert_eq!(content, "ends in Pro");
14667    }
14668
14669    #[tokio::test]
14670    async fn stream_worker_error_is_a_data_chunk_not_a_named_event() {
14671        let (tx, rx) = worker::event_channel();
14672        tx.send(Event::Error(worker::EngineError::engine("boom")))
14673            .unwrap();
14674        drop(tx);
14675        let resp = sse_response(
14676            rx,
14677            "m".into(),
14678            true,
14679            None,
14680            Envelope::new(true),
14681            Vec::new(),
14682            None,
14683        )
14684        .into_response();
14685        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
14686            .await
14687            .unwrap();
14688        let body = String::from_utf8(bytes.to_vec()).unwrap();
14689        // OpenAI clients only parse `data:` lines — no named `event: error` on the chat shape.
14690        assert!(
14691            !body.contains("event: error"),
14692            "named SSE event leaked: {body}"
14693        );
14694        let lines: Vec<&str> = body
14695            .lines()
14696            .filter_map(|l| l.strip_prefix("data: "))
14697            .collect();
14698        let err: serde_json::Value = serde_json::from_str(lines[0]).unwrap();
14699        assert_eq!(err["error"]["message"], "boom");
14700        assert_eq!(err["error"]["type"], "server_error");
14701        assert_eq!(err["error"]["code"], "engine_error");
14702        assert_eq!(lines.last(), Some(&"[DONE]"));
14703    }
14704
14705    #[test]
14706    fn ttft_sse_marker_ignores_keepalive_comments() {
14707        assert!(!is_sse_data_frame(b": keep-alive\n\n"));
14708        assert!(is_sse_data_frame(b"data: {\"choices\":[]}\n\n"));
14709        assert!(is_sse_data_frame(
14710            b"event: error\ndata: {\"error\":\"failed\"}\n\n"
14711        ));
14712    }
14713
14714    #[tokio::test]
14715    async fn error_bodies_use_the_openai_object_shape() {
14716        let (tx, rx) = worker::event_channel();
14717        tx.send(Event::Error(worker::EngineError::model_not_found(
14718            "unknown model \"x\"",
14719        )))
14720        .unwrap();
14721        drop(tx);
14722        let response =
14723            blocking_response(rx, "m".into(), true, Vec::new(), None, Envelope::new(true)).await;
14724        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
14725        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
14726            .await
14727            .unwrap();
14728        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
14729        // {"error": {message, type, param, code}} — the object every OpenAI SDK parses.
14730        assert_eq!(payload["error"]["message"], "unknown model \"x\"");
14731        assert_eq!(payload["error"]["type"], "invalid_request_error");
14732        assert_eq!(payload["error"]["param"], "model");
14733        assert_eq!(payload["error"]["code"], "model_not_found");
14734    }
14735
14736    // ---- G6 taxonomy (lane/serve-hardening) --------------------------------------------
14737    //
14738    // The mapping is the deliverable, so it is asserted class by class rather than through
14739    // one happy-path example. Before this lane EVERY row below answered 400
14740    // invalid_request_error, which no OpenAI-compatible SDK retries.
14741
14742    fn retry_after(resp: &Response) -> Option<String> {
14743        resp.headers()
14744            .get(axum::http::header::RETRY_AFTER)
14745            .and_then(|v| v.to_str().ok())
14746            .map(str::to_string)
14747    }
14748
14749    // ---- timeout_ms + deadline-aware admission (lane/deadline-billing-20260823) ------
14750
14751    async fn body_value(resp: Response) -> serde_json::Value {
14752        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
14753            .await
14754            .expect("body");
14755        serde_json::from_slice(&bytes).expect("json body")
14756    }
14757
14758    /// POST one chat request through the FULL handler, retrying the server's contention
14759    /// refusals until the request is actually ADMITTED.
14760    ///
14761    /// `reserve_pending_admit` reads the process-global lane backlog
14762    /// (`worker::ADMISSION_RESERVATIONS`) and the test runner is parallel: any sibling
14763    /// test's in-flight reservation window puts `backlog > 0` under this request, and
14764    /// with a fresh state's empty metrics the queue-wait estimate is the 2 s static —
14765    /// more than the minimum 1000 ms deadline these tests declare, so the request sheds
14766    /// 429 `shed_deadline` before admission. Schedule-dependent and load-amplified: on a
14767    /// loaded box the windows stretch, and the deadline tests observed 429 where they
14768    /// asserted 408 (the 2026-09-01 accrace flake). The shed is the server's documented,
14769    /// unbilled refusal-under-load — so the honest test answer is to treat it as "try
14770    /// again", never as the outcome: the caller's assertions still require the ADMITTED
14771    /// request to prove its 408/billing contract, and a 429 that is not a shed stays a
14772    /// loud failure.
14773    async fn chat_completion_admitted(st: &AppState, req: serde_json::Value) -> Response {
14774        let mut last_shed = serde_json::Value::Null;
14775        for _ in 0..50 {
14776            let resp = chat_completions(
14777                State(st.clone()),
14778                HeaderMap::new(),
14779                None,
14780                Json(serde_json::from_value(req.clone()).unwrap()),
14781            )
14782            .await;
14783            if resp.status() != StatusCode::TOO_MANY_REQUESTS {
14784                return resp;
14785            }
14786            let body = body_value(resp).await;
14787            let code = body["error"]["code"].as_str().unwrap_or_default();
14788            assert!(
14789                code.starts_with("shed_"),
14790                "only a contention shed may be retried; any other 429 is a finding: {body}"
14791            );
14792            last_shed = body;
14793            tokio::time::sleep(std::time::Duration::from_millis(20)).await;
14794        }
14795        // The shed message names the estimate and the remaining deadline, so triage can
14796        // tell a genuinely saturated run from a shed regression that never clears.
14797        panic!(
14798            "still shed after 50 attempts — either load the retry budget cannot absorb \
14799             or a shed that no longer clears; last refusal: {last_shed}"
14800        );
14801    }
14802
14803    #[test]
14804    fn timeout_ms_parses_clamps_nothing_and_names_every_refusal() {
14805        // Absent / explicit null => the DOCUMENTED default, not "no deadline".
14806        assert_eq!(parse_timeout_ms(None).unwrap(), TIMEOUT_MS_DEFAULT);
14807        assert_eq!(
14808            parse_timeout_ms(Some(&serde_json::Value::Null)).unwrap(),
14809            TIMEOUT_MS_DEFAULT
14810        );
14811        // In-range values are honored EXACTLY (no clamping — an out-of-range value is a
14812        // refusal, because silently shortening a caller's deadline is the accepted-and-
14813        // ignored class the standard-surface law bans).
14814        for ms in [TIMEOUT_MS_MIN, 5_000, 45_000, TIMEOUT_MS_MAX] {
14815            assert_eq!(parse_timeout_ms(Some(&json!(ms))).unwrap(), ms);
14816        }
14817        // Out of range both ways: named 400 stating the range AND the streaming hatch.
14818        for bad in [0u64, TIMEOUT_MS_MIN - 1, TIMEOUT_MS_MAX + 1, 600_000] {
14819            let err = parse_timeout_ms(Some(&json!(bad))).expect_err("out of range must refuse");
14820            assert!(err.contains("timeout_ms"), "{err}");
14821            assert!(
14822                err.contains(&TIMEOUT_MS_MIN.to_string())
14823                    && err.contains(&TIMEOUT_MS_MAX.to_string()),
14824                "the message must state the range: {err}"
14825            );
14826            assert!(
14827                err.contains("stream"),
14828                "the message must point at streaming for longer work: {err}"
14829            );
14830        }
14831        // Unknown types refuse too (never a silent default).
14832        for bad in [json!("30s"), json!(1.5), json!(true), json!({}), json!([])] {
14833            let err = parse_timeout_ms(Some(&bad)).expect_err("bad type must refuse");
14834            assert!(
14835                err.contains("timeout_ms") && err.contains("stream"),
14836                "{err}"
14837            );
14838        }
14839        // Negative numbers are not u64 — same named refusal, not a panic.
14840        assert!(parse_timeout_ms(Some(&json!(-1))).is_err());
14841    }
14842
14843    /// The named 400 is IDENTICAL on all four surfaces (standard-surface law) and costs
14844    /// neither a slot nor a ledger receipt.
14845    #[tokio::test]
14846    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
14847    async fn a_bad_timeout_ms_is_the_same_named_400_on_every_surface() {
14848        let _l = drain_lock();
14849        let st = fake_worker_state();
14850
14851        let comp = completions(
14852            State(st.clone()),
14853            HeaderMap::new(),
14854            None,
14855            Json(
14856                serde_json::from_value(json!({
14857                    "model": "m", "prompt": "t", "timeout_ms": 90_001}))
14858                .unwrap(),
14859            ),
14860        )
14861        .await;
14862        assert_eq!(comp.status(), StatusCode::BAD_REQUEST);
14863        let chat = chat_completions(
14864            State(st.clone()),
14865            HeaderMap::new(),
14866            None,
14867            Json(
14868                serde_json::from_value(json!({
14869                    "model": "m", "messages": [{"role": "user", "content": "t"}],
14870                    "timeout_ms": 90_001}))
14871                .unwrap(),
14872            ),
14873        )
14874        .await;
14875        assert_eq!(chat.status(), StatusCode::BAD_REQUEST);
14876        let resp_api = responses_api::responses(
14877            State(st.clone()),
14878            HeaderMap::new(),
14879            None,
14880            axum::body::Bytes::from(
14881                json!({"model": "m", "input": "t", "timeout_ms": 90_001}).to_string(),
14882            ),
14883        )
14884        .await;
14885        assert_eq!(resp_api.status(), StatusCode::BAD_REQUEST);
14886        let msgs = anthropic::messages(
14887            State(st.clone()),
14888            HeaderMap::new(),
14889            None,
14890            axum::body::Bytes::from(
14891                json!({"model": "m", "max_tokens": 16,
14892                       "messages": [{"role": "user", "content": "t"}],
14893                       "timeout_ms": 90_001})
14894                .to_string(),
14895            ),
14896        )
14897        .await;
14898        assert_eq!(msgs.status(), StatusCode::BAD_REQUEST);
14899
14900        // OpenAI-shaped surfaces name the param; all four name the field in the message.
14901        for (surface, resp) in [
14902            ("/v1/completions", comp),
14903            ("/v1/chat/completions", chat),
14904            ("/v1/responses", resp_api),
14905        ] {
14906            let body = body_value(resp).await;
14907            assert_eq!(body["error"]["type"], "invalid_request_error", "{surface}");
14908            assert_eq!(body["error"]["param"], "timeout_ms", "{surface}");
14909            let m = body["error"]["message"].as_str().unwrap();
14910            assert!(
14911                m.contains("90000") && m.contains("stream"),
14912                "{surface}: {m}"
14913            );
14914        }
14915        // Anthropic shape: no param slot, so the message carries it.
14916        let body = body_value(msgs).await;
14917        assert_eq!(body["error"]["type"], "invalid_request_error");
14918        let m = body["error"]["message"].as_str().unwrap();
14919        assert!(m.contains("timeout_ms") && m.contains("stream"), "{m}");
14920    }
14921
14922    /// Wrong TYPE refuses too — the reasoning-schema philosophy, one surface shown end to
14923    /// end (the parser gate above covers the type matrix).
14924    #[tokio::test]
14925    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
14926    async fn a_non_integer_timeout_ms_is_a_named_400() {
14927        let _l = drain_lock();
14928        let st = fake_worker_state();
14929        let resp = chat_completions(
14930            State(st),
14931            HeaderMap::new(),
14932            None,
14933            Json(
14934                serde_json::from_value(json!({
14935                    "model": "m", "messages": [{"role": "user", "content": "t"}],
14936                    "timeout_ms": "30s"}))
14937                .unwrap(),
14938            ),
14939        )
14940        .await;
14941        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
14942        let body = body_value(resp).await;
14943        assert_eq!(body["error"]["param"], "timeout_ms");
14944    }
14945
14946    /// NON-STREAMING deadline: the response delivers the partial with our standard error
14947    /// object (`code: "deadline_exceeded"`), generation is CANCELLED (the worker's channel
14948    /// is closed — observed via the receiver the fake worker holds), and the receipt
14949    /// settles through `complete_deadline_partial` with the delivered counts — the
14950    /// census-distinct billable outcome, never plain `complete`.
14951    #[tokio::test]
14952    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
14953    async fn a_missed_non_stream_deadline_delivers_the_partial_bills_it_and_cancels_generation() {
14954        let _l = drain_lock();
14955        // A worker that publishes prompt usage and ONE token, then never finishes — the
14956        // shape a real deadline miss has (work done, no terminal event in time). It keeps
14957        // the request's sender so the handler's drop of rx is observable as a closed
14958        // channel: that closure IS the cancel signal the worker acts on at its next tick.
14959        let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<Cmd>();
14960        let cancel_seen = Arc::new(std::sync::atomic::AtomicBool::new(false));
14961        let worker_cancel = cancel_seen.clone();
14962        let health = health::WorkerHealth::new();
14963        let h = health.clone();
14964        std::thread::spawn(move || {
14965            h.mark_ready();
14966            while let Ok(Cmd::Generate(req)) = cmd_rx.recv() {
14967                worker::release_pending_admit();
14968                worker::release_admission_reservation(req.lane);
14969                let _ = req.tx.send(Event::PromptUsage {
14970                    n_prompt: 1,
14971                    n_cached: 0,
14972                });
14973                let _ = req.tx.send(Event::Token {
14974                    id: 1,
14975                    text: "partial".into(),
14976                });
14977                // The abort signal a real worker watches for at every tick: the request's
14978                // event channel closing. Set the flag the test polls when it appears.
14979                for _ in 0..5_000 {
14980                    if req.tx.is_closed() {
14981                        worker_cancel.store(true, std::sync::atomic::Ordering::SeqCst);
14982                        break;
14983                    }
14984                    std::thread::sleep(std::time::Duration::from_millis(1));
14985                }
14986            }
14987        });
14988        for _ in 0..2_000 {
14989            if health.live().is_ok() {
14990                break;
14991            }
14992            std::thread::sleep(std::time::Duration::from_millis(1));
14993        }
14994        let mut st = fake_worker_state();
14995        st.cmd_tx = cmd_tx;
14996        st.health = health;
14997        let mock = MockMetering::admit_all();
14998        st.metering = Some(mock.clone());
14999
15000        let resp = chat_completion_admitted(
15001            &st,
15002            json!({
15003                "model": "m", "messages": [{"role": "user", "content": "t"}],
15004                "timeout_ms": 1_000}),
15005        )
15006        .await;
15007
15008        // CONTRACT CHANGED 2026-08-26 (owner report: a 30k-token non-streaming request
15009        // timed out). This used to assert a 408 with the generated tokens DISCARDED. The
15010        // deadline now DELIVERS what was produced, because throwing away 90 s of a
15011        // customer's tokens to answer an error is the bug, not the safety valve.
15012        assert_eq!(resp.status(), StatusCode::OK);
15013        let body = body_value(resp).await;
15014        assert!(
15015            body["choices"][0]["message"]["content"]
15016                .as_str()
15017                .unwrap()
15018                .contains("partial"),
15019            "the tokens generated before the cut must be delivered: {body}"
15020        );
15021        // OpenRouter dialect, and deliberately NOT finish_reason "length": no provider's
15022        // finish-reason enum has a time value, so reporting a time cut as "length" would
15023        // tell the caller to ask for more tokens when the truth is that it must stream.
15024        assert_eq!(body["choices"][0]["finish_reason"], "error");
15025        assert_eq!(
15026            body["choices"][0]["native_finish_reason"],
15027            "deadline_exceeded"
15028        );
15029        assert_eq!(body["error"]["code"], "deadline_exceeded");
15030        assert_eq!(body["error"]["metadata"]["error_type"], "timeout");
15031        let message = body["error"]["message"].as_str().unwrap();
15032        assert!(
15033            message.contains("1000") && message.contains("stream"),
15034            "the partial must name the deadline and the streaming alternative: {message}"
15035        );
15036        assert_eq!(body["usage"]["completion_tokens"], 1);
15037
15038        // GENERATION CANCELLED: the worker saw its event channel close. Polled with an
15039        // AWAIT (not a blocking recv): the event forwarder that owns the worker-side
15040        // receiver is a tokio task, and a blocking wait on this single-threaded test
15041        // runtime would starve the very task whose exit closes the channel.
15042        let mut cancelled = false;
15043        for _ in 0..500 {
15044            if cancel_seen.load(std::sync::atomic::Ordering::SeqCst) {
15045                cancelled = true;
15046                break;
15047            }
15048            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
15049        }
15050        assert!(
15051            cancelled,
15052            "the deadline must CANCEL generation (worker's event channel closed)"
15053        );
15054
15055        // SEAM: the delivered tokens settle through the census-distinct terminal —
15056        // `complete_deadline_partial`, never plain `complete`. Writing `completed` here
15057        // (the first version of this lane) lost the deadline everywhere except an
15058        // ephemeral log line — a review caught it.
15059        let events = mock.events();
15060        assert!(
15061            events.contains(&MeterEvent::DeadlinePartial {
15062                prompt: 1,
15063                cached: 0,
15064                completion: 1,
15065            }),
15066            "the partial must settle as a deadline-partial with worker-truth counts: {events:?}"
15067        );
15068        assert!(
15069            !events
15070                .iter()
15071                .any(|e| matches!(e, MeterEvent::Complete { .. })),
15072            "a deadline cut must stay distinguishable from a full answer: {events:?}"
15073        );
15074    }
15075
15076    /// The other half of the same contract: a deadline that lands with NOTHING generated
15077    /// still answers 408 and still bills zero. There is no partial to deliver, so the
15078    /// original promise ("we answer inside the deadline or you don't pay") stands.
15079    #[tokio::test]
15080    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
15081    async fn a_deadline_missed_before_any_token_is_still_408_and_unbilled() {
15082        let _l = drain_lock();
15083        let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<Cmd>();
15084        let health = health::WorkerHealth::new();
15085        let h = health.clone();
15086        std::thread::spawn(move || {
15087            h.mark_ready();
15088            // Prompt usage only: admitted, prefilling, and NOT ONE token emitted before
15089            // the deadline — the shape of a prompt too large to prefill in the window.
15090            while let Ok(Cmd::Generate(req)) = cmd_rx.recv() {
15091                worker::release_pending_admit();
15092                worker::release_admission_reservation(req.lane);
15093                let _ = req.tx.send(Event::PromptUsage {
15094                    n_prompt: 1,
15095                    n_cached: 0,
15096                });
15097                for _ in 0..5_000 {
15098                    if req.tx.is_closed() {
15099                        break;
15100                    }
15101                    std::thread::sleep(std::time::Duration::from_millis(1));
15102                }
15103            }
15104        });
15105        for _ in 0..2_000 {
15106            if health.live().is_ok() {
15107                break;
15108            }
15109            std::thread::sleep(std::time::Duration::from_millis(1));
15110        }
15111        let mut st = fake_worker_state();
15112        st.cmd_tx = cmd_tx;
15113        st.health = health;
15114        let mock = MockMetering::admit_all();
15115        st.metering = Some(mock.clone());
15116        let resp = chat_completion_admitted(
15117            &st,
15118            json!({
15119                "model": "m", "messages": [{"role": "user", "content": "t"}],
15120                "timeout_ms": 1_000}),
15121        )
15122        .await;
15123        assert_eq!(resp.status(), StatusCode::REQUEST_TIMEOUT);
15124        // Still retryable, still no invented Retry-After.
15125        assert!(resp.headers().get("x-should-retry").is_none());
15126        assert_eq!(retry_after(&resp), None);
15127        let body = body_value(resp).await;
15128        assert_eq!(body["error"]["code"], "deadline_exceeded");
15129        assert!(
15130            body["error"]["message"]
15131                .as_str()
15132                .unwrap()
15133                .contains("not billed"),
15134            "the zero-token 408 keeps the billing promise: {body}"
15135        );
15136        let events = mock.events();
15137        assert!(
15138            events.contains(&MeterEvent::Unbilled {
15139                outcome: "deadline_exceeded",
15140                status: 408,
15141                code: "deadline_exceeded".into(),
15142            }),
15143            "the named zero-debit census outcome, not the generic reject — every sibling \
15144             deadline path settles this one: {events:?}"
15145        );
15146    }
15147
15148    /// STREAMING, deadline MISSED before the first token: still a pre-header 408 and no
15149    /// bill — nothing was delivered, so there is nothing to charge for.
15150    #[tokio::test]
15151    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
15152    async fn a_stream_that_misses_ttft_is_a_preheader_408_and_not_billed() {
15153        let _l = drain_lock();
15154        // Admits (publishes prompt usage) but produces NO token — a prefill that overruns.
15155        let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<Cmd>();
15156        let health = health::WorkerHealth::new();
15157        let h = health.clone();
15158        std::thread::spawn(move || {
15159            h.mark_ready();
15160            while let Ok(Cmd::Generate(req)) = cmd_rx.recv() {
15161                worker::release_pending_admit();
15162                worker::release_admission_reservation(req.lane);
15163                let _ = req.tx.send(Event::PromptUsage {
15164                    n_prompt: 1,
15165                    n_cached: 0,
15166                });
15167                while !req.tx.is_closed() {
15168                    std::thread::sleep(std::time::Duration::from_millis(1));
15169                }
15170            }
15171        });
15172        for _ in 0..2_000 {
15173            if health.live().is_ok() {
15174                break;
15175            }
15176            std::thread::sleep(std::time::Duration::from_millis(1));
15177        }
15178        let mut st = fake_worker_state();
15179        st.cmd_tx = cmd_tx;
15180        st.health = health;
15181        let mock = MockMetering::admit_all();
15182        st.metering = Some(mock.clone());
15183
15184        let resp = chat_completion_admitted(
15185            &st,
15186            json!({
15187                "model": "m", "messages": [{"role": "user", "content": "t"}],
15188                "stream": true, "timeout_ms": 1_000}),
15189        )
15190        .await;
15191        // PRE-HEADER: a real status, not a 200 with an error chunk — the whole reason the
15192        // TTFT peek exists (a committed 200 leaves no status for a router to act on).
15193        assert_eq!(resp.status(), StatusCode::REQUEST_TIMEOUT);
15194        let body = body_value(resp).await;
15195        assert_eq!(body["error"]["code"], "deadline_exceeded");
15196        assert!(
15197            body["error"]["message"]
15198                .as_str()
15199                .unwrap()
15200                .contains("first token"),
15201            "the streaming message must say the deadline bounded TIME TO FIRST TOKEN: {body}"
15202        );
15203        let events = mock.events();
15204        assert!(
15205            events.contains(&MeterEvent::Unbilled {
15206                outcome: "deadline_exceeded",
15207                status: 408,
15208                code: "deadline_exceeded".into(),
15209            }),
15210            "a TTFT miss must settle unbilled under the deadline outcome: {events:?}"
15211        );
15212    }
15213
15214    /// STREAMING, first token DELIVERED inside the deadline: the parameter is SPENT. A
15215    /// stream whose remaining tokens take longer than timeout_ms still completes and
15216    /// bills in full — post-first-token immunity, the other half of the streaming rule.
15217    #[tokio::test]
15218    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
15219    async fn a_stream_is_immune_to_the_deadline_after_its_first_token() {
15220        let _l = drain_lock();
15221        // 4 tokens, 400ms apart: the first arrives well inside a 1s deadline and the
15222        // stream then runs ~1.6s — past it. The stream must still finish normally.
15223        let mut st = fake_worker_state_with_steps(4, std::time::Duration::from_millis(400));
15224        let mock = MockMetering::admit_all();
15225        st.metering = Some(mock.clone());
15226        let resp = chat_completion_admitted(
15227            &st,
15228            json!({
15229                "model": "m", "messages": [{"role": "user", "content": "t"}],
15230                "stream": true, "timeout_ms": 1_000}),
15231        )
15232        .await;
15233        assert_eq!(
15234            resp.status(),
15235            StatusCode::OK,
15236            "TTFT was met — 200 is correct"
15237        );
15238        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
15239            .await
15240            .expect("the stream must run to completion past the deadline");
15241        let text = String::from_utf8(bytes.to_vec()).unwrap();
15242        assert!(text.contains("[DONE]"), "stream did not complete: {text}");
15243        let events = mock.events();
15244        assert!(
15245            events
15246                .iter()
15247                .any(|e| matches!(e, MeterEvent::Complete { completion: 4, .. })),
15248            "a stream past its deadline after first token still settles as COMPLETE with \
15249             all four tokens: {events:?}"
15250        );
15251    }
15252
15253    /// `worker::ADMISSION_RESERVATIONS` / `worker::PENDING_ADMITS` are PROCESS GLOBALS and
15254    /// the test runner is parallel: two admission tests pumping the same lane counter race,
15255    /// and the loser reads the winner's swapped value (caught live in a co-tenant-loaded
15256    /// local-ci window 2026-08-30 — `deadline_shed_is_interactive_only...` shed on a free
15257    /// slot because a sibling had the interactive counter at max_queue_depth for that
15258    /// instant). Every test that WRITES these counters serializes here.
15259    fn admission_counters_guard() -> std::sync::MutexGuard<'static, ()> {
15260        static COUNTERS: std::sync::Mutex<()> = std::sync::Mutex::new(());
15261        COUNTERS
15262            .lock()
15263            .unwrap_or_else(|poisoned| poisoned.into_inner())
15264    }
15265
15266    /// Put an admission counter back on DROP — including the drop that unwinds a failed
15267    /// assertion. The swap tests below used to restore with a trailing `store(prev)`
15268    /// AFTER their asserts, so one red left the process-global lane backlog pinned at the
15269    /// swapped value (e.g. max_queue_depth) and every later-admitted request in the run
15270    /// shed 429 — the 2026-09-01 one-flake-becomes-21-reds cascade, counter form.
15271    struct CounterRestore<'a>(&'a std::sync::atomic::AtomicUsize, usize);
15272    impl Drop for CounterRestore<'_> {
15273        fn drop(&mut self) {
15274            self.0.store(self.1, std::sync::atomic::Ordering::Release);
15275        }
15276    }
15277
15278    /// `reserve_pending_admit` on the interactive lane, retrying through the TRANSIENT
15279    /// contention shed: the lane backlog is a process-global reading
15280    /// (`worker::ADMISSION_RESERVATIONS`) and the runner is parallel, so a sibling
15281    /// handler test's in-flight reservation puts `backlog > 0` for an instant and the
15282    /// wait estimate then deadline-sheds a tight deadline — schedule-dependent,
15283    /// load-amplified (the 2026-09-01 class). A PERSISTENT shed is not contention and
15284    /// still fails the caller's assert: whatever pins the backlog for all 50 attempts
15285    /// (e.g. a cross-lane leak) is a finding. Any refusal other than the deadline shed
15286    /// panics immediately.
15287    #[allow(clippy::result_large_err)] // allow: passes reserve_pending_admit's own contract through unchanged
15288    fn reserve_interactive_through_contention(
15289        st: &AppState,
15290        rl: &RateLimit,
15291        deadline_ms: u64,
15292    ) -> Result<PendingAdmissionGuard, (Response, &'static str)> {
15293        let reserve = || {
15294            reserve_pending_admit(
15295                st,
15296                lanes::Lane::Interactive,
15297                rl,
15298                RequestDeadline::starting_now(deadline_ms),
15299            )
15300        };
15301        let mut g = reserve();
15302        for _ in 0..50 {
15303            match &g {
15304                Ok(_) => break,
15305                Err((_, "shed_deadline")) => {
15306                    std::thread::sleep(std::time::Duration::from_millis(10));
15307                    g = reserve();
15308                }
15309                Err((_, outcome)) => panic!("unexpected refusal: {outcome}"),
15310            }
15311        }
15312        g
15313    }
15314
15315    /// BACKPRESSURE, absolute bound: at MEMRA_MAX_QUEUE_DEPTH the request sheds with 429 +
15316    /// Retry-After, outcome `shed_queue`, no bill, X-RateLimit trio present.
15317    #[test]
15318    fn the_queue_bound_sheds_with_429_retry_after_and_the_ratelimit_trio() {
15319        let _counters = admission_counters_guard();
15320        let st = fake_worker_state();
15321        let lane = lanes::Lane::Interactive;
15322        let cap = lane_cap(lane);
15323        let counter = &worker::ADMISSION_RESERVATIONS[lane.idx()];
15324        let prev = counter.swap(max_queue_depth(cap), std::sync::atomic::Ordering::AcqRel);
15325        let _restore = CounterRestore(counter, prev);
15326        let rl = RateLimit {
15327            limit: cap,
15328            remaining: 0,
15329            reset_s: 1,
15330        };
15331        let (resp, outcome) = reserve_pending_admit(
15332            &st,
15333            lane,
15334            &rl,
15335            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15336        )
15337        .map(|_| ())
15338        .expect_err("a backlog at the bound must shed");
15339        assert_eq!(outcome, "shed_queue");
15340        assert_eq!(resp.status(), StatusCode::TOO_MANY_REQUESTS);
15341        assert!(
15342            retry_after(&resp).is_some(),
15343            "a shed must carry Retry-After so the router's spill can act on it"
15344        );
15345        // The trio rides the shed exactly like every other 429 on this surface.
15346        let stamped = rl.attach(resp);
15347        for h in [
15348            "x-ratelimit-limit",
15349            "x-ratelimit-remaining",
15350            "x-ratelimit-reset",
15351        ] {
15352            assert!(stamped.headers().get(h).is_some(), "missing {h}");
15353        }
15354    }
15355
15356    /// BACKPRESSURE, deadline test: the SAME loaded lane admits a request whose deadline
15357    /// can absorb the estimated wait and sheds one whose deadline cannot — the shed is
15358    /// keyed on the caller's own deadline, not on load alone.
15359    #[test]
15360    fn admission_sheds_only_when_the_estimated_wait_cannot_fit_the_deadline() {
15361        let _counters = admission_counters_guard();
15362        let st = fake_worker_state();
15363        let lane = lanes::Lane::Interactive;
15364        let cap = lane_cap(lane);
15365        {
15366            let mut m = st.metrics.lock().unwrap();
15367            m.completed = 10;
15368            m.tokens_out = 1_000;
15369            m.step_p50_ms = 10.0; // mean service ~1s
15370        }
15371        let counter = &worker::ADMISSION_RESERVATIONS[lane.idx()];
15372        let prev = counter.swap(cap, std::sync::atomic::Ordering::AcqRel); // one wave ahead
15373        let _restore = CounterRestore(counter, prev);
15374        let rl = RateLimit {
15375            limit: cap,
15376            remaining: 0,
15377            reset_s: 1,
15378        };
15379        // A 90s deadline absorbs a ~2s wait: ADMIT (never shed a request that can wait).
15380        let admitted = reserve_pending_admit(
15381            &st,
15382            lane,
15383            &rl,
15384            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15385        );
15386        assert!(
15387            admitted.is_ok(),
15388            "a request whose deadline covers the estimate must be admitted"
15389        );
15390        drop(admitted); // release the reservation the admit took
15391        // A 1s deadline cannot: SHED, with the estimate as Retry-After.
15392        let (resp, outcome) = reserve_pending_admit(
15393            &st,
15394            lane,
15395            &rl,
15396            RequestDeadline::starting_now(TIMEOUT_MS_MIN),
15397        )
15398        .map(|_| ())
15399        .expect_err("a deadline shorter than the estimated wait must shed");
15400        assert_eq!(outcome, "shed_deadline");
15401        assert_eq!(resp.status(), StatusCode::TOO_MANY_REQUESTS);
15402        assert!(retry_after(&resp).is_some());
15403    }
15404
15405    /// Free capacity never deadline-sheds, and neither do the dark lanes (they shed at cap
15406    /// inside the worker — the deadline gate here is interactive-only by design).
15407    #[test]
15408    fn deadline_shed_is_interactive_only_and_silent_with_free_slots() {
15409        let _counters = admission_counters_guard();
15410        let st = fake_worker_state();
15411        let cap = lane_cap(lanes::Lane::Interactive);
15412        {
15413            let mut m = st.metrics.lock().unwrap();
15414            m.completed = 10;
15415            m.tokens_out = 100_000; // an enormous estimate...
15416            m.step_p50_ms = 100.0;
15417        }
15418        // ...but a free slot and an empty lane mean no wait to estimate.
15419        let free = RateLimit {
15420            limit: cap,
15421            remaining: 1,
15422            reset_s: 0,
15423        };
15424        // Retried through the transient sibling-reservation shed (see the helper): this
15425        // enormous estimate sheds even the minimum deadline whenever the process-global
15426        // backlog reads > 0 for an instant. The assertion still requires the free-slot
15427        // admit to prove itself.
15428        let g = reserve_interactive_through_contention(&st, &free, TIMEOUT_MS_MIN);
15429        assert!(
15430            g.is_ok(),
15431            "free capacity must admit regardless of the estimate"
15432        );
15433        drop(g);
15434        // Loaded, but a dark-lane request: the worker's own lane gate owns those, and the
15435        // deadline shed must not fire off the interactive lane.
15436        let full = RateLimit {
15437            limit: cap,
15438            remaining: 0,
15439            reset_s: 5,
15440        };
15441        for lane in [lanes::Lane::Judge, lanes::Lane::Harvest] {
15442            let counter = &worker::ADMISSION_RESERVATIONS[lane.idx()];
15443            let prev = counter.swap(1, std::sync::atomic::Ordering::AcqRel); // backlog > 0
15444            let _restore = CounterRestore(counter, prev);
15445            let g = reserve_pending_admit(
15446                &st,
15447                lane,
15448                &full,
15449                RequestDeadline::starting_now(TIMEOUT_MS_MIN),
15450            );
15451            assert!(
15452                g.is_ok(),
15453                "{lane:?} must not be deadline-shed by the interactive gate"
15454            );
15455            drop(g);
15456        }
15457    }
15458
15459    /// THE DEFECT SHAPE, kept as the flag-off contract (darklanes#5; prod measured
15460    /// 2026-09-01: 133-137 s of pre-header silence, never a 429). The engine queue is
15461    /// saturated (a full wave of reservations ahead), the HTTP lane still has slots,
15462    /// and the caller's deadline can absorb the estimated wait: no arm sheds, the
15463    /// request queues silently. With `MEMRA_QUEUE_WAIT_CEILING_S` absent or 0 this is
15464    /// today's behavior byte-for-byte, and this test is what holds that line.
15465    #[test]
15466    fn a_saturated_queue_with_free_http_slots_queues_silently_without_a_ceiling() {
15467        let _counters = admission_counters_guard();
15468        let st = fake_worker_state();
15469        let lane = lanes::Lane::Interactive;
15470        let cap = lane_cap(lane);
15471        {
15472            let mut m = st.metrics.lock().unwrap();
15473            m.completed = 10;
15474            m.tokens_out = 1_000; // mean 100 tok/request...
15475            m.step_p50_ms = 100.0; // ...x 100 ms = ~10 s/wave; one wave ahead => ~20 s
15476        }
15477        let counter = &worker::ADMISSION_RESERVATIONS[lane.idx()];
15478        let prev = counter.swap(cap, std::sync::atomic::Ordering::AcqRel); // one wave ahead
15479        let _restore = CounterRestore(counter, prev);
15480        // The HTTP lane is NOT full: a free slot remains, but the wave ahead means this
15481        // request still waits ~20 s for engine capacity.
15482        let free = RateLimit {
15483            limit: cap,
15484            remaining: 1,
15485            reset_s: 0,
15486        };
15487        let g = reserve_pending_admit(
15488            &st,
15489            lane,
15490            &free,
15491            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15492        );
15493        assert!(
15494            g.is_ok(),
15495            "flag off: a ~20 s projected wait whose deadline can absorb it queues \
15496             silently (no 429) - the darklanes#5 defect shape, preserved by default"
15497        );
15498        drop(g);
15499    }
15500
15501    /// QUEUE-WAIT CEILING, shed arm: the exact defect shape above (saturated engine
15502    /// queue, free HTTP slot, patient deadline), but with a ceiling below the estimate:
15503    /// 429, `code: shed_queue_wait`, Retry-After = the estimate (with its ms twin), and
15504    /// the X-RateLimit trio rides the shed like every other 429 on this surface.
15505    #[test]
15506    fn the_queue_wait_ceiling_sheds_with_429_retry_after_and_the_ratelimit_trio() {
15507        let _counters = admission_counters_guard();
15508        let st = fake_worker_state();
15509        let lane = lanes::Lane::Interactive;
15510        let cap = lane_cap(lane);
15511        {
15512            let mut m = st.metrics.lock().unwrap();
15513            m.completed = 10;
15514            m.tokens_out = 1_000; // mean 100 tok/request...
15515            m.step_p50_ms = 100.0; // ...x 100 ms = ~10 s/wave; one wave ahead => ~20 s
15516        }
15517        let counter = &worker::ADMISSION_RESERVATIONS[lane.idx()];
15518        let prev = counter.swap(cap, std::sync::atomic::Ordering::AcqRel); // one wave ahead
15519        let _restore = CounterRestore(counter, prev);
15520        let free = RateLimit {
15521            limit: cap,
15522            remaining: 1,
15523            reset_s: 0,
15524        };
15525        let (resp, outcome) = reserve_pending_admit_with_ceiling(
15526            &st,
15527            lane,
15528            &free,
15529            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15530            5, // ceiling 5 s, estimate ~20 s
15531        )
15532        .map(|_| ())
15533        .expect_err("a projected wait past the ceiling must shed");
15534        assert_eq!(outcome, "shed_queue_wait");
15535        assert_eq!(resp.status(), StatusCode::TOO_MANY_REQUESTS);
15536        assert_eq!(
15537            retry_after(&resp).as_deref(),
15538            Some("20"),
15539            "Retry-After must carry the estimate (~10 s/wave x 2 waves)"
15540        );
15541        assert_eq!(
15542            resp.headers()
15543                .get("retry-after-ms")
15544                .and_then(|v| v.to_str().ok()),
15545            Some("20000"),
15546            "the ms twin must match"
15547        );
15548        let stamped = free.attach(resp);
15549        for h in [
15550            "x-ratelimit-limit",
15551            "x-ratelimit-remaining",
15552            "x-ratelimit-reset",
15553        ] {
15554            assert!(stamped.headers().get(h).is_some(), "missing {h}");
15555        }
15556    }
15557
15558    /// QUEUE-WAIT CEILING, admit arm + lane scope: an estimate UNDER the ceiling still
15559    /// queues exactly as before (the ceiling is a ceiling, not a load switch), and the
15560    /// dark lanes are never judged by it (the worker's own lane gate owns those).
15561    #[test]
15562    fn the_queue_wait_ceiling_admits_under_it_and_never_touches_dark_lanes() {
15563        let _counters = admission_counters_guard();
15564        let st = fake_worker_state();
15565        let lane = lanes::Lane::Interactive;
15566        let cap = lane_cap(lane);
15567        {
15568            let mut m = st.metrics.lock().unwrap();
15569            m.completed = 10;
15570            m.tokens_out = 1_000;
15571            m.step_p50_ms = 100.0; // ~10 s/wave; one wave ahead => ~20 s
15572        }
15573        let counter = &worker::ADMISSION_RESERVATIONS[lane.idx()];
15574        let prev = counter.swap(cap, std::sync::atomic::Ordering::AcqRel);
15575        let _restore = CounterRestore(counter, prev);
15576        let free = RateLimit {
15577            limit: cap,
15578            remaining: 1,
15579            reset_s: 0,
15580        };
15581        let g = reserve_pending_admit_with_ceiling(
15582            &st,
15583            lane,
15584            &free,
15585            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15586            60, // ceiling 60 s, estimate ~20 s
15587        );
15588        assert!(
15589            g.is_ok(),
15590            "an estimate under the ceiling must admit and queue as before"
15591        );
15592        drop(g);
15593        // Dark lanes: a backlog and a 1 s ceiling, and still no shed from this gate.
15594        let full = RateLimit {
15595            limit: cap,
15596            remaining: 0,
15597            reset_s: 5,
15598        };
15599        for dark in [lanes::Lane::Judge, lanes::Lane::Harvest] {
15600            let counter = &worker::ADMISSION_RESERVATIONS[dark.idx()];
15601            let prev = counter.swap(1, std::sync::atomic::Ordering::AcqRel); // backlog > 0
15602            let _restore = CounterRestore(counter, prev);
15603            let g = reserve_pending_admit_with_ceiling(
15604                &st,
15605                dark,
15606                &full,
15607                RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15608                1,
15609            );
15610            assert!(
15611                g.is_ok(),
15612                "{dark:?} must not be shed by the interactive queue-wait ceiling"
15613            );
15614            drop(g);
15615        }
15616    }
15617
15618    /// QUEUE-WAIT CEILING, arm precedence: with the ceiling set, the existing arms still
15619    /// answer first and unchanged. A backlog at the absolute bound stays `shed_queue`;
15620    /// a deadline shorter than the estimate stays `shed_deadline`.
15621    #[test]
15622    fn the_queue_wait_ceiling_leaves_the_existing_shed_arms_first_and_unchanged() {
15623        let _counters = admission_counters_guard();
15624        let st = fake_worker_state();
15625        let lane = lanes::Lane::Interactive;
15626        let cap = lane_cap(lane);
15627        {
15628            let mut m = st.metrics.lock().unwrap();
15629            m.completed = 10;
15630            m.tokens_out = 1_000;
15631            m.step_p50_ms = 100.0;
15632        }
15633        let rl = RateLimit {
15634            limit: cap,
15635            remaining: 0,
15636            reset_s: 1,
15637        };
15638        let counter = &worker::ADMISSION_RESERVATIONS[lane.idx()];
15639        // At the absolute bound: shed_queue wins even with a 1 s ceiling armed.
15640        let prev = counter.swap(max_queue_depth(cap), std::sync::atomic::Ordering::AcqRel);
15641        let _restore = CounterRestore(counter, prev);
15642        assert!(matches!(
15643            reserve_pending_admit_with_ceiling(
15644                &st,
15645                lane,
15646                &rl,
15647                RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15648                1,
15649            ),
15650            Err((_, "shed_queue"))
15651        ));
15652        // Below the bound with a too-short deadline: shed_deadline wins over the ceiling.
15653        counter.store(cap, std::sync::atomic::Ordering::Release);
15654        assert!(matches!(
15655            reserve_pending_admit_with_ceiling(
15656                &st,
15657                lane,
15658                &rl,
15659                RequestDeadline::starting_now(TIMEOUT_MS_MIN),
15660                1,
15661            ),
15662            Err((_, "shed_deadline"))
15663        ));
15664    }
15665
15666    /// QUEUE-WAIT CEILING wiring: the production wrapper feeds the OnceLock env read into
15667    /// the judged path (wiring-assertions law: anchored on the INVOCATION in
15668    /// comment-stripped text, scoped to the wrapper body so this test's own literals
15669    /// cannot satisfy it).
15670    #[test]
15671    fn the_queue_wait_ceiling_is_wired_through_the_production_wrapper() {
15672        let src = include_str!("lib.rs");
15673        let code: String = src
15674            .lines()
15675            .map(|l| l.split("//").next().unwrap_or(""))
15676            .collect::<Vec<_>>()
15677            .join("\n");
15678        let start = code
15679            .find("pub(crate) fn reserve_pending_admit(")
15680            .expect("the production wrapper exists");
15681        let rest = &code[start..];
15682        let end = rest.find("\nfn ").unwrap_or(rest.len());
15683        let wrapper = &rest[..end];
15684        assert!(
15685            wrapper.contains(
15686                "reserve_pending_admit_with_ceiling(st, lane, rl, deadline, queue_wait_ceiling_s())"
15687            ),
15688            "every production ingress must judge the ceiling the env read armed"
15689        );
15690    }
15691
15692    #[test]
15693    fn pending_admission_reservation_is_atomic_and_rolls_back_on_drop() {
15694        let _counters = admission_counters_guard();
15695        let st = fake_worker_state();
15696        let cap = lane_cap(lanes::Lane::Interactive);
15697        let bound = max_queue_depth(cap);
15698        assert!(bound > 0, "the queue bound must admit at least one request");
15699        let rl = RateLimit {
15700            limit: cap,
15701            remaining: 0,
15702            reset_s: 1,
15703        };
15704        let _ = worker::PENDING_ADMITS.fetch_update(
15705            std::sync::atomic::Ordering::AcqRel,
15706            std::sync::atomic::Ordering::Acquire,
15707            |_| Some(0),
15708        );
15709        let counter = &worker::ADMISSION_RESERVATIONS[lanes::Lane::Interactive.idx()];
15710        let _restore = CounterRestore(counter, 0);
15711        counter.store(bound - 1, std::sync::atomic::Ordering::Release);
15712        let guard = reserve_pending_admit(
15713            &st,
15714            lanes::Lane::Interactive,
15715            &rl,
15716            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15717        )
15718        .expect("the final queue slot should be reservable");
15719        assert_eq!(
15720            worker::PENDING_ADMITS.load(std::sync::atomic::Ordering::Acquire),
15721            1
15722        );
15723        assert_eq!(counter.load(std::sync::atomic::Ordering::Acquire), bound);
15724        drop(guard);
15725        assert_eq!(
15726            worker::PENDING_ADMITS.load(std::sync::atomic::Ordering::Acquire),
15727            0
15728        );
15729        assert_eq!(
15730            counter.load(std::sync::atomic::Ordering::Acquire),
15731            bound - 1
15732        );
15733
15734        counter.store(bound, std::sync::atomic::Ordering::Release);
15735        let rejected = reserve_pending_admit(
15736            &st,
15737            lanes::Lane::Interactive,
15738            &rl,
15739            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15740        );
15741        assert!(matches!(rejected, Err((_, "shed_queue"))));
15742    }
15743
15744    #[test]
15745    fn admission_reservations_are_lane_scoped() {
15746        let _counters = admission_counters_guard();
15747        let st = fake_worker_state();
15748        let harvest = lanes::Lane::Harvest;
15749        let interactive = lanes::Lane::Interactive;
15750        let harvest_counter = &worker::ADMISSION_RESERVATIONS[harvest.idx()];
15751        let interactive_counter = &worker::ADMISSION_RESERVATIONS[interactive.idx()];
15752        let _restore = CounterRestore(harvest_counter, 0);
15753        harvest_counter.store(
15754            max_queue_depth(lane_cap(harvest)),
15755            std::sync::atomic::Ordering::Release,
15756        );
15757        interactive_counter.store(0, std::sync::atomic::Ordering::Release);
15758        let free = RateLimit {
15759            limit: lane_cap(interactive),
15760            remaining: 1,
15761            reset_s: 0,
15762        };
15763        // Two arms, because the harvest bound (max_queue_depth of its cap 8 = 32) is far
15764        // below every interactive threshold: a cross-lane backlog leak (a lane.idx()
15765        // slip in reserve_pending_admit) would put 32 on the interactive reading — never
15766        // enough for its shed_queue bound (256), and only 2 s of estimated wait. So the
15767        // MAX arm proves the path is open, and the MIN arm is the teeth: with the leak,
15768        // that pinned 2 s estimate deadline-sheds a 1000 ms request on EVERY attempt and
15769        // outlasts the retry budget; healthy, backlog 0 + a free slot admits with no
15770        // estimate applied at all. The retry absorbs only the TRANSIENT sibling
15771        // reservation (load-flaked run 2 of the 2026-09-01 triple), which clears between
15772        // attempts — the harvest counter this test pins does not.
15773        let guard = reserve_pending_admit(
15774            &st,
15775            interactive,
15776            &free,
15777            RequestDeadline::starting_now(TIMEOUT_MS_MAX),
15778        )
15779        .expect("a full harvest queue must not consume interactive capacity");
15780        drop(guard);
15781        let tight = reserve_interactive_through_contention(&st, &free, TIMEOUT_MS_MIN);
15782        assert!(
15783            tight.is_ok(),
15784            "a full harvest queue must not deadline-shed a tight interactive request \
15785             (a backlog that outlasts the retry budget here is a cross-lane leak, not \
15786             contention)"
15787        );
15788        drop(tight);
15789        let harvest_rl = RateLimit {
15790            limit: lane_cap(harvest),
15791            remaining: 0,
15792            reset_s: 1,
15793        };
15794        assert!(matches!(
15795            reserve_pending_admit(
15796                &st,
15797                harvest,
15798                &harvest_rl,
15799                RequestDeadline::starting_now(TIMEOUT_MS_MAX)
15800            ),
15801            Err((_, "shed_queue"))
15802        ));
15803    }
15804
15805    #[test]
15806    fn taxonomy_maps_every_class_to_its_status_and_code() {
15807        use worker::{EngineError as E, ErrClass as C};
15808        let cases: Vec<(worker::EngineError, StatusCode, &str, &str)> = vec![
15809            (
15810                E::invalid_param("bad json", "response_format"),
15811                StatusCode::BAD_REQUEST,
15812                "invalid_request_error",
15813                "",
15814            ),
15815            (
15816                E::context_length("prompt (9000 tok) >= context cap (8192)"),
15817                StatusCode::BAD_REQUEST,
15818                "invalid_request_error",
15819                "context_length_exceeded",
15820            ),
15821            (
15822                E::model_not_found("unknown model \"nope\""),
15823                StatusCode::BAD_REQUEST,
15824                "invalid_request_error",
15825                "model_not_found",
15826            ),
15827            (
15828                E::rate_limit("lane judge is at capacity, retry"),
15829                StatusCode::TOO_MANY_REQUESTS,
15830                "rate_limit_error",
15831                "rate_limit_exceeded",
15832            ),
15833            (
15834                E::overloaded("no VRAM for a new session"),
15835                StatusCode::SERVICE_UNAVAILABLE,
15836                "server_error",
15837                "overloaded",
15838            ),
15839            (
15840                E::engine("graph step failed: launch error"),
15841                StatusCode::INTERNAL_SERVER_ERROR,
15842                "server_error",
15843                "engine_error",
15844            ),
15845        ];
15846        for (err, want_status, want_type, want_code) in cases {
15847            let (status, etype, code) = class_http(err.class);
15848            assert_eq!(status, want_status, "{:?}", err);
15849            assert_eq!(etype, want_type, "{:?}", err);
15850            if !want_code.is_empty() {
15851                assert_eq!(code, Some(want_code), "{:?}", err);
15852            }
15853            // the rendered body agrees with the mapping
15854            let body = engine_error_body(&err);
15855            assert_eq!(body["error"]["message"], err.message);
15856            assert_eq!(body["error"]["type"], want_type);
15857        }
15858        // and no class is silently missing from the match
15859        for c in [
15860            C::InvalidRequest,
15861            C::ContextLength,
15862            C::ModelNotFound,
15863            C::RateLimit,
15864            C::Overloaded,
15865            C::Engine,
15866        ] {
15867            let (s, t, _) = class_http(c);
15868            assert!(s.is_client_error() || s.is_server_error(), "{c:?} -> {s}");
15869            assert!(!t.is_empty());
15870        }
15871    }
15872
15873    #[test]
15874    fn a_cuda_oom_message_is_capacity_503_not_a_500() {
15875        // The one deliberate text rule: the driver's own OOM text promotes an engine fault to
15876        // Overloaded, because the box ran out of VRAM (a retryable capacity condition) rather
15877        // than hitting a bug. Same predicate the step-OOM park path uses, so the two paths
15878        // cannot disagree about what an OOM is.
15879        let e = worker::EngineError::engine(
15880            "step error: DriverError(CUDA_ERROR_OUT_OF_MEMORY, \"out of memory\")",
15881        );
15882        let resp = engine_error_response(&e);
15883        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
15884        assert_eq!(retry_after(&resp).as_deref(), Some("5"));
15885    }
15886
15887    #[test]
15888    fn retry_headers_follow_the_sdk_contract() {
15889        // openai-python reads retry-after-ms FIRST, then retry-after, and ABANDONS the retry
15890        // if the delay exceeds 120 s; litellm honors retry-after only for 0 < v <= 60. So:
15891        // integer seconds, <= 60, with a matching millisecond twin.
15892        for e in [
15893            worker::EngineError::rate_limit("shed"),
15894            worker::EngineError::overloaded("no VRAM"),
15895        ] {
15896            let resp = engine_error_response(&e);
15897            let ra = retry_after(&resp).expect("retryable class must carry Retry-After");
15898            let secs: u64 = ra
15899                .parse()
15900                .expect("Retry-After must be integer delay-seconds");
15901            assert!(
15902                secs > 0 && secs <= 60,
15903                "Retry-After {secs}s outside the honored window"
15904            );
15905            let ms = resp
15906                .headers()
15907                .get("retry-after-ms")
15908                .unwrap()
15909                .to_str()
15910                .unwrap();
15911            assert_eq!(
15912                ms.parse::<u64>().unwrap(),
15913                secs * 1000,
15914                "the two headers disagree"
15915            );
15916            assert!(
15917                resp.headers().get("x-should-retry").is_none(),
15918                "a retryable class must not say x-should-retry: false"
15919            );
15920        }
15921    }
15922
15923    /// D2 gap G6 (lane/d2-engine-gaps-20260831): the predictive-admission would-reject
15924    /// path must be byte-compatible with the existing shed contract. Both flow through
15925    /// `retry_contract_response`, and this gate pins that: same status, byte-identical
15926    /// retry header pair, same body schema with `type=rate_limit_error`; only the
15927    /// `code` names the producer. Shadow mode LOGS the horizon; this is the response
15928    /// the enforcing flip sends, qualified before any flip exists.
15929    #[tokio::test]
15930    async fn admit_predict_reject_matches_shed_contract() {
15931        // Today's shed 429, exactly as reserve_pending_admit shapes it.
15932        let shed = retry_contract_response(
15933            (
15934                StatusCode::TOO_MANY_REQUESTS,
15935                Json(error_body(
15936                    "interactive queue is at its bound",
15937                    "rate_limit_error",
15938                    None,
15939                    Some("shed_queue"),
15940                )),
15941            )
15942                .into_response(),
15943            Some(7),
15944        );
15945        // The enforcing predictor's would-reject: the producer-computed horizon rides
15946        // the SAME machinery.
15947        let predict = engine_error_response(&worker::EngineError::rate_limit_after(
15948            "predicted KV-to-completion exceeds the box budget; retry",
15949            7,
15950        ));
15951        assert_eq!(shed.status(), predict.status());
15952        for header in ["retry-after", "retry-after-ms"] {
15953            assert_eq!(
15954                shed.headers().get(header),
15955                predict.headers().get(header),
15956                "header {header} must be byte-identical to the shed contract"
15957            );
15958        }
15959        let shed_body: serde_json::Value = serde_json::from_slice(
15960            &axum::body::to_bytes(shed.into_body(), usize::MAX)
15961                .await
15962                .unwrap(),
15963        )
15964        .unwrap();
15965        let predict_body: serde_json::Value = serde_json::from_slice(
15966            &axum::body::to_bytes(predict.into_body(), usize::MAX)
15967                .await
15968                .unwrap(),
15969        )
15970        .unwrap();
15971        assert_eq!(shed_body["error"]["type"], predict_body["error"]["type"]);
15972        assert_eq!(predict_body["error"]["type"], "rate_limit_error");
15973        let shed_keys: Vec<&String> = shed_body["error"].as_object().unwrap().keys().collect();
15974        let predict_keys: Vec<&String> =
15975            predict_body["error"].as_object().unwrap().keys().collect();
15976        assert_eq!(shed_keys, predict_keys, "same body schema, key for key");
15977        assert_eq!(predict_body["error"]["code"], "rate_limit_exceeded");
15978
15979        // The producer horizon obeys the shed clamp window (integer seconds, <= 60)...
15980        let clamped = engine_error_response(&worker::EngineError::rate_limit_after("m", 400));
15981        assert_eq!(retry_after(&clamped).as_deref(), Some("60"));
15982        // ...and its absence keeps the historical class default (no regression).
15983        let plain = engine_error_response(&worker::EngineError::rate_limit("m"));
15984        assert_eq!(retry_after(&plain).as_deref(), Some("2"));
15985    }
15986
15987    #[tokio::test]
15988    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
15989    async fn command_send_failure_obeys_the_retry_contract() {
15990        let _l = drain_lock();
15991        let mut st = fake_worker_state();
15992        let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<Cmd>();
15993        drop(cmd_rx);
15994        st.cmd_tx = cmd_tx;
15995
15996        let completion = completions(
15997            State(st.clone()),
15998            axum::http::HeaderMap::new(),
15999            None,
16000            Json(
16001                serde_json::from_value(serde_json::json!({
16002                    "model": "m", "prompt": "test"
16003                }))
16004                .unwrap(),
16005            ),
16006        )
16007        .await;
16008        let chat = chat_completions(
16009            State(st),
16010            axum::http::HeaderMap::new(),
16011            None,
16012            Json(
16013                serde_json::from_value(serde_json::json!({
16014                    "model": "m", "messages": [{"role": "user", "content": "test"}]
16015                }))
16016                .unwrap(),
16017            ),
16018        )
16019        .await;
16020
16021        for resp in [completion, chat] {
16022            assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
16023            assert_eq!(retry_after(&resp).as_deref(), Some("2"));
16024            assert_eq!(resp.headers().get("retry-after-ms").unwrap(), "2000");
16025            assert_ne!(
16026                resp.headers()
16027                    .get("x-should-retry")
16028                    .and_then(|v| v.to_str().ok()),
16029                Some("false")
16030            );
16031            let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
16032                .await
16033                .unwrap();
16034            let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
16035            assert_eq!(payload["error"]["type"], "server_error");
16036            assert_eq!(payload["error"]["code"], "overloaded");
16037        }
16038    }
16039
16040    #[test]
16041    fn unfixable_client_errors_say_x_should_retry_false() {
16042        // Retrying the identical bytes cannot succeed, and a client that retries on status
16043        // alone would hammer for nothing. openai-python honors this override explicitly.
16044        for e in [
16045            worker::EngineError::model_not_found("unknown model \"x\""),
16046            worker::EngineError::context_length("prompt too long"),
16047            worker::EngineError::invalid_param("bad", "messages"),
16048        ] {
16049            let resp = engine_error_response(&e);
16050            assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
16051            assert_eq!(resp.headers().get("x-should-retry").unwrap(), "false");
16052            assert!(
16053                retry_after(&resp).is_none(),
16054                "a 400 must not promise a retry window"
16055            );
16056        }
16057    }
16058
16059    #[tokio::test]
16060    async fn a_closed_worker_channel_is_503_not_500() {
16061        // The worker thread died (panicked, unrecoverable) mid-request: the Event channel
16062        // closes with neither Done nor Error. The client's retry may land on a restarted
16063        // process, so this is capacity-class with a window — not a bare 500.
16064        let (tx, rx) = worker::event_channel();
16065        drop(tx);
16066        let resp =
16067            blocking_response(rx, "m".into(), true, Vec::new(), None, Envelope::new(true)).await;
16068        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
16069        assert_eq!(retry_after(&resp).as_deref(), Some("5"));
16070    }
16071
16072    #[tokio::test]
16073    async fn a_dark_lane_shed_is_429_with_an_openai_object_body() {
16074        // The admission peek used to answer `{"error": "<string>"}` — a bare string where every SDK
16075        // expects an object, which renders as a blank message client-side.
16076        let (tx, rx) = worker::event_channel();
16077        tx.send(Event::Error(worker::EngineError::rate_limit(
16078            "lane judge shed: interactive p99 over budget, retry",
16079        )))
16080        .unwrap();
16081        let (resp, error_code) = peek_admission(rx)
16082            .await
16083            .expect_err("a shed must not be forwarded into the stream");
16084        assert_eq!(resp.status(), StatusCode::TOO_MANY_REQUESTS);
16085        assert_eq!(error_code, "rate_limit_exceeded");
16086        assert_eq!(retry_after(&resp).as_deref(), Some("2"));
16087        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
16088            .await
16089            .unwrap();
16090        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
16091        assert!(
16092            payload["error"].is_object(),
16093            "bare-string error body: {payload}"
16094        );
16095        assert_eq!(payload["error"]["type"], "rate_limit_error");
16096        assert!(
16097            payload["error"]["message"]
16098                .as_str()
16099                .unwrap()
16100                .contains("shed")
16101        );
16102    }
16103
16104    #[tokio::test]
16105    async fn interactive_admission_error_is_a_preheader_429() {
16106        // An unattainable long-context request must remain retryable even when the client asked
16107        // for streaming; committing a 200 before this worker verdict would prevent failover.
16108        let (tx, rx) = worker::event_channel();
16109        tx.send(Event::Error(worker::EngineError::rate_limit(
16110            "KV capacity unavailable",
16111        )))
16112        .unwrap();
16113        let (resp, error_code) = peek_admission(rx)
16114            .await
16115            .expect_err("admission error must stay pre-header");
16116        assert_eq!(resp.status(), StatusCode::TOO_MANY_REQUESTS);
16117        assert_eq!(error_code, "rate_limit_exceeded");
16118    }
16119
16120    #[tokio::test]
16121    async fn admission_peek_preserves_context_error_for_the_ledger() {
16122        let (tx, rx) = worker::event_channel();
16123        tx.send(Event::Error(worker::EngineError::context_length(
16124            "prompt exceeds configured model maximum",
16125        )))
16126        .unwrap();
16127        let (resp, error_code) = peek_admission(rx)
16128            .await
16129            .expect_err("context rejection must stay pre-header");
16130        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
16131        assert_eq!(error_code, "context_length_exceeded");
16132    }
16133
16134    #[tokio::test]
16135    async fn admission_peek_replays_prompt_usage_without_waiting_for_a_token() {
16136        let (tx, rx) = worker::event_channel();
16137        tx.send(Event::PromptUsage {
16138            n_prompt: 262_143,
16139            n_cached: 0,
16140        })
16141        .unwrap();
16142        let mut replay = peek_admission(rx).await.expect("successful admission");
16143        assert!(matches!(
16144            replay.recv().await,
16145            Some(Event::PromptUsage {
16146                n_prompt: 262_143,
16147                n_cached: 0
16148            }),
16149        ));
16150    }
16151
16152    #[test]
16153    fn penalties_plumb_from_http_to_sampler_config() {
16154        // gap-scan F3: the fields existed in SamplerConfig all along — assert the HTTP
16155        // layer actually delivers them, with the one cross-path history window armed.
16156        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
16157            "model": "m", "messages": [{"role": "user", "content": "task"}],
16158            "frequency_penalty": 0.5, "presence_penalty": 0.25, "repetition_penalty": 1.1
16159        }))
16160        .unwrap();
16161        let (tx, _rx) = worker::event_channel();
16162        let cfg = build_chat_request(req, None, tx, lanes::Lane::Interactive, None)
16163            .unwrap()
16164            .request
16165            .sampler_cfg;
16166        assert_eq!(cfg.penalty_freq, 0.5);
16167        assert_eq!(cfg.penalty_present, 0.25);
16168        assert_eq!(cfg.penalty_repeat, 1.1);
16169        assert_eq!(cfg.penalty_last_n, memra_engine::spec::PEN_WINDOW_MAX);
16170
16171        let req: CompletionReq = serde_json::from_value(serde_json::json!({
16172            "model": "m", "prompt": "task", "frequency_penalty": 1.5
16173        }))
16174        .unwrap();
16175        let (tx, _rx) = worker::event_channel();
16176        let cfg = build_request(&req, tx, lanes::Lane::Interactive, None).sampler_cfg;
16177        assert_eq!(cfg.penalty_freq, 1.5);
16178        assert_eq!(cfg.penalty_last_n, memra_engine::spec::PEN_WINDOW_MAX);
16179
16180        // no penalties set -> window off, byte-identical legacy config.
16181        let req: CompletionReq = serde_json::from_value(serde_json::json!({
16182            "model": "m", "prompt": "task"
16183        }))
16184        .unwrap();
16185        let (tx, _rx) = worker::event_channel();
16186        let cfg = build_request(&req, tx, lanes::Lane::Interactive, None).sampler_cfg;
16187        assert_eq!(cfg.penalty_last_n, 0);
16188        assert_eq!(cfg.penalty_repeat, 1.0);
16189    }
16190
16191    #[test]
16192    fn omitted_temperature_is_openai_default_not_greedy() {
16193        // dogfood F4: `#[serde(default)] temperature: f32` yielded 0.0 = greedy, so any
16194        // client that omits temperature (the owner's own agentic pill, the OpenAI SDK's
16195        // documented "leave it out" path) got locked into deterministic argmax — same
16196        // context in, same token out, identical tool-call cycles forever. OpenAI's
16197        // default-when-omitted is 1.0 on BOTH surfaces.
16198        //
16199        // SCOPE, after lane/vendor-default-sampling (2026-08-19): this test now pins the
16200        // API-STANDARD FALLBACK — the path taken when NO per-model vendor default is declared
16201        // and the model's arch publishes none either (`SamplingDefaults::default()`, which is
16202        // what `build_chat_request`/`build_request` pass here). That path must stay exactly as
16203        // it was: 1.0 / 1.0 / 0 / 0, pure-temp, never greedy. A SERVED model's omitted request
16204        // resolves to its vendor recommendation instead — see
16205        // `vendor_sampling_defaults_fill_only_the_omitted_fields` and
16206        // `vendor_defaults_leave_the_pure_temp_sampled_spec_regime`. Both laws are live at once:
16207        // "no declaration = OpenAI-compatible", "declaration = the vendor's own numbers".
16208        let chat_temp = |body: serde_json::Value| {
16209            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
16210            let (tx, _rx) = worker::event_channel();
16211            build_chat_request(req, None, tx, lanes::Lane::Interactive, None)
16212                .unwrap()
16213                .request
16214                .sampler_cfg
16215                .temperature
16216        };
16217        let comp_temp = |body: serde_json::Value| {
16218            let req: CompletionReq = serde_json::from_value(body).unwrap();
16219            let (tx, _rx) = worker::event_channel();
16220            build_request(&req, tx, lanes::Lane::Interactive, None)
16221                .sampler_cfg
16222                .temperature
16223        };
16224
16225        // OMITTED => 1.0 (sampled), all the way through to the SamplerConfig.
16226        assert_eq!(
16227            chat_temp(serde_json::json!({
16228            "model": "m", "messages": [{"role": "user", "content": "t"}]})),
16229            1.0,
16230            "omitted chat temperature must be the OpenAI 1.0 default, not 0.0/greedy"
16231        );
16232        assert_eq!(
16233            comp_temp(serde_json::json!({
16234            "model": "m", "prompt": "t"})),
16235            1.0,
16236            "omitted completions temperature must be the OpenAI 1.0 default"
16237        );
16238
16239        // EXPLICIT 0 still means greedy — a caller asking for determinism gets it.
16240        assert_eq!(
16241            chat_temp(serde_json::json!({
16242            "model": "m", "messages": [{"role": "user", "content": "t"}],
16243            "temperature": 0.0})),
16244            0.0,
16245            "explicit temperature 0 must stay greedy"
16246        );
16247        assert_eq!(
16248            comp_temp(serde_json::json!({
16249            "model": "m", "prompt": "t", "temperature": 0})),
16250            0.0,
16251            "explicit temperature 0 must stay greedy"
16252        );
16253        // and the greedy predicate agrees (this is what gates the spec/graph arms).
16254        assert!(
16255            memra_engine::sampler::Sampler::new(sampler_config(
16256                0.0,
16257                0,
16258                1.0,
16259                0.0,
16260                0.0,
16261                0.0,
16262                1.0,
16263                Some(0)
16264            ))
16265            .is_greedy()
16266        );
16267        assert!(
16268            !memra_engine::sampler::Sampler::new(sampler_config(
16269                1.0,
16270                0,
16271                1.0,
16272                0.0,
16273                0.0,
16274                0.0,
16275                1.0,
16276                Some(0)
16277            ))
16278            .is_greedy()
16279        );
16280
16281        // explicit non-default values still pass through untouched.
16282        assert_eq!(
16283            chat_temp(serde_json::json!({
16284            "model": "m", "messages": [{"role": "user", "content": "t"}],
16285            "temperature": 0.7})),
16286            0.7
16287        );
16288
16289        // OMITTED filter defaults: top_p disabled at 1.0 (OpenAI default), top_k/min_p
16290        // disabled at 0 (not OpenAI params — OpenRouter/HF convention, 0 = keep all).
16291        // An omitted-temperature request must therefore be PURE temperature-1.0 sampling.
16292        let req: CompletionReq = serde_json::from_value(serde_json::json!({
16293            "model": "m", "prompt": "t"}))
16294        .unwrap();
16295        let (tx, _rx) = worker::event_channel();
16296        let cfg = build_request(&req, tx, lanes::Lane::Interactive, None).sampler_cfg;
16297        assert_eq!(cfg.top_p, 1.0, "omitted top_p = OpenAI 1.0 = disabled");
16298        assert_eq!(cfg.top_k, 0, "omitted top_k = disabled");
16299        assert_eq!(cfg.min_p, 0.0, "omitted min_p = disabled");
16300        assert_eq!(cfg.penalty_last_n, 0, "omitted penalties = window off");
16301        // and it lands in the PURE-TEMP sampled-spec regime — the one that keeps the
16302        // in-graph sampled draft chain (spec.rs `pure_temp`). Filters/penalties would still
16303        // be spec-eligible but would drop the draft to the eager chain, so the default
16304        // request shape must stay in the fast regime.
16305        assert!(
16306            memra_engine::sampler::Sampler::new(cfg).is_spec_sampling(),
16307            "the omitted-temperature default must ride sampled spec's pure-temp regime"
16308        );
16309    }
16310
16311    #[test]
16312    fn step35_chat_uses_published_sampling_defaults_only_when_omitted() {
16313        let caps = ModelCaps {
16314            chat_temperature_default: Some(0.5),
16315            chat_top_p_default: Some(0.9),
16316            chat_ok: true,
16317            ..Default::default()
16318        };
16319        let cfg = |extra: serde_json::Value| {
16320            let mut body = serde_json::json!({
16321                "model": "step35",
16322                "messages": [{"role": "user", "content": "task"}]
16323            });
16324            body.as_object_mut()
16325                .unwrap()
16326                .extend(extra.as_object().unwrap().clone());
16327            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
16328            let (tx, _rx) = worker::event_channel();
16329            build_chat_request(req, Some(&caps), tx, lanes::Lane::Interactive, None)
16330                .unwrap()
16331                .request
16332                .sampler_cfg
16333        };
16334
16335        let omitted = cfg(serde_json::json!({}));
16336        assert_eq!(omitted.temperature, 0.5);
16337        assert_eq!(omitted.top_p, 0.9);
16338
16339        let explicit_temp = cfg(serde_json::json!({"temperature": 0.7}));
16340        assert_eq!(explicit_temp.temperature, 0.7);
16341        assert_eq!(
16342            explicit_temp.top_p, 0.9,
16343            "omitting top_p must retain StepFun's nucleus default"
16344        );
16345
16346        let explicit = cfg(serde_json::json!({"temperature": 0.0, "top_p": 1.0}));
16347        assert_eq!(
16348            explicit.temperature, 0.0,
16349            "explicit greedy must remain authoritative"
16350        );
16351        assert_eq!(
16352            explicit.top_p, 1.0,
16353            "explicit untruncated sampling must remain authoritative"
16354        );
16355    }
16356
16357    /// qwen/qwen3.8-27b's own model card, § Best Practices / § API Usage Tip (thinking mode —
16358    /// the mode our template defaults to): temperature 1.0, top_p 0.95, top_k 20, min_p 0.0,
16359    /// presence_penalty 0.0, repetition_penalty 1.0.
16360    fn qwen38_vendor_defaults() -> SamplingDefaults {
16361        SamplingDefaults {
16362            temperature: Some(1.0),
16363            top_p: Some(0.95),
16364            top_k: Some(20),
16365            min_p: Some(0.0),
16366            presence_penalty: Some(0.0),
16367            repetition_penalty: Some(1.0),
16368            frequency_penalty: None,
16369        }
16370    }
16371
16372    /// google/gemma-4-31B-it's own model card, § Best Practices / 1. Sampling Parameters
16373    /// ("Use the following standardized sampling configuration across all use cases"):
16374    /// temperature 1.0, top_p 0.95, top_k 64. Google recommends nothing for min_p or the
16375    /// penalties, so those stay None -> API-standard (never invented).
16376    fn gemma4_vendor_defaults() -> SamplingDefaults {
16377        SamplingDefaults {
16378            temperature: Some(1.0),
16379            top_p: Some(0.95),
16380            top_k: Some(64),
16381            ..Default::default()
16382        }
16383    }
16384
16385    #[test]
16386    fn vendor_sampling_defaults_fill_only_the_omitted_fields() {
16387        // Owner ruling 2026-08-19: "we don't have to serve greedy, we measure greedy but we
16388        // serve what the user chooses" / "we default to what are the recommendations" /
16389        // "greedy can create issues". So an OMITTING client gets the model vendor's own
16390        // published numbers, and every explicit client value still wins.
16391        let d = ModelSamplingDefaults::single(gemma4_vendor_defaults());
16392        let chat = |extra: serde_json::Value| {
16393            let mut body = serde_json::json!({
16394                "model": "google/gemma-4-31b-it",
16395                "messages": [{"role": "user", "content": "task"}],
16396                // pin the seed so two configs are comparable field-by-field.
16397                "seed": 7
16398            });
16399            body.as_object_mut()
16400                .unwrap()
16401                .extend(extra.as_object().unwrap().clone());
16402            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
16403            let (tx, _rx) = worker::event_channel();
16404            build_chat_request_with_trace(
16405                req,
16406                Some(&ModelCaps {
16407                    chat_ok: true,
16408                    ..Default::default()
16409                }),
16410                tx,
16411                lanes::Lane::Interactive,
16412                None,
16413                None,
16414                None,
16415                &d,
16416            )
16417            .unwrap()
16418            .request
16419            .sampler_cfg
16420        };
16421
16422        // OMITTED EVERYTHING => the vendor's recommendation, not greedy and not 1.0/1.0/0/0.
16423        let omitted = chat(serde_json::json!({}));
16424        assert_eq!(omitted.temperature, 1.0, "gemma-4 card temperature");
16425        assert_eq!(omitted.top_p, 0.95, "gemma-4 card top_p");
16426        assert_eq!(omitted.top_k, 64, "gemma-4 card top_k");
16427        // Google recommends no min_p / penalties: API-standard, NOT invented.
16428        assert_eq!(omitted.min_p, 0.0, "undeclared min_p stays API-standard");
16429        assert_eq!(omitted.penalty_repeat, 1.0);
16430        assert_eq!(omitted.penalty_freq, 0.0);
16431        assert_eq!(omitted.penalty_present, 0.0);
16432        assert_eq!(omitted.penalty_last_n, 0, "no penalty => no history window");
16433        assert!(
16434            !memra_engine::sampler::Sampler::new(omitted).is_greedy(),
16435            "the vendor default must NOT be greedy — that is the whole point of the lane"
16436        );
16437
16438        // EXPLICIT temperature 0 => TRUE GREEDY, vendor default notwithstanding. This is the
16439        // invariant every determinism gate we own depends on.
16440        let greedy = chat(serde_json::json!({"temperature": 0}));
16441        assert_eq!(
16442            greedy.temperature, 0.0,
16443            "explicit temperature 0 stays greedy"
16444        );
16445        assert!(
16446            memra_engine::sampler::Sampler::new(greedy).is_greedy(),
16447            "an explicit temperature 0 must satisfy the greedy predicate that gates the \
16448             spec/graph exactness arms"
16449        );
16450
16451        // Each explicit field wins ALONE — the others still take the vendor value.
16452        let one_field = chat(serde_json::json!({"top_k": 3}));
16453        assert_eq!(one_field.top_k, 3, "explicit top_k wins");
16454        assert_eq!(
16455            one_field.temperature, 1.0,
16456            "omitting temperature still takes the vendor value"
16457        );
16458        assert_eq!(one_field.top_p, 0.95, "omitting top_p still takes vendor");
16459
16460        // Explicit DISABLING values are honored, not mistaken for absence: top_k 0 = keep all,
16461        // top_p 1.0 = untruncated. A client must be able to switch the vendor filters OFF.
16462        let disabled = chat(serde_json::json!({"top_k": 0, "top_p": 1.0}));
16463        assert_eq!(
16464            disabled.top_k, 0,
16465            "an explicit top_k 0 means KEEP ALL, not 'unset'"
16466        );
16467        assert_eq!(
16468            disabled.top_p, 1.0,
16469            "an explicit top_p 1.0 means untruncated"
16470        );
16471
16472        // Explicit penalties are honored and arm the one cross-path bounded window.
16473        let penal = chat(serde_json::json!({"presence_penalty": 1.5}));
16474        assert_eq!(penal.penalty_present, 1.5);
16475        assert_eq!(penal.penalty_last_n, memra_engine::spec::PEN_WINDOW_MAX);
16476    }
16477
16478    #[test]
16479    fn vendor_sampling_defaults_are_identical_on_every_surface() {
16480        // STANDARD-SURFACE LAW. Before this lane the surfaces DISAGREED: the chat body's
16481        // temperature/top_p were `Option` and consulted the per-model default, while
16482        // /v1/completions used bare `f32`s with `serde(default)` — so "omitted" was
16483        // indistinguishable from "1.0" there and the per-model default was unreachable on the
16484        // raw-prompt surface. Both bodies now funnel into ONE `resolve_sampler_config`.
16485        //
16486        // /v1/messages and /v1/responses are covered transitively and by construction: both
16487        // translate into a ChatCompletionReq and call the same `build_chat_request_with_trace`
16488        // with the same `ModelSamplingDefaults` (see surfaces.rs). Their own tests pin the other
16489        // half of the contract — that an omitted field translates to an ABSENT field rather
16490        // than a zero-filled one.
16491        let d = qwen38_vendor_defaults();
16492        let md = ModelSamplingDefaults::single(d);
16493        let comp = |extra: serde_json::Value| {
16494            let mut body = serde_json::json!({
16495                "model": "qwen/qwen3.8-27b", "prompt": "task", "seed": 11 });
16496            body.as_object_mut()
16497                .unwrap()
16498                .extend(extra.as_object().unwrap().clone());
16499            let req: CompletionReq = serde_json::from_value(body).unwrap();
16500            let (tx, _rx) = worker::event_channel();
16501            build_request_with_trace(&req, tx, lanes::Lane::Interactive, None, None, &d).sampler_cfg
16502        };
16503        let chat = |extra: serde_json::Value| {
16504            let mut body = serde_json::json!({
16505                "model": "qwen/qwen3.8-27b",
16506                "messages": [{"role": "user", "content": "task"}],
16507                "seed": 11 });
16508            body.as_object_mut()
16509                .unwrap()
16510                .extend(extra.as_object().unwrap().clone());
16511            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
16512            let (tx, _rx) = worker::event_channel();
16513            build_chat_request_with_trace(
16514                req,
16515                Some(&ModelCaps {
16516                    chat_ok: true,
16517                    ..Default::default()
16518                }),
16519                tx,
16520                lanes::Lane::Interactive,
16521                None,
16522                None,
16523                None,
16524                &md,
16525            )
16526            .unwrap()
16527            .request
16528            .sampler_cfg
16529        };
16530
16531        for extra in [
16532            serde_json::json!({}),
16533            serde_json::json!({"temperature": 0}),
16534            serde_json::json!({"temperature": 0.0}),
16535            serde_json::json!({"temperature": 0.7}),
16536            serde_json::json!({"top_p": 1.0}),
16537            serde_json::json!({"top_k": 0}),
16538            serde_json::json!({"min_p": 0.05}),
16539            serde_json::json!({"repetition_penalty": 1.1}),
16540            serde_json::json!({"frequency_penalty": 0.5}),
16541            serde_json::json!({"presence_penalty": 1.5}),
16542            serde_json::json!({
16543                "temperature": 0.3, "top_p": 0.5, "top_k": 7, "min_p": 0.02,
16544                "frequency_penalty": 0.1, "presence_penalty": 0.2,
16545                "repetition_penalty": 1.05 }),
16546        ] {
16547            let c = comp(extra.clone());
16548            let h = chat(extra.clone());
16549            assert_eq!(
16550                (
16551                    c.temperature,
16552                    c.top_p,
16553                    c.top_k,
16554                    c.min_p,
16555                    c.penalty_repeat,
16556                    c.penalty_freq,
16557                    c.penalty_present,
16558                    c.penalty_last_n,
16559                    c.seed
16560                ),
16561                (
16562                    h.temperature,
16563                    h.top_p,
16564                    h.top_k,
16565                    h.min_p,
16566                    h.penalty_repeat,
16567                    h.penalty_freq,
16568                    h.penalty_present,
16569                    h.penalty_last_n,
16570                    h.seed
16571                ),
16572                "/v1/completions and /v1/chat/completions disagree on {extra} — \
16573                 standard-surface-law violation"
16574            );
16575        }
16576
16577        // and the vendor values really are what the omitting request lands on, on BOTH.
16578        let omitted = comp(serde_json::json!({}));
16579        assert_eq!(
16580            omitted.temperature, 1.0,
16581            "qwen3.8 card thinking temperature"
16582        );
16583        assert_eq!(omitted.top_p, 0.95, "qwen3.8 card top_p");
16584        assert_eq!(omitted.top_k, 20, "qwen3.8 card top_k");
16585        // explicit greedy survives on the raw-prompt surface too.
16586        assert!(
16587            memra_engine::sampler::Sampler::new(comp(serde_json::json!({"temperature": 0})))
16588                .is_greedy()
16589        );
16590    }
16591
16592    /// WORKER-TRUTH surface parity (hermes `d991b51699218285`): the SAME omitted-sampling
16593    /// request, sent through all four REAL handlers, must reach the worker with the SAME
16594    /// effective sampling. The builder-level test above proves the two request builders
16595    /// agree when handed one `SamplingDefaults`; this one proves the HANDLERS do —
16596    /// including each surface's own per-request `AppState::sampling_defaults` lookup and
16597    /// the /v1/messages + /v1/responses translations, which that test only covered "by
16598    /// construction". The pinned scenario is the finding's exact one: a model whose arch
16599    /// caps carry the Step-3.7 vendor recommendation (0.5/0.9) and a client that says
16600    /// nothing. Pre-resolver, /v1/completions never consulted ModelCaps and shipped
16601    /// temperature 1.0 against the 0.5/0.9 the chat path applied; a surface that stops
16602    /// consulting caps, resolves through a different body, or zero-fills an omitted field
16603    /// in translation diverges HERE and fails by name.
16604    #[tokio::test]
16605    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
16606    async fn same_omitted_request_resolves_identically_on_all_four_surfaces() {
16607        let _l = drain_lock();
16608        let step_caps = ModelCaps {
16609            chat_ok: true,
16610            chat_temperature_default: Some(0.5),
16611            chat_top_p_default: Some(0.9),
16612            ..Default::default()
16613        };
16614        let (cfg_tx, cfg_rx) = std::sync::mpsc::channel::<WorkerSaw>();
16615        let st = fake_worker_state_full(
16616            1,
16617            std::time::Duration::ZERO,
16618            HashMap::from([("m".to_string(), step_caps)]),
16619            Some(cfg_tx),
16620        );
16621        // Everything a distribution-side comparison can see, EXCEPT the seed: an omitted
16622        // seed is fresh entropy per request BY CONTRACT
16623        // (`omitted_seed_is_fresh_entropy_not_a_pinned_zero`), so surfaces must NOT agree
16624        // on it.
16625        let fields = |saw: &WorkerSaw| {
16626            let c = &saw.sampler_cfg;
16627            (
16628                c.temperature,
16629                c.top_p,
16630                c.top_k,
16631                c.min_p,
16632                c.penalty_repeat,
16633                c.penalty_freq,
16634                c.penalty_present,
16635                c.penalty_last_n,
16636            )
16637        };
16638        let worker_saw = |surface: &str| {
16639            cfg_rx
16640                .recv_timeout(std::time::Duration::from_secs(10))
16641                .unwrap_or_else(|_| panic!("{surface}: request never reached the worker"))
16642        };
16643
16644        let resp = completions(
16645            State(st.clone()),
16646            axum::http::HeaderMap::new(),
16647            None,
16648            Json(serde_json::from_value(serde_json::json!({"model": "m", "prompt": "t"})).unwrap()),
16649        )
16650        .await;
16651        assert_eq!(
16652            resp.status(),
16653            StatusCode::OK,
16654            "/v1/completions rejected the omitted-sampling request"
16655        );
16656        let comp = worker_saw("/v1/completions");
16657
16658        let resp = chat_completions(
16659            State(st.clone()),
16660            axum::http::HeaderMap::new(),
16661            None,
16662            Json(
16663                serde_json::from_value(serde_json::json!({
16664                    "model": "m", "messages": [{"role": "user", "content": "t"}]}))
16665                .unwrap(),
16666            ),
16667        )
16668        .await;
16669        assert_eq!(
16670            resp.status(),
16671            StatusCode::OK,
16672            "/v1/chat/completions rejected the omitted-sampling request"
16673        );
16674        let chat = worker_saw("/v1/chat/completions");
16675
16676        let resp = anthropic::messages(
16677            State(st.clone()),
16678            axum::http::HeaderMap::new(),
16679            None,
16680            axum::body::Bytes::from(
16681                serde_json::json!({
16682                    "model": "m", "max_tokens": 16,
16683                    "messages": [{"role": "user", "content": "t"}]})
16684                .to_string(),
16685            ),
16686        )
16687        .await;
16688        assert_eq!(
16689            resp.status(),
16690            StatusCode::OK,
16691            "/v1/messages rejected the omitted-sampling request"
16692        );
16693        let msg = worker_saw("/v1/messages");
16694
16695        let resp = responses_api::responses(
16696            State(st.clone()),
16697            axum::http::HeaderMap::new(),
16698            None,
16699            axum::body::Bytes::from(serde_json::json!({"model": "m", "input": "t"}).to_string()),
16700        )
16701        .await;
16702        assert_eq!(
16703            resp.status(),
16704            StatusCode::OK,
16705            "/v1/responses rejected the omitted-sampling request"
16706        );
16707        let rsp = worker_saw("/v1/responses");
16708
16709        for (surface, cfg) in [
16710            ("/v1/completions", &comp),
16711            ("/v1/messages", &msg),
16712            ("/v1/responses", &rsp),
16713        ] {
16714            assert_eq!(
16715                fields(cfg),
16716                fields(&chat),
16717                "{surface} resolved DIFFERENT effective sampling than /v1/chat/completions \
16718                 for the same omitted-sampling request — standard-surface-law violation \
16719                 (hermes d991b51699218285)"
16720            );
16721        }
16722        // ...and the value every surface lands on IS the Step vendor recommendation, not
16723        // the API-standard 1.0/1.0 the pre-resolver completions surface shipped.
16724        assert_eq!(
16725            (comp.sampler_cfg.temperature, comp.sampler_cfg.top_p),
16726            (0.5, 0.9),
16727            "an omitting client must get the model's vendor caps (Step-3.7: 0.5/0.9) on \
16728             EVERY surface, not the API-standard 1.0/1.0 (hermes d991b51699218285)"
16729        );
16730    }
16731
16732    /// WORKER-TRUTH effort parity (issue #31, standard-surface law): the SAME
16733    /// reasoning-effort value, expressed in each surface's own field —
16734    /// `reasoning_effort` on /v1/chat/completions, `reasoning.effort` on /v1/responses,
16735    /// `output_config.effort` on /v1/messages — must produce the SAME acceptance
16736    /// decision AND the same resolved (ThinkMode, effort_level) at the worker boundary.
16737    /// Before this lane /v1/messages accepted EVERY string (bogus/banana/"" -> 200) and
16738    /// silently ignored the parameter: `anthropic::translate` never read
16739    /// `output_config.effort`, so it was dropped before `parse_think` — a mutation that
16740    /// restores the drop fails every row of this test by name.
16741    #[tokio::test]
16742    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
16743    async fn same_effort_value_resolves_identically_on_every_surface() {
16744        let _l = drain_lock();
16745        // effort_levels caps so the level string is worker-visible too (step35 dialect);
16746        // ThinkMode alone would still catch the switch half on binary templates.
16747        let caps = ModelCaps {
16748            chat_ok: true,
16749            effort_levels: true,
16750            ..Default::default()
16751        };
16752        let (saw_tx, saw_rx) = std::sync::mpsc::channel::<WorkerSaw>();
16753        let st = fake_worker_state_full(
16754            1,
16755            std::time::Duration::ZERO,
16756            HashMap::from([("m".to_string(), caps)]),
16757            Some(saw_tx),
16758        );
16759        let send = |st: AppState, surface: &'static str, effort: &'static str| async move {
16760            match surface {
16761                "/v1/chat/completions" => {
16762                    chat_completions(
16763                        State(st),
16764                        axum::http::HeaderMap::new(),
16765                        None,
16766                        Json(
16767                            serde_json::from_value(serde_json::json!({
16768                                "model": "m", "max_tokens": 8,
16769                                "reasoning_effort": effort,
16770                                "messages": [{"role": "user", "content": "t"}]}))
16771                            .unwrap(),
16772                        ),
16773                    )
16774                    .await
16775                }
16776                "/v1/responses" => {
16777                    responses_api::responses(
16778                        State(st),
16779                        axum::http::HeaderMap::new(),
16780                        None,
16781                        axum::body::Bytes::from(
16782                            serde_json::json!({
16783                                "model": "m", "max_output_tokens": 8, "input": "t",
16784                                "reasoning": {"effort": effort}})
16785                            .to_string(),
16786                        ),
16787                    )
16788                    .await
16789                }
16790                "/v1/messages" => {
16791                    anthropic::messages(
16792                        State(st),
16793                        axum::http::HeaderMap::new(),
16794                        None,
16795                        axum::body::Bytes::from(
16796                            serde_json::json!({
16797                                "model": "m", "max_tokens": 8,
16798                                "messages": [{"role": "user", "content": "t"}],
16799                                "output_config": {"effort": effort}})
16800                            .to_string(),
16801                        ),
16802                    )
16803                    .await
16804                }
16805                other => panic!("unknown surface {other}"),
16806            }
16807        };
16808        const SURFACES: [&str; 3] = ["/v1/chat/completions", "/v1/responses", "/v1/messages"];
16809
16810        // Accepted rows: same 200, same worker-truth (ThinkMode, effort_level) on all
16811        // three surfaces. none/minimal REALLY suppress thinking on /v1/messages now.
16812        for (effort, want_think, want_level) in [
16813            ("none", ThinkMode::NoThink, Some("low")),
16814            ("minimal", ThinkMode::NoThink, Some("low")),
16815            ("low", ThinkMode::Think, Some("low")),
16816            ("medium", ThinkMode::Think, Some("medium")),
16817            ("high", ThinkMode::Think, Some("high")),
16818            // the issue's divergent row: xhigh was 400 on chat, 200 on the other two.
16819            ("xhigh", ThinkMode::Think, Some("high")),
16820        ] {
16821            for surface in SURFACES {
16822                let resp = send(st.clone(), surface, effort).await;
16823                assert_eq!(
16824                    resp.status(),
16825                    StatusCode::OK,
16826                    "{surface} rejected effort {effort:?} — the surfaces' allowlists \
16827                     diverged again (issue #31)"
16828                );
16829                let saw = saw_rx
16830                    .recv_timeout(std::time::Duration::from_secs(10))
16831                    .unwrap_or_else(|_| {
16832                        panic!("{surface}: effort {effort:?} request never reached the worker")
16833                    });
16834                assert_eq!(
16835                    (saw.think, saw.reasoning_effort.as_deref()),
16836                    (want_think, want_level),
16837                    "{surface} resolved effort {effort:?} to a DIFFERENT worker-truth \
16838                     reasoning surface — the parameter was dropped or remapped before \
16839                     parse_think (issue #31 regression)"
16840                );
16841            }
16842        }
16843
16844        // Rejected rows: the SAME 400 decision on all three surfaces — /v1/messages
16845        // accepting a value the other surfaces refuse is exactly issue #31.
16846        for effort in ["bogus", "banana", ""] {
16847            for surface in SURFACES {
16848                let resp = send(st.clone(), surface, effort).await;
16849                assert_eq!(
16850                    resp.status(),
16851                    StatusCode::BAD_REQUEST,
16852                    "{surface} accepted effort {effort:?} — silent-accept regression \
16853                     (issue #31: the value never reached parse_think's allowlist)"
16854                );
16855                // Each surface still speaks its own documented error envelope.
16856                let body = axum::body::to_bytes(resp.into_body(), 1 << 20)
16857                    .await
16858                    .unwrap();
16859                let v: serde_json::Value = serde_json::from_slice(&body)
16860                    .unwrap_or_else(|_| panic!("{surface}: non-JSON 400 body for {effort:?}"));
16861                match surface {
16862                    "/v1/messages" => {
16863                        assert_eq!(v["type"], "error", "{surface} error envelope");
16864                        assert_eq!(
16865                            v["error"]["type"], "invalid_request_error",
16866                            "{surface} error type"
16867                        );
16868                    }
16869                    _ => {
16870                        assert!(
16871                            v["error"]["message"].is_string(),
16872                            "{surface} OpenAI-shaped error body: {v}"
16873                        );
16874                    }
16875                }
16876            }
16877        }
16878
16879        // Anthropic precedence at the HTTP boundary: thinking.type wins the switch when
16880        // both levers are present (documented Anthropic semantics), and the effort is
16881        // still validated rather than silently dropped.
16882        let resp = anthropic::messages(
16883            State(st.clone()),
16884            axum::http::HeaderMap::new(),
16885            None,
16886            axum::body::Bytes::from(
16887                serde_json::json!({
16888                    "model": "m", "max_tokens": 8,
16889                    "messages": [{"role": "user", "content": "t"}],
16890                    "thinking": {"type": "enabled"},
16891                    "output_config": {"effort": "none"}})
16892                .to_string(),
16893            ),
16894        )
16895        .await;
16896        assert_eq!(resp.status(), StatusCode::OK);
16897        let saw = saw_rx
16898            .recv_timeout(std::time::Duration::from_secs(10))
16899            .expect("thinking+effort request never reached the worker");
16900        assert_eq!(
16901            saw.think,
16902            ThinkMode::Think,
16903            "thinking.type (the documented Anthropic lever) must win the switch over \
16904             output_config.effort"
16905        );
16906        let resp = anthropic::messages(
16907            State(st.clone()),
16908            axum::http::HeaderMap::new(),
16909            None,
16910            axum::body::Bytes::from(
16911                serde_json::json!({
16912                    "model": "m", "max_tokens": 8,
16913                    "messages": [{"role": "user", "content": "t"}],
16914                    "thinking": {"type": "enabled"},
16915                    "output_config": {"effort": "banana"}})
16916                .to_string(),
16917            ),
16918        )
16919        .await;
16920        assert_eq!(
16921            resp.status(),
16922            StatusCode::BAD_REQUEST,
16923            "an invalid effort must 400 even next to an explicit thinking.type — \
16924             precedence must not re-open the silent-accept hole"
16925        );
16926    }
16927
16928    #[test]
16929    fn vendor_sampling_defaults_are_boot_validated() {
16930        // Same posture as default_reasoning_effort: a typo'd default fails at metadata parse
16931        // (before GPU load), never as a per-request 400 storm after a watchdog restart.
16932        let parsed = OpenRouterMetadataFile::from_toml(
16933            r#"
16934[models.g]
16935default_temperature = 1.0
16936default_top_p = 0.95
16937default_top_k = 64
16938default_min_p = 0.0
16939default_presence_penalty = 0.0
16940default_frequency_penalty = 0.0
16941default_repetition_penalty = 1.0
16942"#,
16943        )
16944        .unwrap();
16945        let g = parsed.get("g").unwrap();
16946        assert_eq!(g.default_temperature, Some(1.0));
16947        assert_eq!(g.default_top_p, Some(0.95));
16948        assert_eq!(g.default_top_k, Some(64));
16949
16950        // A ZERO default temperature is refused ON PURPOSE: it would reinstate
16951        // greedy-by-default deployment-wide, silently, for every omitting client — exactly the
16952        // hazard this lane exists to remove. Greedy stays reachable per-request.
16953        let err = OpenRouterMetadataFile::from_toml(
16954            r#"
16955[models.g]
16956default_temperature = 0.0
16957"#,
16958        )
16959        .unwrap_err();
16960        assert!(err.contains("default_temperature"), "{err}");
16961        assert!(
16962            err.contains("greedy"),
16963            "the refusal must say WHY a zero default is refused: {err}"
16964        );
16965
16966        for bad in [
16967            "default_temperature = 2.5",
16968            "default_temperature = -1.0",
16969            "default_top_p = 0.0",
16970            "default_top_p = 1.5",
16971            "default_min_p = 1.0",
16972            "default_min_p = -0.1",
16973            "default_presence_penalty = 3.0",
16974            "default_frequency_penalty = -2.5",
16975            "default_repetition_penalty = 0.0",
16976        ] {
16977            let err =
16978                OpenRouterMetadataFile::from_toml(&format!("[models.g]\n{bad}\n")).unwrap_err();
16979            let key = bad.split(' ').next().unwrap();
16980            assert!(err.contains(key), "{bad} must be refused by name: {err}");
16981        }
16982
16983        // DEPLOY-ORDER TRAP (the same one default_reasoning_effort created):
16984        // `deny_unknown_fields` means an OLDER binary FAILS BOOT on a config carrying these
16985        // new keys. Binary first, then config — never the other way round.
16986        let err = OpenRouterMetadataFile::from_toml(
16987            r#"
16988[models.g]
16989default_temperture = 1.0
16990"#,
16991        )
16992        .unwrap_err();
16993        assert!(
16994            err.contains("unknown field"),
16995            "an unknown key must be fatal, which is what makes binary-first ordering \
16996             mandatory: {err}"
16997        );
16998    }
16999
17000    #[test]
17001    fn non_thinking_sampling_arm_is_boot_validated() {
17002        // Same posture as the flat keys: a typo'd arm fails at metadata parse, before GPU
17003        // load. The arm goes through the SAME range law (validate_sampling_arm), so the two
17004        // arms cannot drift apart in what they accept.
17005        let parsed = OpenRouterMetadataFile::from_toml(
17006            r#"
17007[models.q]
17008default_temperature = 1.0
17009default_top_p = 0.95
17010default_top_k = 20
17011
17012[models.q.non_thinking_sampling]
17013temperature = 0.7
17014top_p = 0.8
17015top_k = 20
17016presence_penalty = 1.5
17017"#,
17018        )
17019        .unwrap();
17020        let arm = parsed
17021            .get("q")
17022            .unwrap()
17023            .non_thinking_sampling
17024            .as_ref()
17025            .unwrap();
17026        assert_eq!(arm.temperature, Some(0.7));
17027        assert_eq!(arm.top_p, Some(0.8));
17028        assert_eq!(arm.top_k, Some(20));
17029        assert_eq!(arm.presence_penalty, Some(1.5));
17030        assert_eq!(
17031            arm.min_p, None,
17032            "undeclared arm fields stay undeclared, never invented"
17033        );
17034
17035        // A zero arm temperature is refused for the same reason as the flat key: it would be
17036        // greedy-by-default for every thinking-off omitting client. The refusal names the
17037        // exact nested key the operator wrote.
17038        let err = OpenRouterMetadataFile::from_toml(
17039            r#"
17040[models.q]
17041[models.q.non_thinking_sampling]
17042temperature = 0.0
17043"#,
17044        )
17045        .unwrap_err();
17046        assert!(err.contains("non_thinking_sampling.temperature"), "{err}");
17047        assert!(err.contains("greedy"), "{err}");
17048
17049        // A DECLARED-but-empty arm is refused: it would silently hand thinking-off traffic
17050        // the bare API-standard defaults while the file looks configured.
17051        let err = OpenRouterMetadataFile::from_toml(
17052            r#"
17053[models.q]
17054[models.q.non_thinking_sampling]
17055"#,
17056        )
17057        .unwrap_err();
17058        assert!(err.contains("non_thinking_sampling"), "{err}");
17059        assert!(err.contains("declare"), "{err}");
17060
17061        // Out-of-range arm values are named with their full nested key.
17062        for bad in [
17063            "temperature = 2.5",
17064            "top_p = 0.0",
17065            "top_p = 1.5",
17066            "min_p = 1.0",
17067            "presence_penalty = 3.0",
17068            "frequency_penalty = -2.5",
17069            "repetition_penalty = 0.0",
17070        ] {
17071            let err = OpenRouterMetadataFile::from_toml(&format!(
17072                "[models.q]\n[models.q.non_thinking_sampling]\n{bad}\n"
17073            ))
17074            .unwrap_err();
17075            let key = bad.split(' ').next().unwrap();
17076            assert!(
17077                err.contains(&format!("non_thinking_sampling.{key}")),
17078                "the refusal for {bad:?} must name the nested key: {err}"
17079            );
17080        }
17081
17082        // DEPLOY-ORDER TRAP, inherited on purpose: the arm table is deny_unknown_fields too,
17083        // and an OLDER binary fails boot on the whole `non_thinking_sampling` table itself —
17084        // binary first, then config, exactly like the flat keys.
17085        let err = OpenRouterMetadataFile::from_toml(
17086            r#"
17087[models.q]
17088[models.q.non_thinking_sampling]
17089temperture = 0.7
17090"#,
17091        )
17092        .unwrap_err();
17093        assert!(err.contains("unknown field"), "{err}");
17094    }
17095
17096    /// qwen/qwen3.8-27b's own model card publishes a SECOND sampling arm for
17097    /// thinking-disabled use (retrieved 2026-08-24): temperature 0.7, top_p 0.80,
17098    /// top_k 20, presence_penalty 1.5. min_p and the other penalties are not
17099    /// separately recommended for this arm.
17100    fn qwen38_non_thinking_defaults() -> SamplingDefaults {
17101        SamplingDefaults {
17102            temperature: Some(0.7),
17103            top_p: Some(0.8),
17104            top_k: Some(20),
17105            presence_penalty: Some(1.5),
17106            ..Default::default()
17107        }
17108    }
17109
17110    fn qwen38_two_arm_defaults() -> ModelSamplingDefaults {
17111        ModelSamplingDefaults {
17112            thinking: qwen38_vendor_defaults(),
17113            non_thinking: Some(qwen38_non_thinking_defaults()),
17114        }
17115    }
17116
17117    /// The served qwen3.8 template's caps shape: think tail on by default WITH the
17118    /// enable_thinking switch, so an explicit off-request is honorable (no 400 from the
17119    /// silent-ignore gate).
17120    fn qwen38_caps() -> ModelCaps {
17121        ModelCaps {
17122            chat_ok: true,
17123            qwen_think: true,
17124            think_switch: true,
17125            ..Default::default()
17126        }
17127    }
17128
17129    /// Field-tuple key for comparing two SamplerConfigs exactly (the struct itself is not
17130    /// PartialEq; the seed is pinned by the test bodies so it participates too).
17131    fn sampler_key(c: &SamplerConfig) -> (f32, f32, usize, f32, f32, f32, f32, usize, u64) {
17132        (
17133            c.temperature,
17134            c.top_p,
17135            c.top_k,
17136            c.min_p,
17137            c.penalty_present,
17138            c.penalty_freq,
17139            c.penalty_repeat,
17140            c.penalty_last_n,
17141            c.seed,
17142        )
17143    }
17144
17145    fn build_with_arms(
17146        defaults: &ModelSamplingDefaults,
17147        caps: &ModelCaps,
17148        default_effort: Option<&str>,
17149        extra: serde_json::Value,
17150    ) -> Request {
17151        let mut body = serde_json::json!({
17152            "model": "m",
17153            "messages": [{"role": "user", "content": "task"}],
17154            // pinned so two builds of the same body are comparable field-by-field.
17155            "seed": 3
17156        });
17157        body.as_object_mut()
17158            .unwrap()
17159            .extend(extra.as_object().unwrap().clone());
17160        let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
17161        let (tx, _rx) = worker::event_channel();
17162        build_chat_request_with_trace(
17163            req,
17164            Some(caps),
17165            tx,
17166            lanes::Lane::Interactive,
17167            None,
17168            None,
17169            default_effort,
17170            defaults,
17171        )
17172        .unwrap()
17173        .request
17174    }
17175
17176    #[test]
17177    fn resolved_thinking_mode_picks_the_vendor_sampling_arm() {
17178        // THE RESOLUTION MATRIX (owner ruling 2026-08-24): mode x set/unset x both model
17179        // shapes. Two models: qwen3.8 (vendor publishes TWO arms) and an ornith-shaped
17180        // single-arm model (Ornith-1.5 documents NO non-thinking arm) — the latter must be
17181        // unaffected by every row of the matrix.
17182        let two_arm = qwen38_two_arm_defaults();
17183        let single_arm = ModelSamplingDefaults::single(qwen38_vendor_defaults());
17184        let caps = qwen38_caps();
17185
17186        // Every live off-spelling resolves to NoThink and takes the NON-THINKING arm.
17187        let off_spellings = [
17188            serde_json::json!({"reasoning_effort": "none"}),
17189            serde_json::json!({"enable_thinking": false}),
17190            serde_json::json!({"chat_template_kwargs": {"enable_thinking": false}}),
17191            serde_json::json!({"reasoning": {"enabled": false}}),
17192        ];
17193        for extra in &off_spellings {
17194            let r = build_with_arms(&two_arm, &caps, None, extra.clone());
17195            assert_eq!(r.think, ThinkMode::NoThink, "{extra}");
17196            let c = &r.sampler_cfg;
17197            assert_eq!(c.temperature, 0.7, "{extra}: non-thinking card temperature");
17198            assert_eq!(c.top_p, 0.8, "{extra}: non-thinking card top_p");
17199            assert_eq!(c.top_k, 20, "{extra}: non-thinking card top_k");
17200            assert_eq!(
17201                c.penalty_present, 1.5,
17202                "{extra}: non-thinking presence_penalty"
17203            );
17204            assert_eq!(
17205                c.penalty_last_n,
17206                memra_engine::spec::PEN_WINDOW_MAX,
17207                "{extra}: the arm's presence penalty uses the cross-path history window"
17208            );
17209            assert_eq!(
17210                c.min_p, 0.0,
17211                "{extra}: the arm recommends no min_p — API standard, never the other arm's"
17212            );
17213
17214            // The SAME off-request on the single-arm model keeps the single arm — the arm
17215            // machinery must be invisible to a model that never declared a second arm.
17216            let s = build_with_arms(&single_arm, &caps, None, extra.clone());
17217            assert_eq!(s.think, ThinkMode::NoThink, "{extra}");
17218            assert_eq!(s.sampler_cfg.temperature, 1.0, "{extra}: single-arm model");
17219            assert_eq!(s.sampler_cfg.top_p, 0.95, "{extra}: single-arm model");
17220            assert_eq!(
17221                s.sampler_cfg.penalty_present, 0.0,
17222                "{extra}: single-arm model"
17223            );
17224        }
17225
17226        // Thinking ON — explicitly or by the template's own default — keeps the PRIMARY arm,
17227        // on both models.
17228        for extra in [
17229            serde_json::json!({}),
17230            serde_json::json!({"enable_thinking": true}),
17231            serde_json::json!({"reasoning_effort": "high"}),
17232            serde_json::json!({"reasoning": {"enabled": true}}),
17233        ] {
17234            for defaults in [&two_arm, &single_arm] {
17235                let c = build_with_arms(defaults, &caps, None, extra.clone()).sampler_cfg;
17236                assert_eq!(c.temperature, 1.0, "{extra}: thinking card temperature");
17237                assert_eq!(c.top_p, 0.95, "{extra}: thinking card top_p");
17238                assert_eq!(c.top_k, 20, "{extra}: thinking card top_k");
17239                assert_eq!(
17240                    c.penalty_present, 0.0,
17241                    "{extra}: thinking arm has no presence"
17242                );
17243            }
17244        }
17245
17246        // An operator `default_reasoning_effort = "none"` resolves the UNSET case to
17247        // NoThink upstream, so the unset case lands on the non-thinking arm...
17248        let c = build_with_arms(&two_arm, &caps, Some("none"), serde_json::json!({})).sampler_cfg;
17249        assert_eq!(
17250            c.temperature, 0.7,
17251            "deployment-default off = non-thinking arm"
17252        );
17253        // ...and an explicit client ON next to that deployment default wins it back.
17254        let c = build_with_arms(
17255            &two_arm,
17256            &caps,
17257            Some("none"),
17258            serde_json::json!({"enable_thinking": true}),
17259        )
17260        .sampler_cfg;
17261        assert_eq!(
17262            c.temperature, 1.0,
17263            "explicit ON beats the deployment default"
17264        );
17265
17266        // SET params are NEVER overridden, whichever arm applies; only unset fields take it.
17267        let c = build_with_arms(
17268            &two_arm,
17269            &caps,
17270            None,
17271            serde_json::json!({"enable_thinking": false, "temperature": 0.55}),
17272        )
17273        .sampler_cfg;
17274        assert_eq!(c.temperature, 0.55, "explicit temperature survives the arm");
17275        assert_eq!(c.top_p, 0.8, "unset top_p still takes the non-thinking arm");
17276        let c = build_with_arms(
17277            &two_arm,
17278            &caps,
17279            None,
17280            serde_json::json!({
17281                "reasoning_effort": "none", "top_p": 0.99, "presence_penalty": 0.0}),
17282        )
17283        .sampler_cfg;
17284        assert_eq!(c.top_p, 0.99, "explicit top_p wins");
17285        assert_eq!(
17286            c.penalty_present, 0.0,
17287            "an explicit presence_penalty 0.0 wins over the arm's 1.5 — a disabling value \
17288             is a value, not an absence"
17289        );
17290        assert_eq!(
17291            c.penalty_last_n, 0,
17292            "all penalties off => no history window"
17293        );
17294        assert_eq!(c.top_k, 20, "unset top_k still takes the arm");
17295
17296        // Explicit temperature 0 stays TRUE GREEDY under the non-thinking arm too — the one
17297        // invariant every determinism gate depends on bends for no arm.
17298        let c = build_with_arms(
17299            &two_arm,
17300            &caps,
17301            None,
17302            serde_json::json!({"enable_thinking": false, "temperature": 0}),
17303        )
17304        .sampler_cfg;
17305        assert!(
17306            memra_engine::sampler::Sampler::new(c).is_greedy(),
17307            "explicit temperature 0 must stay greedy on the non-thinking arm"
17308        );
17309
17310        // The same explicit-set matrix on the SINGLE-ARM model: identical to the two-arm
17311        // model's thinking rows, untouched by every off-request.
17312        let c = build_with_arms(
17313            &single_arm,
17314            &caps,
17315            None,
17316            serde_json::json!({"enable_thinking": false, "temperature": 0.55}),
17317        )
17318        .sampler_cfg;
17319        assert_eq!(c.temperature, 0.55);
17320        assert_eq!(
17321            c.top_p, 0.95,
17322            "single-arm model: unset top_p takes its one arm"
17323        );
17324    }
17325
17326    #[test]
17327    fn sampling_arms_never_blend_field_by_field() {
17328        // The two arms are separate vendor programs. A field the vendor left out of the
17329        // non-thinking arm falls to the API-STANDARD default — never to the thinking arm's
17330        // value and never to the arch cap — because a blended config would be numbers no
17331        // vendor ever published.
17332        let parsed = OpenRouterMetadataFile::from_toml(
17333            r#"
17334[models.m]
17335default_temperature = 1.0
17336default_min_p = 0.05
17337
17338[models.m.non_thinking_sampling]
17339temperature = 0.6
17340"#,
17341        )
17342        .unwrap();
17343        let caps = ModelCaps {
17344            chat_temperature_default: Some(0.5),
17345            chat_top_p_default: Some(0.9),
17346            ..Default::default()
17347        };
17348        let d = ModelSamplingDefaults::resolve(parsed.get("m"), Some(&caps));
17349        let client = ClientSampling {
17350            seed: Some(1),
17351            ..Default::default()
17352        };
17353
17354        let off = resolve_sampler_config(client, d.for_mode(ThinkMode::NoThink));
17355        assert_eq!(off.temperature, 0.6, "the arm's own field applies");
17356        assert_eq!(
17357            off.min_p, 0.0,
17358            "min_p undeclared on the arm = API standard, NOT the thinking arm's 0.05"
17359        );
17360        assert_eq!(
17361            off.top_p, 1.0,
17362            "top_p undeclared on the arm = API standard, NOT the arch cap's 0.9"
17363        );
17364
17365        // Default and Think keep the primary arm, caps fallback included.
17366        for mode in [ThinkMode::Default, ThinkMode::Think] {
17367            let on = resolve_sampler_config(client, d.for_mode(mode));
17368            assert_eq!(on.temperature, 1.0);
17369            assert_eq!(on.min_p, 0.05);
17370            assert_eq!(on.top_p, 0.9, "primary arm keeps the arch-cap fallback");
17371        }
17372    }
17373
17374    #[test]
17375    fn single_arm_models_and_thinking_on_requests_match_the_pre_arm_law_exactly() {
17376        // BYTE-IDENTITY PIN. Two populations must be exactly what they were before the arm
17377        // existed: (a) every request against a single-arm model (Ornith-1.5 documents NO
17378        // non-thinking arm), (b) thinking-on requests against the two-arm model. "Before"
17379        // is the one-resolver law verbatim — resolve_sampler_config(client, the one arm) —
17380        // so each build is compared against that expression computed directly. Sampling
17381        // resolution consumes no render input and produces none: chat_turns/tools/think/
17382        // effort are built from the request alone, so sampler equality here IS render
17383        // byte-identity (think/effort are additionally asserted per body).
17384        let caps = qwen38_caps();
17385        let single_arm = ModelSamplingDefaults::single(qwen38_vendor_defaults());
17386        let two_arm = qwen38_two_arm_defaults();
17387
17388        let bodies = [
17389            serde_json::json!({}),
17390            serde_json::json!({"enable_thinking": true}),
17391            serde_json::json!({"reasoning_effort": "high"}),
17392            serde_json::json!({"reasoning_effort": "none"}),
17393            serde_json::json!({"enable_thinking": false}),
17394            serde_json::json!({"chat_template_kwargs": {"enable_thinking": false}}),
17395            serde_json::json!({"temperature": 0.3, "top_p": 0.5}),
17396            serde_json::json!({"enable_thinking": false, "temperature": 0}),
17397        ];
17398        for extra in &bodies {
17399            // (a) the single-arm model: every mode, byte-equal to the pre-arm resolver.
17400            let r = build_with_arms(&single_arm, &caps, None, extra.clone());
17401            let mut client = ClientSampling {
17402                seed: Some(3),
17403                ..Default::default()
17404            };
17405            if let Some(t) = extra.get("temperature").and_then(|v| v.as_f64()) {
17406                client.temperature = Some(t as f32);
17407            }
17408            if let Some(p) = extra.get("top_p").and_then(|v| v.as_f64()) {
17409                client.top_p = Some(p as f32);
17410            }
17411            let pre_arm = resolve_sampler_config(client, &qwen38_vendor_defaults());
17412            assert_eq!(
17413                sampler_key(&r.sampler_cfg),
17414                sampler_key(&pre_arm),
17415                "{extra}: single-arm model diverged from the pre-arm resolution law"
17416            );
17417
17418            // (b) thinking-on / unset bodies: the TWO-arm model is byte-equal to the
17419            // single-arm build — think mode, effort string and sampler all included.
17420            if r.think != ThinkMode::NoThink {
17421                let t = build_with_arms(&two_arm, &caps, None, extra.clone());
17422                assert_eq!(t.think, r.think, "{extra}");
17423                assert_eq!(t.reasoning_effort, r.reasoning_effort, "{extra}");
17424                assert_eq!(
17425                    sampler_key(&t.sampler_cfg),
17426                    sampler_key(&r.sampler_cfg),
17427                    "{extra}: a thinking-on request must not feel the non-thinking arm"
17428                );
17429            }
17430        }
17431    }
17432
17433    #[test]
17434    fn constraint_forced_nothink_takes_the_non_thinking_arm() {
17435        // response_format on a switch-carrying think template forces the think switch off
17436        // (the grammar x think law above build_chat_request_with_trace). The model then
17437        // GENUINELY runs non-thinking, so the vendor's non-thinking arm is the honest
17438        // default for the sampling fields such a request left unset — the arm is selected
17439        // AFTER the constraint gate settles the mode, and this pins that ordering.
17440        let r = build_with_arms(
17441            &qwen38_two_arm_defaults(),
17442            &qwen38_caps(),
17443            None,
17444            serde_json::json!({"response_format": {"type": "json_object"}}),
17445        );
17446        assert_eq!(
17447            r.think,
17448            ThinkMode::NoThink,
17449            "constraint forces the switch off"
17450        );
17451        assert_eq!(
17452            r.sampler_cfg.temperature, 0.7,
17453            "and the arm follows the real mode"
17454        );
17455        assert_eq!(r.sampler_cfg.penalty_present, 1.5);
17456    }
17457
17458    #[test]
17459    fn metadata_sampling_defaults_outrank_arch_caps_but_never_the_client() {
17460        // Two default sources exist: the operator's per-model metadata block and the engine's
17461        // arch-keyed caps (step35 = StepFun's published 0.5/0.9). The operator's declaration is
17462        // about the artifact actually loaded on THIS box, so it wins; the cap remains the
17463        // fallback so a metadata-less box behaves exactly as it did before this lane.
17464        let caps = ModelCaps {
17465            chat_temperature_default: Some(0.5),
17466            chat_top_p_default: Some(0.9),
17467            chat_ok: true,
17468            ..Default::default()
17469        };
17470        let metadata = OpenRouterModelMetadata {
17471            default_temperature: Some(1.0),
17472            default_top_p: Some(0.95),
17473            default_top_k: Some(64),
17474            ..Default::default()
17475        };
17476
17477        let caps_only = SamplingDefaults::resolve(None, Some(&caps));
17478        assert_eq!(caps_only.temperature, Some(0.5), "arch cap is the fallback");
17479        assert_eq!(caps_only.top_p, Some(0.9));
17480        assert_eq!(caps_only.top_k, None, "caps declare no top_k");
17481
17482        let both = SamplingDefaults::resolve(Some(&metadata), Some(&caps));
17483        assert_eq!(
17484            both.temperature,
17485            Some(1.0),
17486            "metadata outranks the arch cap"
17487        );
17488        assert_eq!(both.top_p, Some(0.95));
17489        assert_eq!(both.top_k, Some(64));
17490
17491        // Partial metadata falls through to the cap field by field, not wholesale.
17492        let partial = SamplingDefaults::resolve(
17493            Some(&OpenRouterModelMetadata {
17494                default_temperature: Some(0.7),
17495                ..Default::default()
17496            }),
17497            Some(&caps),
17498        );
17499        assert_eq!(partial.temperature, Some(0.7));
17500        assert_eq!(
17501            partial.top_p,
17502            Some(0.9),
17503            "an undeclared metadata field must fall through to the cap, not to 1.0"
17504        );
17505
17506        // No metadata AND no caps = the pre-lane API-standard path, byte-for-byte.
17507        assert_eq!(
17508            SamplingDefaults::resolve(None, None),
17509            SamplingDefaults::default()
17510        );
17511    }
17512
17513    #[test]
17514    fn vendor_defaults_leave_the_pure_temp_sampled_spec_regime() {
17515        // COST OF THE CHANGE, pinned so it is never a surprise (lane/vendor-default-sampling,
17516        // 2026-08-19). Both served models' vendor recommendations carry TRUNCATION FILTERS
17517        // (qwen3.8: top_p 0.95 + top_k 20; gemma-4: top_p 0.95 + top_k 64), and the in-graph
17518        // sampled draft chain samples from the RAW softmax — it can hold no per-row filter
17519        // stats, so spec.rs engages `graph_s` only in the pure-temp regime and otherwise falls
17520        // back to the EAGER draft chain (memra-sampling `is_spec_sampling`, spec.rs `pure_temp`).
17521        //
17522        // Nothing about exactness changes: filters are applied symmetrically to draft q and
17523        // target p under the rejection verify, so these requests stay spec-ELIGIBLE and
17524        // distribution-exact. What changes is which draft chain runs — and it changes for the
17525        // DEFAULT request shape, i.e. the one most customers send. That trade is the owner's
17526        // call, not this test's; the test exists so the flip is measured, not discovered.
17527        let resolved = |d: &SamplingDefaults| {
17528            resolve_sampler_config(
17529                ClientSampling {
17530                    seed: Some(1),
17531                    ..Default::default()
17532                },
17533                d,
17534            )
17535        };
17536
17537        // Pre-lane default shape (no per-model key declared): pure temp, in-graph draft.
17538        assert!(
17539            memra_engine::sampler::Sampler::new(resolved(&SamplingDefaults::default()))
17540                .is_spec_sampling(),
17541            "the API-standard default must stay in the fast pure-temp regime"
17542        );
17543
17544        for (name, d) in [
17545            ("qwen/qwen3.8-27b", qwen38_vendor_defaults()),
17546            ("google/gemma-4-31b-it", gemma4_vendor_defaults()),
17547        ] {
17548            let sampler = memra_engine::sampler::Sampler::new(resolved(&d));
17549            assert!(
17550                !sampler.is_greedy(),
17551                "{name}: vendor default must not be greedy"
17552            );
17553            assert!(
17554                !sampler.is_spec_sampling(),
17555                "{name}: vendor top_p/top_k DO leave the pure-temp regime — if this ever \
17556                 starts passing, either the vendor numbers changed or the in-graph draft \
17557                 learned filters, and the perf note in docs/SERVING.md needs revisiting"
17558            );
17559        }
17560
17561        // A client that wants the fast regime back can still ask for it explicitly.
17562        let opted_out = resolve_sampler_config(
17563            ClientSampling {
17564                top_p: Some(1.0),
17565                top_k: Some(0),
17566                seed: Some(1),
17567                ..Default::default()
17568            },
17569            &qwen38_vendor_defaults(),
17570        );
17571        assert!(
17572            memra_engine::sampler::Sampler::new(opted_out).is_spec_sampling(),
17573            "explicitly disabling the filters must restore the pure-temp regime"
17574        );
17575    }
17576
17577    #[test]
17578    fn omitted_seed_is_fresh_entropy_not_a_pinned_zero() {
17579        // dogfood F4, SECOND HALF — found only by driving the live server. Fixing the
17580        // temperature default is NOT sufficient: `#[serde(default)] seed: u64` gave 0, a
17581        // perfectly valid FIXED seed, so a temp-1.0 request with seed omitted still replayed
17582        // one single sampled stream. Measured on the pre-fix binary: 4/4 byte-identical
17583        // completions at temperature 1.0 with seed omitted (receipts in
17584        // research/sampledspec-20260804/). The loop survives the temperature fix alone.
17585        let comp_seed = |body: serde_json::Value| {
17586            let req: CompletionReq = serde_json::from_value(body).unwrap();
17587            let (tx, _rx) = worker::event_channel();
17588            build_request(&req, tx, lanes::Lane::Interactive, None)
17589                .sampler_cfg
17590                .seed
17591        };
17592        let chat_seed = |body: serde_json::Value| {
17593            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
17594            let (tx, _rx) = worker::event_channel();
17595            build_chat_request(req, None, tx, lanes::Lane::Interactive, None)
17596                .unwrap()
17597                .request
17598                .sampler_cfg
17599                .seed
17600        };
17601
17602        // OMITTED seed: successive requests must NOT share a seed (that was the loop), and
17603        // must not be the old pinned 0.
17604        let a = comp_seed(serde_json::json!({"model": "m", "prompt": "t"}));
17605        let b = comp_seed(serde_json::json!({"model": "m", "prompt": "t"}));
17606        let c = chat_seed(serde_json::json!({
17607            "model": "m", "messages": [{"role": "user", "content": "t"}]}));
17608        assert_ne!(
17609            a, 0,
17610            "omitted seed must not be the pinned 0 that caused the loop"
17611        );
17612        assert_ne!(b, 0);
17613        assert_ne!(c, 0);
17614        assert_ne!(
17615            a, b,
17616            "two seed-omitting requests must get DIFFERENT streams"
17617        );
17618        assert_ne!(a, c);
17619
17620        // EXPLICIT seed is honored exactly — including an explicit 0, which every
17621        // determinism gate in tools/ and research/ relies on.
17622        assert_eq!(
17623            comp_seed(serde_json::json!({
17624            "model": "m", "prompt": "t", "seed": 0})),
17625            0,
17626            "explicit seed 0 must stay 0 — the determinism gates depend on it"
17627        );
17628        assert_eq!(
17629            comp_seed(serde_json::json!({
17630            "model": "m", "prompt": "t", "seed": 12345})),
17631            12345
17632        );
17633        assert_eq!(
17634            chat_seed(serde_json::json!({
17635            "model": "m", "messages": [{"role": "user", "content": "t"}],
17636            "seed": 777})),
17637            777
17638        );
17639        // explicit seed is reproducible across calls (the gate contract).
17640        assert_eq!(
17641            comp_seed(serde_json::json!({"model": "m", "prompt": "t", "seed": 42})),
17642            comp_seed(serde_json::json!({"model": "m", "prompt": "t", "seed": 42}))
17643        );
17644
17645        // fresh_seed itself: never 0, and distinct across rapid successive calls (the
17646        // same-nanosecond batched-arrival case the counter mix exists for).
17647        let seeds: std::collections::HashSet<u64> = (0..256).map(|_| fresh_seed()).collect();
17648        assert_eq!(
17649            seeds.len(),
17650            256,
17651            "fresh_seed must not collide across rapid calls"
17652        );
17653        assert!(!seeds.contains(&0));
17654    }
17655
17656    #[test]
17657    fn response_format_builds_grammar_only_when_present() {
17658        // NO-OP CONTRACT (lane/constrained): absent / {"type":"text"} => grammar None —
17659        // the worker Request is field-identical to a pre-lane request, no llguidance
17660        // object is ever built. json_object / json_schema arm the grammar.
17661        let mk = |rf: Option<serde_json::Value>| {
17662            let mut body = serde_json::json!({
17663                "model": "m", "messages": [{"role": "user", "content": "t"}]});
17664            if let Some(rf) = rf {
17665                body["response_format"] = rf;
17666            }
17667            let req: ChatCompletionReq = serde_json::from_value(body).unwrap();
17668            let (tx, _rx) = worker::event_channel();
17669            build_chat_request(req, None, tx, lanes::Lane::Interactive, None)
17670        };
17671        assert!(mk(None).unwrap().request.grammar.is_none());
17672        assert!(
17673            mk(Some(serde_json::json!({"type": "text"})))
17674                .unwrap()
17675                .request
17676                .grammar
17677                .is_none()
17678        );
17679        assert!(matches!(
17680            mk(Some(serde_json::json!({"type": "json_object"})))
17681                .unwrap()
17682                .request
17683                .grammar,
17684            Some(constrained::GrammarSpec::JsonObject)
17685        ));
17686        assert!(matches!(
17687            mk(Some(serde_json::json!({"type": "json_schema",
17688            "json_schema": {"schema": {"type": "object"}}})))
17689            .unwrap()
17690            .request
17691            .grammar,
17692            Some(constrained::GrammarSpec::JsonSchema(_))
17693        ));
17694        // unknown type: loud error, never silent.
17695        assert!(mk(Some(serde_json::json!({"type": "yaml"}))).is_err());
17696    }
17697
17698    /// GRAMMAR x THINK admit/refuse table (lane/step37-postthink-grammar, 2026-08-30).
17699    /// Three template classes, three verdicts:
17700    ///   switch-carrying (qwen): think forced OFF, grammar from token 1 — byte-identical
17701    ///     to the pre-lane path;
17702    ///   think-forced WITH a derivable close contract (step37): ADMITTED, think stays ON
17703    ///     (post-think two-phase — the worker arms the gate from the same load-time
17704    ///     contract);
17705    ///   think-forced with NO derivable close contract: the loud 400 stays — never a
17706    ///     silent constrain-from-token-1 stream.
17707    #[test]
17708    fn response_format_think_table_switch_postthink_refusal() {
17709        let mk = |caps: &ModelCaps| {
17710            let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
17711                "model": "m", "messages": [{"role": "user", "content": "t"}],
17712                "response_format": {"type": "json_object"}}))
17713            .unwrap();
17714            let (tx, _rx) = worker::event_channel();
17715            build_chat_request(req, Some(caps), tx, lanes::Lane::Interactive, None)
17716        };
17717        // qwen class: enable_thinking switch — grammar path forces NoThink, unchanged.
17718        let switch = ModelCaps {
17719            chat_ok: true,
17720            qwen_think: true,
17721            think_switch: true,
17722            ..Default::default()
17723        };
17724        let plan = mk(&switch).unwrap();
17725        assert_eq!(
17726            plan.request.think,
17727            memra_tokenizer::chat::ThinkMode::NoThink,
17728            "switch-carrying template must keep the grammar-from-token-1 path"
17729        );
17730        assert!(plan.request.grammar.is_some());
17731
17732        // step37 class: think-forced, close contract derivable — admitted, think ON.
17733        let postthink = ModelCaps {
17734            chat_ok: true,
17735            qwen_think: true,
17736            think_switch: false,
17737            think_close: vec![128799],
17738            ..Default::default()
17739        };
17740        let plan = mk(&postthink).unwrap();
17741        assert_ne!(
17742            plan.request.think,
17743            memra_tokenizer::chat::ThinkMode::NoThink,
17744            "post-think constrained request must keep the think channel ON"
17745        );
17746        assert!(plan.request.grammar.is_some());
17747
17748        // think-forced, NO contract: the loud refusal stays.
17749        let no_contract = ModelCaps {
17750            chat_ok: true,
17751            qwen_think: true,
17752            think_switch: false,
17753            think_close: Vec::new(),
17754            ..Default::default()
17755        };
17756        let err = match mk(&no_contract) {
17757            Err(err) => err,
17758            Ok(_) => panic!("think-forced template with no close contract must refuse"),
17759        };
17760        assert!(
17761            err.contains("think-close"),
17762            "refusal must name the missing close contract: {err}"
17763        );
17764    }
17765
17766    #[test]
17767    fn unsupported_semantic_params_are_named_rejections() {
17768        // gap-scan F4: fields serde used to swallow now deserialize into rejection slots.
17769        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
17770            "model": "m", "messages": [{"role": "user", "content": "t"}],
17771            "response_format": {"type": "json_object"}
17772        }))
17773        .unwrap();
17774        assert!(req.response_format.is_some());
17775        let req: ChatCompletionReq = serde_json::from_value(serde_json::json!({
17776            "model": "m", "messages": [{"role": "user", "content": "t"}],
17777            "response_format": {"type": "text"}, "logprobs": false, "n": 1,
17778            "user": "u-1", "stream_options": {"include_usage": true}
17779        }))
17780        .unwrap();
17781        // the no-op forms + cosmetic fields: all fine (accept-and-ignore class).
17782        assert_eq!(req.response_format.as_ref().unwrap()["type"], "text");
17783        assert_eq!(req.logprobs.as_ref().unwrap().as_bool(), Some(false));
17784        assert_eq!(req.n, Some(1));
17785        // the gate law itself: present -> named error, absent -> Ok.
17786        assert!(reject_unsupported(&[("logit_bias", false, "")]).is_ok());
17787        let (msg, param) = reject_unsupported(&[("logit_bias", true, " (why)")]).unwrap_err();
17788        assert_eq!(param, "logit_bias");
17789        assert_eq!(msg, "logit_bias is not supported (why)");
17790    }
17791
17792    #[test]
17793    fn completions_accept_openai_stop_forms() {
17794        for (value, expected) in [
17795            (serde_json::json!("Problem:"), vec!["Problem:"]),
17796            (
17797                serde_json::json!(["Question:", "Problem:"]),
17798                vec!["Question:", "Problem:"],
17799            ),
17800            (serde_json::Value::Null, Vec::<&str>::new()),
17801        ] {
17802            let req: CompletionReq = serde_json::from_value(serde_json::json!({
17803                "model": "plain_quant", "prompt": "task", "stop": value
17804            }))
17805            .unwrap();
17806            assert_eq!(req.stop.into_vec(), expected);
17807        }
17808    }
17809
17810    /// Fake GPU worker: consumes Generate commands and answers each with one Token +
17811    /// Done — handler-level tests (headers, drain) without a GPU or a loaded model.
17812    ///
17813    /// It also drives the SAME health handle the real worker does (mark_ready at "load"
17814    /// completion, beat_busy per iteration), which is what lets the /health and /readyz tests
17815    /// exercise the real handlers instead of a mock.
17816    fn fake_worker_state() -> AppState {
17817        fake_worker_state_with_steps(1, std::time::Duration::ZERO)
17818    }
17819
17820    fn fake_worker_state_with_steps(steps: usize, step_delay: std::time::Duration) -> AppState {
17821        fake_worker_state_full(steps, step_delay, HashMap::new(), None)
17822    }
17823
17824    /// What the fake worker SAW for one admitted request — the worker-truth fields the
17825    /// surface-parity tests compare: the resolved sampling AND the resolved reasoning
17826    /// surface (issue #31: /v1/messages dropped `output_config.effort` before this point,
17827    /// so only a worker-boundary tap can prove the effect half of effort parity).
17828    struct WorkerSaw {
17829        sampler_cfg: SamplerConfig,
17830        think: ThinkMode,
17831        reasoning_effort: Option<String>,
17832    }
17833
17834    /// Fake worker with per-model `caps` and a WORKER-TRUTH tap: each admitted request's
17835    /// resolved `WorkerSaw` snapshot is sent on `saw_tx` the moment the worker receives
17836    /// it — i.e. what the engine would actually run with, after every
17837    /// surface/translation/default layer has run. Surface-parity tests read this instead
17838    /// of a build helper so a divergence ANYWHERE in a handler path (not just in the
17839    /// shared resolver) fails the test.
17840    fn fake_worker_state_full(
17841        steps: usize,
17842        step_delay: std::time::Duration,
17843        caps: HashMap<String, ModelCaps>,
17844        saw_tx: Option<std::sync::mpsc::Sender<WorkerSaw>>,
17845    ) -> AppState {
17846        let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<Cmd>();
17847        let health = health::WorkerHealth::new();
17848        let h = health.clone();
17849        std::thread::spawn(move || {
17850            h.mark_ready();
17851            while let Ok(Cmd::Generate(mut req)) = cmd_rx.recv() {
17852                if let Some(tx) = &saw_tx {
17853                    let _ = tx.send(WorkerSaw {
17854                        sampler_cfg: req.sampler_cfg.clone(),
17855                        think: req.think,
17856                        reasoning_effort: req.reasoning_effort.clone(),
17857                    });
17858                }
17859                // Mirror handle_cmd: handlers reserve both the burst-yield gauge and the hard
17860                // queue bound before send. A fake worker must release both at its admission
17861                // boundary or leak process-global state into unrelated tests.
17862                worker::release_pending_admit();
17863                worker::release_admission_reservation(req.lane);
17864                h.beat_busy();
17865                if let Some(ready) = req.constraint_ready.take() {
17866                    let _ = ready.send(Ok(()));
17867                }
17868                let _ = req.tx.send(Event::PromptUsage {
17869                    n_prompt: 1,
17870                    n_cached: 0,
17871                });
17872                // Capture requests (embeddings/rerank) read the prompt's last position: the
17873                // real worker answers PromptCapture before Done, and the route 500s without
17874                // it. A fixed two-wide hidden state and a yes>no logit pair are enough for
17875                // the handler-level tests (unit-norm pooling, top-index ordering).
17876                if let Some(spec) = req.capture.as_ref() {
17877                    let _ = req.tx.send(Event::PromptCapture {
17878                        hidden: spec.hidden.then(|| vec![1.0, 0.0]),
17879                        logits: if spec.logit_pieces.is_empty() {
17880                            Vec::new()
17881                        } else {
17882                            vec![2.0, 0.0]
17883                        },
17884                    });
17885                }
17886                for step in 0..steps {
17887                    h.beat_busy();
17888                    let text = if steps == 1 { "ok" } else { "x" };
17889                    let _ = req.tx.send(Event::Token {
17890                        id: step as u32 + 1,
17891                        text: text.into(),
17892                    });
17893                    if !step_delay.is_zero() {
17894                        std::thread::sleep(step_delay);
17895                    }
17896                }
17897                let _ = req.tx.send(Event::Done {
17898                    stop_reason: "Eos".into(),
17899                    n_tokens: steps,
17900                    n_prompt: 1,
17901                    n_cached: 0,
17902                    elapsed_s: 0.01,
17903                    spec: None,
17904                });
17905                h.set_phase(health::PHASE_IDLE);
17906            }
17907        });
17908        // The spawn above is the "load"; wait for its ready stamp so a health assertion is not
17909        // racing the thread start (the real path blocks on ready_tx for the same reason).
17910        for _ in 0..2000 {
17911            if health.live().is_ok() {
17912                break;
17913            }
17914            std::thread::sleep(std::time::Duration::from_millis(1));
17915        }
17916        AppState {
17917            cmd_tx,
17918            models: Arc::new(vec!["m".into()]),
17919            caps: Arc::new(caps),
17920            openrouter_metadata: Arc::new(HashMap::new()),
17921            provider_metadata: Arc::new(None),
17922            metering: None,
17923
17924            budget_tokenizers: None,
17925            api_auth: ApiAuth::default(),
17926            metrics_auth: MetricsAuth::default(),
17927            metrics: SharedMetrics::default(),
17928            inflight: Arc::new(Default::default()),
17929            tenant_inflight: Arc::new(Default::default()),
17930            health,
17931            bg: None,
17932        }
17933    }
17934
17935    #[tokio::test]
17936    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
17937    async fn deep_schema_fails_while_normal_decode_keeps_stepping() {
17938        let _l = drain_lock();
17939        let st = fake_worker_state_with_steps(64, std::time::Duration::from_millis(5));
17940        let normal_state = st.clone();
17941        let normal = tokio::spawn(async move {
17942            chat_completions(
17943                State(normal_state),
17944                axum::http::HeaderMap::new(),
17945                None,
17946                Json(
17947                    serde_json::from_value(serde_json::json!({
17948                        "model": "m",
17949                        "messages": [{"role": "user", "content": "keep decoding"}],
17950                    }))
17951                    .unwrap(),
17952                ),
17953            )
17954            .await
17955        });
17956        tokio::time::sleep(std::time::Duration::from_millis(15)).await;
17957
17958        let mut deep = serde_json::json!({"type": "string"});
17959        for _ in 0..(constrained::MAX_SCHEMA_DEPTH / 2 + 1) {
17960            deep = serde_json::json!({"allOf": [deep]});
17961        }
17962        let bad = chat_completions(
17963            State(st.clone()),
17964            axum::http::HeaderMap::new(),
17965            None,
17966            Json(
17967                serde_json::from_value(serde_json::json!({
17968                    "model": "m",
17969                    "messages": [{"role": "user", "content": "bad schema"}],
17970                    "response_format": {
17971                        "type": "json_schema",
17972                        "json_schema": {"schema": deep},
17973                    },
17974                }))
17975                .unwrap(),
17976            ),
17977        )
17978        .await;
17979        assert_eq!(bad.status(), StatusCode::BAD_REQUEST);
17980        assert_eq!(bad.headers().get("x-should-retry").unwrap(), "false");
17981        let bytes = axum::body::to_bytes(bad.into_body(), usize::MAX)
17982            .await
17983            .unwrap();
17984        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
17985        assert!(
17986            payload["error"]["message"]
17987                .as_str()
17988                .unwrap()
17989                .contains("maximum nesting depth")
17990        );
17991        assert!(
17992            !normal.is_finished(),
17993            "bad schema stalled or replaced the normal decode"
17994        );
17995
17996        let normal_response = normal.await.unwrap();
17997        assert_eq!(normal_response.status(), StatusCode::OK);
17998        let snapshot = st.health.snapshot();
17999        assert!(
18000            st.health.live().is_ok(),
18001            "normal decode left health stalled"
18002        );
18003        assert!(snapshot.beat_age_ms < snapshot.stall_threshold_ms);
18004    }
18005
18006    #[tokio::test]
18007    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
18008    async fn valid_response_format_preflight_preserves_generation() {
18009        let _l = drain_lock();
18010        let response = chat_completions(
18011            State(fake_worker_state()),
18012            axum::http::HeaderMap::new(),
18013            None,
18014            Json(
18015                serde_json::from_value(serde_json::json!({
18016                    "model": "m",
18017                    "messages": [{"role": "user", "content": "valid schema"}],
18018                    "response_format": {"type": "json_object"},
18019                }))
18020                .unwrap(),
18021            ),
18022        )
18023        .await;
18024        assert_eq!(response.status(), StatusCode::OK);
18025        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
18026            .await
18027            .unwrap();
18028        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
18029        assert_eq!(payload["choices"][0]["message"]["content"], "ok");
18030    }
18031
18032    #[tokio::test]
18033    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
18034    async fn unknown_model_refuses_model_not_found_before_admission() {
18035        let _l = drain_lock();
18036        // The fake worker answers ANY admitted request with "ok", so a model_not_found
18037        // response proves the handler refused BEFORE worker admission — and a fortiori
18038        // before prepaid budget reservation, which sits between (the live bug: a typo'd
18039        // model id on a budgeted tenant surfaced as a 503 about budget accounting).
18040        let response = chat_completions(
18041            State(fake_worker_state()),
18042            axum::http::HeaderMap::new(),
18043            None,
18044            Json(
18045                serde_json::from_value(serde_json::json!({
18046                    "model": "qwen/qwen3.8-27b-typo",
18047                    "messages": [{"role": "user", "content": "hi"}],
18048                }))
18049                .unwrap(),
18050            ),
18051        )
18052        .await;
18053        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
18054        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
18055            .await
18056            .unwrap();
18057        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
18058        assert_eq!(payload["error"]["code"], "model_not_found");
18059        assert_eq!(payload["error"]["type"], "invalid_request_error");
18060
18061        // Same law on the text-completions surface.
18062        let response = completions(
18063            State(fake_worker_state()),
18064            axum::http::HeaderMap::new(),
18065            None,
18066            Json(
18067                serde_json::from_value(serde_json::json!({
18068                    "model": "nope",
18069                    "prompt": "hi",
18070                }))
18071                .unwrap(),
18072            ),
18073        )
18074        .await;
18075        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
18076        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
18077            .await
18078            .unwrap();
18079        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
18080        assert_eq!(payload["error"]["code"], "model_not_found");
18081    }
18082
18083    const METRICS_KEY_ACME: &str = "completion-acme-secret";
18084    const METRICS_KEY_BLUE: &str = "completion-blue-secret";
18085
18086    fn multi_key_metrics_state(metrics_token: Option<&str>) -> AppState {
18087        let spec = format!(
18088            "acme:{},blue:{}",
18089            auth::sha256_hex(METRICS_KEY_ACME),
18090            auth::sha256_hex(METRICS_KEY_BLUE),
18091        );
18092        let keyring = Box::leak(Box::new(auth::KeyStore::from_spec(&spec).unwrap()));
18093        let mut st = fake_worker_state();
18094        st.api_auth.keyring = Some(keyring);
18095        st.metrics_auth = MetricsAuth::new(
18096            true,
18097            st.api_auth.configured(),
18098            metrics_token.map(str::to_string),
18099        );
18100        {
18101            let mut metrics = st.metrics.lock().unwrap();
18102            metrics.admitted = 17;
18103            metrics.prompt_tokens_in = 400;
18104            metrics.cached_tokens_in = 60;
18105            metrics.prefix_hits = 2;
18106            metrics.prefix_misses = 3;
18107            metrics.prefix_inserts = 5;
18108            metrics.prefix_evictions = 7;
18109            metrics.prefix_skips_budget = 9;
18110            metrics.prefix_skips_pinned = 10;
18111            metrics.prefix_hit_tokens = 11;
18112            metrics.lcp_hist[4] = 13;
18113            metrics.ns_tokens.insert("t:acme".into(), [100, 40]);
18114            metrics.ns_tokens.insert("t:blue".into(), [300, 20]);
18115            metrics.adsd_suspect_total.insert("t:acme".into(), 1);
18116            metrics.adsd_suspect_total.insert("t:blue".into(), 2);
18117            metrics.prefix_entries = 29;
18118            metrics.prefix_bytes = 31;
18119            metrics.active_sessions = 3;
18120            metrics.queued_requests = 5;
18121            metrics.admission_inflight.insert("m".into(), 4);
18122            metrics
18123                .admission_booked_bytes
18124                .insert("m".into(), 41_000_000);
18125            metrics.continuation_pool_entries = 7;
18126            metrics.spec_pool_entries = 11;
18127            metrics.cuda_driver_free_bytes = 13;
18128            metrics.cuda_pool_reserved_bytes = 17;
18129            metrics.cuda_pool_used_bytes = 19;
18130            metrics.cuda_pool_cached_bytes = 23;
18131            metrics.batch_size_last = 37;
18132            metrics.spec.insert(
18133                "m".into(),
18134                memra_engine::spec::SpecTelemetry {
18135                    rounds: 2,
18136                    drafted: 6,
18137                    accepted: 4,
18138                    ..Default::default()
18139                },
18140            );
18141            let mut spec_window = memra_engine::spec::SpecTelemetry {
18142                rounds: 4,
18143                drafted: 12,
18144                accepted: 6,
18145                ..Default::default()
18146            };
18147            spec_window.pos_drafted[..3].copy_from_slice(&[4, 4, 4]);
18148            spec_window.pos_accepted[..3].copy_from_slice(&[3, 2, 1]);
18149            metrics.spec_window.insert("m".into(), spec_window);
18150            metrics.constraint_compiler_fail_closed.insert(
18151                "m".into(),
18152                Arc::new(std::sync::atomic::AtomicBool::new(true)),
18153            );
18154        }
18155        st
18156    }
18157
18158    async fn metrics_json(st: AppState, bearer: &str) -> serde_json::Value {
18159        let mut headers = HeaderMap::new();
18160        headers.insert("authorization", format!("Bearer {bearer}").parse().unwrap());
18161        let response = get_metrics(State(st), headers).await;
18162        assert_eq!(response.status(), StatusCode::OK);
18163        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
18164            .await
18165            .unwrap();
18166        serde_json::from_slice(&bytes).unwrap()
18167    }
18168
18169    async fn yield_metrics_json(st: AppState, bearer: &str) -> serde_json::Value {
18170        let mut headers = HeaderMap::new();
18171        headers.insert("authorization", format!("Bearer {bearer}").parse().unwrap());
18172        let response = yield_metrics(State(st), headers).await;
18173        assert_eq!(response.status(), StatusCode::OK);
18174        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
18175            .await
18176            .unwrap();
18177        serde_json::from_slice(&bytes).unwrap()
18178    }
18179
18180    #[test]
18181    fn exposed_open_bind_is_refused_before_server_start() {
18182        assert!(validate_bind_security("127.0.0.1:8080", false, false).unwrap());
18183        assert!(validate_bind_security("[::1]:8080", false, false).unwrap());
18184
18185        let err = validate_bind_security("0.0.0.0:8000", false, false).unwrap_err();
18186        assert!(err.contains("refusing unauthenticated non-loopback bind"));
18187        assert!(err.contains("MEMRA_API_KEY"));
18188        assert!(err.contains("MEMRA_ALLOW_OPEN_BIND=1"));
18189        assert!(validate_bind_security("[::]:8000", false, false).is_err());
18190
18191        assert!(!validate_bind_security("0.0.0.0:8000", true, false).unwrap());
18192        assert!(!validate_bind_security("0.0.0.0:8000", false, true).unwrap());
18193    }
18194
18195    #[tokio::test]
18196    async fn keyed_metrics_require_and_accept_api_bearer() {
18197        let mut st = fake_worker_state();
18198        st.api_auth.single_key = Some(Arc::from("completion-secret"));
18199        st.metrics_auth = MetricsAuth::new(true, st.api_auth.configured(), None);
18200
18201        let response = get_metrics(State(st.clone()), HeaderMap::new()).await;
18202        assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
18203        let response = yield_metrics(State(st.clone()), HeaderMap::new()).await;
18204        assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
18205
18206        let mut headers = HeaderMap::new();
18207        headers.insert("authorization", "Bearer completion-secret".parse().unwrap());
18208        assert_eq!(
18209            get_metrics(State(st.clone()), headers.clone())
18210                .await
18211                .status(),
18212            StatusCode::OK,
18213        );
18214        let body = metrics_json(st.clone(), "completion-secret").await;
18215        assert!(
18216            body.get("admitted").is_some(),
18217            "the legacy single-key domain keeps cumulative counters",
18218        );
18219        assert!(
18220            body.get("active_sessions").is_none(),
18221            "a static completion key is not an operator metrics principal",
18222        );
18223        assert_eq!(
18224            yield_metrics(State(st), headers).await.status(),
18225            StatusCode::OK
18226        );
18227    }
18228
18229    #[tokio::test]
18230    async fn keyring_metrics_bearer_sees_only_its_tenant_rows() {
18231        let st = multi_key_metrics_state(None);
18232        let body = metrics_json(st.clone(), METRICS_KEY_ACME).await;
18233        assert_eq!(
18234            body.as_object().unwrap().len(),
18235            2,
18236            "completion metrics must contain only tenant-scoped rows",
18237        );
18238        let tenants = body["tenants"].as_object().unwrap();
18239        assert_eq!(tenants.len(), 1);
18240        assert_eq!(tenants["t:acme"]["prompt_tokens_in"], 100);
18241        assert!(!tenants.contains_key("t:blue"));
18242        let adsd = body["adsd_suspect_total"].as_object().unwrap();
18243        assert_eq!(adsd.len(), 1);
18244        assert_eq!(adsd["t:acme"], 1);
18245        assert!(!adsd.contains_key("t:blue"));
18246
18247        let mut headers = HeaderMap::new();
18248        headers.insert(
18249            "authorization",
18250            format!("Bearer {METRICS_KEY_ACME}").parse().unwrap(),
18251        );
18252        assert_eq!(
18253            yield_metrics(State(st), headers).await.status(),
18254            StatusCode::FORBIDDEN,
18255            "the process-wide yield view requires an operator metrics token",
18256        );
18257    }
18258
18259    #[tokio::test]
18260    async fn tenant_metrics_hide_capacity_and_aggregate_spec() {
18261        let body = metrics_json(multi_key_metrics_state(None), METRICS_KEY_ACME).await;
18262        for operator_only in [
18263            "prefix_cache_entries",
18264            "prefix_cache_bytes",
18265            "prefix_cache_skips_budget",
18266            "prefix_cache_skips_pinned",
18267            "active_sessions",
18268            "queued_requests",
18269            "admission_inflight",
18270            "admission_booked_bytes",
18271            "continuation_pool_entries",
18272            "spec_pool_entries",
18273            "cuda_driver_free_bytes",
18274            "cuda_pool_reserved_bytes",
18275            "cuda_pool_used_bytes",
18276            "cuda_pool_cached_bytes",
18277            "constraint_compiler_fail_closed",
18278            "serve_idle_seconds",
18279            "spec",
18280            "spec_tau",
18281            "spec_accept_by_position",
18282            "dual_pp",
18283            "pp_wave",
18284            "peer_probe_bypassed",
18285            "peer_probe_boundary_copies",
18286            "peer_probe_runtime_reprobes",
18287            "peer_probe_runtime_failures",
18288            "peer_probe_deferred_total",
18289            "peer_probe_integrity_degraded",
18290            "peer_probe_degraded_to_host_bounce",
18291        ] {
18292            assert!(
18293                body.get(operator_only).is_none(),
18294                "tenant metrics must not expose operator field {operator_only}",
18295            );
18296        }
18297    }
18298
18299    #[test]
18300    fn populated_spec_acceptance_metrics_are_operator_only() {
18301        for scope in [
18302            MetricsScope::CompletionDomain,
18303            MetricsScope::Tenant("t:acme".into()),
18304        ] {
18305            let mut body = json!({});
18306            insert_spec_acceptance_metrics(&mut body, &scope, || {
18307                panic!("tenant scope evaluated the process-wide spec snapshot")
18308            });
18309            assert!(body.get("spec_tau").is_none(), "{scope:?} leaked spec tau");
18310            assert!(
18311                body.get("spec_accept_by_position").is_none(),
18312                "{scope:?} leaked the accept histogram"
18313            );
18314        }
18315
18316        let mut telemetry = memra_engine::spec::SpecTelemetry {
18317            rounds: 4,
18318            drafted: 12,
18319            accepted: 6,
18320            ..Default::default()
18321        };
18322        telemetry.pos_drafted[..3].copy_from_slice(&[4, 4, 4]);
18323        telemetry.pos_accepted[..3].copy_from_slice(&[3, 2, 1]);
18324        let mut body = json!({});
18325        insert_spec_acceptance_metrics(&mut body, &MetricsScope::All, || {
18326            HashMap::from([("model-a".to_string(), telemetry)])
18327        });
18328        assert_eq!(body["spec_tau"]["model-a"], 1.5);
18329        let histogram = &body["spec_accept_by_position"]["model-a"];
18330        assert_eq!(histogram["window_seconds"], worker::SPEC_METRICS_WINDOW_S);
18331        assert_eq!(histogram["rounds"], 4);
18332        assert_eq!(histogram["offered"], json!([4, 4, 4]));
18333        assert_eq!(histogram["accepted"], json!([3, 2, 1]));
18334        assert_eq!(histogram["accept_rate"], json!([0.75, 0.5, 0.25]));
18335    }
18336
18337    #[test]
18338    fn populated_dual_pp_metrics_are_operator_only() {
18339        let populated = DualPpMetricsSnapshot {
18340            stage_ns: [1_000_000, 2_000_000, 3_000_000, 4_000_000],
18341            stage_samples: [1, 1, 1, 1],
18342            dropped_timing_samples: 0,
18343            overlaps: 17,
18344            slot_pairs: 19,
18345            slot_uses: [19, 19],
18346            slot_collisions: 0,
18347        };
18348        for scope in [
18349            MetricsScope::CompletionDomain,
18350            MetricsScope::Tenant("t:acme".into()),
18351        ] {
18352            let mut body = json!({});
18353            insert_dual_pp_metrics(&mut body, &scope, || populated);
18354            assert!(
18355                body.get("dual_pp").is_none(),
18356                "{scope:?} leaked dual PP topology"
18357            );
18358        }
18359
18360        let mut body = json!({});
18361        insert_dual_pp_metrics(&mut body, &MetricsScope::All, || populated);
18362        assert_eq!(body["dual_pp"]["overlaps"], 17);
18363        assert_eq!(body["dual_pp"]["slot_pairs"], 19);
18364        assert_eq!(body["dual_pp"]["slot_uses"], json!([19, 19]));
18365        assert_eq!(body["dual_pp"]["slot_collisions"], 0);
18366        assert_eq!(
18367            body["dual_pp"]["cuda_event_spans"]["wave_a_stage0"]["mean_ms"],
18368            1.0
18369        );
18370    }
18371
18372    #[test]
18373    fn populated_pp_wave_metrics_are_operator_only() {
18374        let populated = PpWaveMetricsSnapshot {
18375            ticks: 11,
18376            cells: 96,
18377            overlaps: 37,
18378        };
18379        for scope in [
18380            MetricsScope::CompletionDomain,
18381            MetricsScope::Tenant("t:acme".into()),
18382        ] {
18383            let mut body = json!({});
18384            insert_pp_wave_metrics(&mut body, &scope, || populated);
18385            assert!(
18386                body.get("pp_wave").is_none(),
18387                "{scope:?} leaked PP wave topology"
18388            );
18389        }
18390
18391        let mut body = json!({});
18392        insert_pp_wave_metrics(&mut body, &MetricsScope::All, || populated);
18393        assert_eq!(body["pp_wave"]["ticks"], 11);
18394        assert_eq!(body["pp_wave"]["cells"], 96);
18395        assert_eq!(body["pp_wave"]["overlaps"], 37);
18396    }
18397
18398    #[test]
18399    fn peer_probe_metrics_are_operator_only() {
18400        let populated = memra_engine::pp::PeerProbeMetrics {
18401            bypassed: 1,
18402            boundary_copies: 8_192,
18403            runtime_probes: 1,
18404            runtime_failures: 0,
18405            deferred_total: 4,
18406            integrity_degraded: true,
18407            degraded_to_host_bounce: true,
18408        };
18409        for scope in [
18410            MetricsScope::CompletionDomain,
18411            MetricsScope::Tenant("t:acme".into()),
18412        ] {
18413            let mut body = json!({});
18414            insert_peer_probe_metrics(&mut body, &scope, || populated);
18415            assert!(body.get("peer_probe_bypassed").is_none());
18416        }
18417
18418        let mut body = json!({});
18419        insert_peer_probe_metrics(&mut body, &MetricsScope::All, || populated);
18420        assert_eq!(body["peer_probe_bypassed"], 1);
18421        assert_eq!(body["peer_probe_boundary_copies"], 8_192);
18422        assert_eq!(body["peer_probe_runtime_reprobes"], 1);
18423        assert_eq!(body["peer_probe_runtime_failures"], 0);
18424        assert_eq!(body["peer_probe_deferred_total"], 4);
18425        assert_eq!(body["peer_probe_integrity_degraded"], true);
18426        assert_eq!(body["peer_probe_degraded_to_host_bounce"], true);
18427    }
18428
18429    #[tokio::test]
18430    async fn prefix_aggregate_metrics_are_operator_only_but_tenant_ratio_remains() {
18431        let tenant_body = metrics_json(multi_key_metrics_state(None), METRICS_KEY_ACME).await;
18432        for operator_only in [
18433            "lcp_histogram",
18434            "cache_hit_token_ratio",
18435            "prefix_cache_hits",
18436            "prefix_cache_misses",
18437            "prefix_cache_inserts",
18438            "prefix_cache_evictions",
18439            "prefix_cache_skips_budget",
18440            "prefix_cache_skips_pinned",
18441            "prefix_cache_hit_tokens",
18442        ] {
18443            assert!(
18444                tenant_body.get(operator_only).is_none(),
18445                "tenant metrics must not expose global prefix field {operator_only}",
18446            );
18447        }
18448        assert_eq!(tenant_body["tenants"].as_object().unwrap().len(), 1);
18449        assert_eq!(tenant_body["tenants"]["t:acme"]["prompt_tokens_in"], 100);
18450        assert_eq!(tenant_body["tenants"]["t:acme"]["cached_tokens_in"], 40);
18451        assert_eq!(
18452            tenant_body["tenants"]["t:acme"]["cache_hit_token_ratio"],
18453            0.4
18454        );
18455
18456        let operator_body = metrics_json(
18457            multi_key_metrics_state(Some("scrape-secret")),
18458            "scrape-secret",
18459        )
18460        .await;
18461        assert_eq!(operator_body["prefix_cache_hits"], 2);
18462        assert_eq!(operator_body["prefix_cache_misses"], 3);
18463        assert_eq!(operator_body["prefix_cache_inserts"], 5);
18464        assert_eq!(operator_body["prefix_cache_evictions"], 7);
18465        assert_eq!(operator_body["prefix_cache_skips_budget"], 9);
18466        assert_eq!(operator_body["prefix_cache_skips_pinned"], 10);
18467        assert_eq!(operator_body["prefix_cache_hit_tokens"], 11);
18468        assert_eq!(operator_body["cache_hit_token_ratio"], 0.15);
18469        assert_eq!(operator_body["lcp_histogram"]["counts"][4], 13);
18470    }
18471
18472    #[tokio::test]
18473    async fn configured_metrics_token_is_exclusive_and_sees_all_tenants() {
18474        let st = multi_key_metrics_state(Some("scrape-secret"));
18475        let mut completion_headers = HeaderMap::new();
18476        completion_headers.insert(
18477            "authorization",
18478            format!("Bearer {METRICS_KEY_ACME}").parse().unwrap(),
18479        );
18480        assert_eq!(
18481            get_metrics(State(st.clone()), completion_headers.clone())
18482                .await
18483                .status(),
18484            StatusCode::FORBIDDEN,
18485        );
18486        assert_eq!(
18487            yield_metrics(State(st.clone()), completion_headers)
18488                .await
18489                .status(),
18490            StatusCode::FORBIDDEN,
18491        );
18492
18493        let body = metrics_json(st.clone(), "scrape-secret").await;
18494        let tenants = body["tenants"].as_object().unwrap();
18495        assert_eq!(tenants.len(), 2);
18496        assert!(tenants.contains_key("t:acme"));
18497        assert!(tenants.contains_key("t:blue"));
18498        assert_eq!(body["adsd_suspect_total"]["t:acme"], 1);
18499        assert_eq!(body["adsd_suspect_total"]["t:blue"], 2);
18500        assert_eq!(body["active_sessions"], 3);
18501        assert_eq!(body["queued_requests"], 5);
18502        // D2 gap G2: the per-model admission book is an operator surface.
18503        assert_eq!(body["admission_inflight"]["m"], 4);
18504        assert_eq!(body["admission_booked_bytes"]["m"], 41_000_000);
18505        assert_eq!(body["prefix_cache_bytes"], 31);
18506        assert_eq!(body["cuda_driver_free_bytes"], 13);
18507        assert_eq!(body["constraint_compiler_fail_closed"]["m"], 1);
18508        assert_eq!(body["spec"]["m"]["drafted"], 6);
18509        assert_eq!(body["spec_tau"]["m"], 1.5);
18510        assert_eq!(
18511            body["spec_accept_by_position"]["m"]["accepted"],
18512            json!([3, 2, 1])
18513        );
18514        let yield_body = yield_metrics_json(st, "scrape-secret").await;
18515        assert_eq!(yield_body["batch_size_last"], 37);
18516    }
18517
18518    #[tokio::test]
18519    async fn metrics_token_protects_public_override_without_api_keys() {
18520        let mut st = fake_worker_state();
18521        st.metrics_auth = MetricsAuth::new(false, false, Some("scrape-secret".into()));
18522
18523        assert_eq!(
18524            get_metrics(State(st.clone()), HeaderMap::new())
18525                .await
18526                .status(),
18527            StatusCode::UNAUTHORIZED,
18528        );
18529        let mut headers = HeaderMap::new();
18530        headers.insert("authorization", "Bearer scrape-secret".parse().unwrap());
18531        assert_eq!(
18532            get_metrics(State(st.clone()), headers.clone())
18533                .await
18534                .status(),
18535            StatusCode::OK,
18536        );
18537        assert_eq!(
18538            yield_metrics(State(st), headers).await.status(),
18539            StatusCode::OK
18540        );
18541    }
18542
18543    #[tokio::test]
18544    async fn no_key_loopback_metrics_remain_open_for_development() {
18545        let mut st = fake_worker_state();
18546        st.metrics_auth = MetricsAuth::new(true, false, None);
18547        let response = get_metrics(State(st.clone()), HeaderMap::new()).await;
18548        assert_eq!(response.status(), StatusCode::OK);
18549        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
18550            .await
18551            .unwrap();
18552        let body: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
18553        assert!(
18554            body.get("active_sessions").is_some(),
18555            "no-key loopback development keeps full operator visibility",
18556        );
18557        assert_eq!(
18558            yield_metrics(State(st), HeaderMap::new()).await.status(),
18559            StatusCode::OK,
18560        );
18561    }
18562
18563    #[test]
18564    fn rate_limit_math_remaining_hits_zero_at_cap_and_reset_arms() {
18565        let metrics = SharedMetrics::default();
18566        // free slots: remaining counts down, reset stays 0.
18567        let rl = RateLimit::compute(4, 1, &metrics);
18568        assert_eq!((rl.limit, rl.remaining, rl.reset_s), (4, 3, 0));
18569        let rl = RateLimit::compute(4, 3, &metrics);
18570        assert_eq!(rl.remaining, 1);
18571        // at cap: remaining 0, reset arms (static default — no meter signal here).
18572        let rl = RateLimit::compute(4, 4, &metrics);
18573        assert_eq!(rl.remaining, 0);
18574        assert!(rl.reset_s > 0, "reset must arm when no slots are free");
18575        // over cap (queued interactive): saturates at 0, never underflows.
18576        assert_eq!(RateLimit::compute(4, 9, &metrics).remaining, 0);
18577        // meter signal: reset = mean tokens/request x p50 step, ceil seconds.
18578        let m = worker::Metrics {
18579            completed: 2,
18580            tokens_out: 200,
18581            step_p50_ms: 20.0,
18582            ..Default::default()
18583        };
18584        assert_eq!(reset_estimate_s(&m), 2); // 100 tok x 20ms = 2.0s
18585    }
18586
18587    #[test]
18588    fn inflight_guard_counts_up_and_frees_on_drop() {
18589        let counts: InflightCounts = Arc::new(Default::default());
18590        let tenants: TenantGauge = Arc::new(Default::default());
18591        let (g1, n1, t1) = InflightGuard::try_acquire(
18592            counts.clone(),
18593            lanes::Lane::Interactive,
18594            tenants.clone(),
18595            "acme",
18596            None,
18597        )
18598        .unwrap();
18599        let (g2, n2, t2) = InflightGuard::try_acquire(
18600            counts.clone(),
18601            lanes::Lane::Interactive,
18602            tenants.clone(),
18603            "acme",
18604            None,
18605        )
18606        .unwrap();
18607        assert_eq!((n1, n2), (1, 2));
18608        // tenant gauge counts per tenant, across lanes.
18609        assert_eq!((t1, t2), (1, 2));
18610        // lanes are independent gauges; a different tenant starts at 1.
18611        let (gj, nj, tj) = InflightGuard::try_acquire(
18612            counts.clone(),
18613            lanes::Lane::Judge,
18614            tenants.clone(),
18615            "blue",
18616            None,
18617        )
18618        .unwrap();
18619        assert_eq!((nj, tj), (1, 1));
18620        drop(g1);
18621        drop(gj);
18622        assert_eq!(counts[0].load(std::sync::atomic::Ordering::SeqCst), 1);
18623        assert_eq!(counts[1].load(std::sync::atomic::Ordering::SeqCst), 0);
18624        assert_eq!(tenants.lock().unwrap().get("acme"), Some(&1));
18625        // tenant entries are removed at zero (bounded by CONCURRENT tenants).
18626        assert!(tenants.lock().unwrap().get("blue").is_none());
18627        drop(g2);
18628        assert_eq!(counts[0].load(std::sync::atomic::Ordering::SeqCst), 0);
18629        assert!(tenants.lock().unwrap().is_empty());
18630    }
18631
18632    #[test]
18633    fn tenant_concurrency_cap_is_atomic_across_arrivals() {
18634        let counts: InflightCounts = Arc::new(Default::default());
18635        let tenants: TenantGauge = Arc::new(Default::default());
18636        let start = Arc::new(std::sync::Barrier::new(3));
18637        let attempted = Arc::new(std::sync::Barrier::new(3));
18638        let mut joins = Vec::new();
18639        for _ in 0..2 {
18640            let counts = counts.clone();
18641            let tenants = tenants.clone();
18642            let start = start.clone();
18643            let attempted = attempted.clone();
18644            joins.push(std::thread::spawn(move || {
18645                start.wait();
18646                let result = InflightGuard::try_acquire(
18647                    counts,
18648                    lanes::Lane::Interactive,
18649                    tenants,
18650                    "preview_001",
18651                    Some(1),
18652                );
18653                let won = result.is_ok();
18654                attempted.wait(); // winner holds its guard until both arrivals attempted.
18655                drop(result);
18656                won
18657            }));
18658        }
18659        start.wait();
18660        attempted.wait();
18661        let wins = joins
18662            .into_iter()
18663            .map(|join| join.join().unwrap())
18664            .filter(|won| *won)
18665            .count();
18666        assert_eq!(wins, 1, "exactly one simultaneous request may pass cap=1");
18667        assert_eq!(counts[0].load(std::sync::atomic::Ordering::SeqCst), 0);
18668        assert!(tenants.lock().unwrap().is_empty());
18669    }
18670
18671    #[tokio::test]
18672    async fn tenant_concurrency_cap_rejects_before_worker_admission() {
18673        let st = fake_worker_state();
18674        let tenant = auth::TenantCtx {
18675            tenant: "preview_001".into(),
18676            lane_class: auth::LaneClass::Interactive,
18677            rate_limit: Some(1),
18678            key_prefix: None,
18679        };
18680        let first_env = Envelope::new(true);
18681        let (guard, first_rl) =
18682            match acquire_request_slot(&st, lanes::Lane::Interactive, &tenant, &first_env) {
18683                Ok(slot) => slot,
18684                Err(_) => panic!("the first request must acquire the tenant slot"),
18685            };
18686        assert_eq!((first_rl.limit, first_rl.remaining), (1, 0));
18687
18688        let second_env = Envelope::new(true);
18689        let response =
18690            match acquire_request_slot(&st, lanes::Lane::Interactive, &tenant, &second_env) {
18691                Err(response) => response,
18692                Ok(_) => panic!("the second request must be rejected at the tenant cap"),
18693            };
18694        assert_eq!(response.status(), StatusCode::TOO_MANY_REQUESTS);
18695        assert_eq!(response.headers()["retry-after"], "2");
18696        assert_eq!(response.headers()["retry-after-ms"], "2000");
18697        assert_eq!(response.headers()["x-ratelimit-limit"], "1");
18698        assert_eq!(response.headers()["x-ratelimit-remaining"], "0");
18699        assert_eq!(response.headers()["x-request-id"], second_env.id);
18700        assert_eq!(
18701            st.inflight[0].load(std::sync::atomic::Ordering::SeqCst),
18702            1,
18703            "rejected request must not consume a lane slot"
18704        );
18705        assert_eq!(
18706            st.tenant_inflight
18707                .lock()
18708                .unwrap()
18709                .get("preview_001")
18710                .copied(),
18711            Some(1),
18712            "rejected request must not increment the tenant gauge"
18713        );
18714        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
18715            .await
18716            .unwrap();
18717        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
18718        assert_eq!(payload["error"]["type"], "rate_limit_error");
18719        assert_eq!(payload["error"]["code"], "rate_limit_exceeded");
18720        assert!(
18721            payload["error"]["message"]
18722                .as_str()
18723                .unwrap()
18724                .contains("concurrent request limit")
18725        );
18726
18727        drop(guard);
18728        let _ = InflightGuard::try_acquire(
18729            st.inflight.clone(),
18730            lanes::Lane::Interactive,
18731            st.tenant_inflight.clone(),
18732            "preview_001",
18733            Some(1),
18734        )
18735        .expect("slot must reopen after the in-flight request completes");
18736    }
18737
18738    #[test]
18739    fn tenant_rate_limit_override_is_min_with_global_cap() {
18740        let metrics = SharedMetrics::default();
18741        let unlimited = auth::TenantCtx::default_tenant();
18742        let capped = auth::TenantCtx {
18743            tenant: "acme".into(),
18744            lane_class: auth::LaneClass::Interactive,
18745            rate_limit: Some(2),
18746            key_prefix: None,
18747        };
18748        let global = lane_cap(lanes::Lane::Interactive);
18749        // no override: the global lane cap reports as before.
18750        let rl = RateLimit::at_admit(lanes::Lane::Interactive, 1, &metrics, &unlimited, 1);
18751        assert_eq!((rl.limit, rl.remaining), (global, global - 1));
18752        // override binds: limit = the tenant cap, remaining counts the TENANT gauge.
18753        let rl = RateLimit::at_admit(lanes::Lane::Interactive, 5, &metrics, &capped, 1);
18754        assert_eq!((rl.limit, rl.remaining), (2, 1));
18755        let rl = RateLimit::at_admit(lanes::Lane::Interactive, 5, &metrics, &capped, 2);
18756        assert_eq!(rl.remaining, 0);
18757        assert!(rl.reset_s > 0, "reset must arm at the tenant cap too");
18758        // the GLOBAL cap stays authoritative: a saturated lane zeroes the tenant's
18759        // remaining even below its own cap, and an override above the global cap is
18760        // ignored (min(t, global) — a key cannot widen the lane).
18761        let rl = RateLimit::at_admit(lanes::Lane::Interactive, global, &metrics, &capped, 0);
18762        assert_eq!(rl.remaining, 0);
18763        let wide = auth::TenantCtx {
18764            rate_limit: Some(global + 100),
18765            ..capped.clone()
18766        };
18767        let rl = RateLimit::at_admit(lanes::Lane::Interactive, 1, &metrics, &wide, 1);
18768        assert_eq!((rl.limit, rl.remaining), (global, global - 1));
18769    }
18770
18771    #[test]
18772    fn batch_class_keys_default_to_harvest_and_cannot_claim_interactive() {
18773        let batch = auth::TenantCtx {
18774            tenant: "bulk".into(),
18775            lane_class: auth::LaneClass::Batch,
18776            rate_limit: None,
18777            key_prefix: None,
18778        };
18779        let interactive = auth::TenantCtx::default_tenant();
18780        let hdr = |v: Option<&str>| {
18781            let mut h = axum::http::HeaderMap::new();
18782            if let Some(v) = v {
18783                h.insert("x-lane", axum::http::HeaderValue::from_str(v).unwrap());
18784            }
18785            h
18786        };
18787        // interactive-class: legacy behavior exactly (default interactive, header honored).
18788        assert_eq!(
18789            lane_for_tenant(&hdr(None), &interactive).unwrap(),
18790            lanes::Lane::Interactive
18791        );
18792        assert_eq!(
18793            lane_for_tenant(&hdr(Some("judge")), &interactive).unwrap(),
18794            lanes::Lane::Judge
18795        );
18796        // batch-class: defaults to harvest; judge ok; interactive is a loud 403.
18797        assert_eq!(
18798            lane_for_tenant(&hdr(None), &batch).unwrap(),
18799            lanes::Lane::Harvest
18800        );
18801        assert_eq!(
18802            lane_for_tenant(&hdr(Some("judge")), &batch).unwrap(),
18803            lanes::Lane::Judge
18804        );
18805        let resp = lane_for_tenant(&hdr(Some("interactive")), &batch).unwrap_err();
18806        assert_eq!(resp.status(), StatusCode::FORBIDDEN);
18807        // unknown lane still 400s for everyone.
18808        let resp = lane_for_tenant(&hdr(Some("turbo")), &interactive).unwrap_err();
18809        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
18810    }
18811
18812    #[tokio::test]
18813    async fn handler_layer_refusals_are_openai_objects_with_x_should_retry() {
18814        // The lane refusals were the last bare-string error bodies on the surface:
18815        // `{"error": "unknown x-lane ..."}` indexes as a string in every SDK that reads
18816        // error.type / error.code. Both lane refusals now go through error_response_coded,
18817        // and both are unfixable-by-retry 4xx, so both must also say so in a header.
18818        let hdr = |v: &str| {
18819            let mut h = axum::http::HeaderMap::new();
18820            h.insert("x-lane", axum::http::HeaderValue::from_str(v).unwrap());
18821            h
18822        };
18823        let body = |resp: Response| async move {
18824            let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
18825                .await
18826                .unwrap();
18827            serde_json::from_slice::<serde_json::Value>(&bytes).unwrap()
18828        };
18829
18830        let resp = lane_for_tenant(&hdr("turbo"), &auth::TenantCtx::default_tenant()).unwrap_err();
18831        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
18832        assert_eq!(resp.headers().get("x-should-retry").unwrap(), "false");
18833        let payload = body(resp).await;
18834        assert!(
18835            payload["error"].is_object(),
18836            "bare-string error body: {payload}"
18837        );
18838        assert_eq!(payload["error"]["type"], "invalid_request_error");
18839        assert_eq!(payload["error"]["param"], "x-lane");
18840        assert_eq!(payload["error"]["code"], "invalid_lane");
18841
18842        let batch = auth::TenantCtx {
18843            tenant: "bulk".into(),
18844            lane_class: auth::LaneClass::Batch,
18845            rate_limit: None,
18846            key_prefix: None,
18847        };
18848        let resp = lane_for_tenant(&hdr("interactive"), &batch).unwrap_err();
18849        assert_eq!(resp.status(), StatusCode::FORBIDDEN);
18850        assert_eq!(resp.headers().get("x-should-retry").unwrap(), "false");
18851        let payload = body(resp).await;
18852        assert_eq!(payload["error"]["type"], "authentication_error");
18853        assert_eq!(payload["error"]["param"], "x-lane");
18854    }
18855
18856    /// Serializes tests that read or flip the process-global DRAINING flag (the drain
18857    /// test must not 503 a concurrently-running handler test).
18858    static DRAIN_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
18859
18860    /// Acquire DRAIN_LOCK surviving a poisoned peer, and restore the baseline it guards.
18861    ///
18862    /// 2026-09-01 (accrace close): one load-flaky deadline test panicked while holding
18863    /// this lock, and every later acquirer's `.unwrap()` then failed with PoisonError —
18864    /// one flake became 21 reds and buried its own cause under twenty unrelated ones.
18865    /// The lock guards the process-global DRAINING flag, not any invariant of the
18866    /// panicked test's own data, so recovering the guard is sound as long as the flag is
18867    /// put back to the "not draining" baseline every acquirer assumes; the drain tests
18868    /// that want it up set it themselves AFTER acquiring. Same poison-recovery idiom as
18869    /// `admission_counters_guard`. This normalization also retires the per-test
18870    /// `DRAINING.store(false, ..)` resets the 2026-08-09 flake introduced — the baseline
18871    /// now has one owner.
18872    fn drain_lock() -> std::sync::MutexGuard<'static, ()> {
18873        let guard = DRAIN_LOCK.lock().unwrap_or_else(|poisoned| {
18874            // Un-latch the flag too: poison otherwise persists forever, and only call
18875            // sites routed through this helper would survive it.
18876            DRAIN_LOCK.clear_poison();
18877            poisoned.into_inner()
18878        });
18879        DRAINING.store(false, std::sync::atomic::Ordering::SeqCst);
18880        guard
18881    }
18882
18883    /// Put DRAINING back down on drop — including the drop that unwinds a failed
18884    /// assertion. The flag is read by every handler, INCLUDING in tests that have no
18885    /// reason to hold DRAIN_LOCK: a drain test that panicked between its `store(true)`
18886    /// and its reset would 503 every concurrently-running handler test until the next
18887    /// `drain_lock()` acquisition normalized the flag.
18888    struct DrainingRestore;
18889    impl Drop for DrainingRestore {
18890        fn drop(&mut self) {
18891            DRAINING.store(false, std::sync::atomic::Ordering::SeqCst);
18892        }
18893    }
18894
18895    #[tokio::test]
18896    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
18897    async fn responses_carry_rate_limit_headers_and_slot_frees() {
18898        let _l = drain_lock();
18899        let st = fake_worker_state();
18900        // non-stream chat: headers present, remaining = cap - 1 (this request held
18901        // the only slot), slot freed after completion.
18902        let resp = chat_completions(
18903            State(st.clone()),
18904            axum::http::HeaderMap::new(),
18905            None,
18906            Json(
18907                serde_json::from_value(serde_json::json!({
18908                    "model": "m", "messages": [{"role": "user", "content": "t"}]
18909                }))
18910                .unwrap(),
18911            ),
18912        )
18913        .await;
18914        assert_eq!(resp.status(), StatusCode::OK);
18915        let h = resp.headers();
18916        let limit: usize = h["x-ratelimit-limit"].to_str().unwrap().parse().unwrap();
18917        let remaining: usize = h["x-ratelimit-remaining"]
18918            .to_str()
18919            .unwrap()
18920            .parse()
18921            .unwrap();
18922        assert_eq!(remaining, limit - 1);
18923        assert_eq!(h["x-ratelimit-reset"], "0");
18924        assert_eq!(
18925            st.inflight[0].load(std::sync::atomic::Ordering::SeqCst),
18926            0,
18927            "slot must free at completion"
18928        );
18929        // streaming completions: headers on the SSE response too; slot freed once the
18930        // body is drained (the guard rides the stream).
18931        let resp = completions(
18932            State(st.clone()),
18933            axum::http::HeaderMap::new(),
18934            None,
18935            Json(
18936                serde_json::from_value(serde_json::json!({
18937                    "model": "m", "prompt": "t", "stream": true
18938                }))
18939                .unwrap(),
18940            ),
18941        )
18942        .await;
18943        assert_eq!(resp.status(), StatusCode::OK);
18944        assert!(resp.headers().contains_key("x-ratelimit-limit"));
18945        assert!(resp.headers().contains_key("x-ratelimit-remaining"));
18946        assert!(resp.headers().contains_key("x-ratelimit-reset"));
18947        assert_eq!(
18948            st.inflight[0].load(std::sync::atomic::Ordering::SeqCst),
18949            1,
18950            "stream in flight holds the slot"
18951        );
18952        let _ = axum::body::to_bytes(resp.into_body(), usize::MAX)
18953            .await
18954            .unwrap();
18955        assert_eq!(
18956            st.inflight[0].load(std::sync::atomic::Ordering::SeqCst),
18957            0,
18958            "slot must free when the stream completes"
18959        );
18960    }
18961
18962    /// REGRESSION FENCE for the 2026-09-02 rerank/embeddings ledger incident: a multi-item
18963    /// capture request opens ONE receipt PER ITEM, each under its own child id
18964    /// `<x-request-id>.<index>`, and settles every one of them. Under the old shared parent
18965    /// id this test's `opened` list read `[parent, parent, parent]`, which the darklanes
18966    /// ledger's replay guard turned into one debit (equal costs) or a 500 (unequal costs).
18967    #[tokio::test]
18968    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
18969    async fn multi_item_capture_requests_open_one_receipt_per_item_under_child_ids() {
18970        let _l = drain_lock();
18971        let mut st = fake_worker_state();
18972        let mock = MockMetering::admit_all();
18973        st.metering = Some(mock.clone());
18974
18975        let resp = embed_api::embeddings_admitted(
18976            State(st.clone()),
18977            HeaderMap::new(),
18978            AdmittedJson(
18979                serde_json::from_value(json!({"model": "m", "input": ["a", "bb", "ccc"]})).unwrap(),
18980                BodyAdmissionLease(None),
18981            ),
18982        )
18983        .await;
18984        assert_eq!(resp.status(), StatusCode::OK);
18985        let parent = resp.headers()["x-request-id"].to_str().unwrap().to_string();
18986        assert!(
18987            !parent.contains('.'),
18988            "the caller sees the parent id: {parent}"
18989        );
18990        let body: serde_json::Value = serde_json::from_slice(
18991            &axum::body::to_bytes(resp.into_body(), usize::MAX)
18992                .await
18993                .unwrap(),
18994        )
18995        .unwrap();
18996        assert_eq!(body["data"].as_array().map(Vec::len), Some(3));
18997        let events = mock.events();
18998        let opened: Vec<(String, &'static str)> = events
18999            .iter()
19000            .filter_map(|e| match e {
19001                MeterEvent::Open {
19002                    request_id, route, ..
19003                } => Some((request_id.clone(), *route)),
19004                _ => None,
19005            })
19006            .collect();
19007        assert_eq!(
19008            opened,
19009            vec![
19010                (format!("{parent}.0"), "/v1/embeddings"),
19011                (format!("{parent}.1"), "/v1/embeddings"),
19012                (format!("{parent}.2"), "/v1/embeddings"),
19013            ],
19014            "one receipt per input, each under its own child id: {events:?}"
19015        );
19016        assert_eq!(
19017            events
19018                .iter()
19019                .filter(|e| matches!(e, MeterEvent::Complete { .. }))
19020                .count(),
19021            3,
19022            "every input settles its own receipt: {events:?}"
19023        );
19024
19025        let resp = embed_api::rerank_admitted(
19026            State(st),
19027            HeaderMap::new(),
19028            AdmittedJson(
19029                serde_json::from_value(
19030                    json!({"model": "m", "query": "q", "documents": ["d0", "d1"]}),
19031                )
19032                .unwrap(),
19033                BodyAdmissionLease(None),
19034            ),
19035        )
19036        .await;
19037        assert_eq!(resp.status(), StatusCode::OK);
19038        let parent = resp.headers()["x-request-id"].to_str().unwrap().to_string();
19039        let opened: Vec<String> = mock
19040            .events()
19041            .into_iter()
19042            .skip(events.len())
19043            .filter_map(|e| match e {
19044                MeterEvent::Open {
19045                    request_id,
19046                    route: "/v1/rerank",
19047                    ..
19048                } => Some(request_id),
19049                _ => None,
19050            })
19051            .collect();
19052        assert_eq!(opened, vec![format!("{parent}.0"), format!("{parent}.1")]);
19053    }
19054
19055    #[tokio::test]
19056    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19057    async fn handlers_sync_worker_truth_usage_and_cost_before_terminal_response() {
19058        let _l = drain_lock();
19059        let mut st = fake_worker_state();
19060        let mock = MockMetering::admit_all();
19061        st.metering = Some(mock.clone());
19062
19063        let nonstream = chat_completions(
19064            State(st.clone()),
19065            HeaderMap::new(),
19066            None,
19067            Json(
19068                serde_json::from_value(json!({
19069                    "model": "m",
19070                    "messages": [{"role": "user", "content": "t"}],
19071                }))
19072                .unwrap(),
19073            ),
19074        )
19075        .await;
19076        assert_eq!(nonstream.status(), StatusCode::OK);
19077        let nonstream_id = nonstream.headers()["x-request-id"]
19078            .to_str()
19079            .unwrap()
19080            .to_string();
19081
19082        let stream = completions(
19083            State(st),
19084            HeaderMap::new(),
19085            None,
19086            Json(
19087                serde_json::from_value(json!({
19088                    "model": "m",
19089                    "prompt": "t",
19090                    "stream": true,
19091                }))
19092                .unwrap(),
19093            ),
19094        )
19095        .await;
19096        assert_eq!(stream.status(), StatusCode::OK);
19097        let stream_id = stream.headers()["x-request-id"]
19098            .to_str()
19099            .unwrap()
19100            .to_string();
19101        let _ = axum::body::to_bytes(stream.into_body(), usize::MAX)
19102            .await
19103            .unwrap();
19104
19105        // Both requests opened receipts under THEIR request ids (the x-request-id the
19106        // caller saw) and settled COMPLETE with worker-truth counts before the terminal
19107        // response was published.
19108        let events = mock.events();
19109        let opened: Vec<&str> = events
19110            .iter()
19111            .filter_map(|e| match e {
19112                MeterEvent::Open { request_id, .. } => Some(request_id.as_str()),
19113                _ => None,
19114            })
19115            .collect();
19116        assert_eq!(opened, vec![nonstream_id.as_str(), stream_id.as_str()]);
19117        let completes = events
19118            .iter()
19119            .filter(|e| {
19120                matches!(
19121                    e,
19122                    MeterEvent::Complete {
19123                        prompt: 1,
19124                        cached: 0,
19125                        completion: 1,
19126                    }
19127                )
19128            })
19129            .count();
19130        assert_eq!(
19131            completes, 2,
19132            "both surfaces settle complete with worker-truth usage: {events:?}"
19133        );
19134    }
19135
19136    #[tokio::test]
19137    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19138    async fn completion_admission_supports_metered_blocked_and_paid_transitions() {
19139        let _l = drain_lock();
19140        // The handler's admission obligations, scripted at the seam: a denial maps to
19141        // the 402 contract and settles a REJECT receipt; an admission (with or without
19142        // a reservation permit) serves and settles COMPLETE, permit threaded through to
19143        // open(). Which MODES produce which answers is the implementation's business
19144        // and is tested with it (plus the cross-binary parity battery).
19145        let mock = MockMetering::with_limits(vec![
19146            ReserveScript::Insufficient,
19147            ReserveScript::Admit { with_permit: false },
19148            ReserveScript::Blocked,
19149            ReserveScript::Admit { with_permit: true },
19150        ]);
19151        let mut st = fake_worker_state();
19152        st.metering = Some(mock.clone());
19153
19154        // Limits-source health reaches the operator metrics surface through the seam.
19155        let metrics = get_metrics(State(st.clone()), HeaderMap::new()).await;
19156        assert_eq!(metrics.status(), StatusCode::OK);
19157        let metrics_body = axum::body::to_bytes(metrics.into_body(), usize::MAX)
19158            .await
19159            .unwrap();
19160        let metrics_body: serde_json::Value = serde_json::from_slice(&metrics_body).unwrap();
19161        assert_eq!(metrics_body["budget_source_reload_failed"], 0);
19162        assert_eq!(metrics_body["budget_source_reload_consecutive"], 0);
19163        assert_eq!(metrics_body["budget_source_available"], true);
19164
19165        let request = || {
19166            Json(
19167                serde_json::from_value::<CompletionReq>(json!({
19168                    "model": "m",
19169                    "prompt_ids": [1],
19170                    "max_tokens": 1,
19171                }))
19172                .unwrap(),
19173            )
19174        };
19175
19176        let denied = completions(State(st.clone()), HeaderMap::new(), None, request()).await;
19177        assert_eq!(denied.status(), StatusCode::PAYMENT_REQUIRED);
19178        let denied_body = axum::body::to_bytes(denied.into_body(), usize::MAX)
19179            .await
19180            .unwrap();
19181        let denied_body: serde_json::Value = serde_json::from_slice(&denied_body).unwrap();
19182        assert_eq!(denied_body["error"]["type"], "insufficient_balance");
19183        assert_eq!(denied_body["error"]["code"], "insufficient_balance");
19184
19185        let included = completions(State(st.clone()), HeaderMap::new(), None, request()).await;
19186        assert_eq!(included.status(), StatusCode::OK);
19187
19188        // A Blocked denial deliberately reuses the prepaid 402 shape: callers get one
19189        // recovery action; the distinct admission mode is an operator-surface fact.
19190        let blocked = completions(State(st.clone()), HeaderMap::new(), None, request()).await;
19191        assert_eq!(blocked.status(), StatusCode::PAYMENT_REQUIRED);
19192
19193        let admitted = completions(State(st.clone()), HeaderMap::new(), None, request()).await;
19194        assert_eq!(admitted.status(), StatusCode::OK);
19195
19196        let events = mock.events();
19197        let terminal: Vec<&MeterEvent> = events
19198            .iter()
19199            .filter(|e| matches!(e, MeterEvent::Reject { .. } | MeterEvent::Complete { .. }))
19200            .collect();
19201        assert_eq!(
19202            terminal.len(),
19203            4,
19204            "four requests, four terminal settles: {events:?}"
19205        );
19206        assert!(matches!(
19207            terminal[0],
19208            MeterEvent::Reject { status: 402, .. }
19209        ));
19210        assert!(matches!(terminal[1], MeterEvent::Complete { .. }));
19211        assert!(matches!(
19212            terminal[2],
19213            MeterEvent::Reject { status: 402, .. }
19214        ));
19215        assert!(matches!(terminal[3], MeterEvent::Complete { .. }));
19216        // The reservation permit made it through to open() on the paid admission.
19217        let permits: Vec<bool> = events
19218            .iter()
19219            .filter_map(|e| match e {
19220                MeterEvent::Open { with_permit, .. } => Some(*with_permit),
19221                _ => None,
19222            })
19223            .collect();
19224        assert_eq!(
19225            permits,
19226            vec![false, false, false, true],
19227            "the permit rides the receipt exactly when reserve minted one: {events:?}"
19228        );
19229    }
19230
19231    /// A capped KEY answers its own 402 code (the recovery is raising the cap, not
19232    /// adding credit) and the authenticated key's prefix crossed the seam to reserve
19233    /// — the per-key-policy hook (stage 4, engine-billing-extraction-20260829).
19234    #[tokio::test]
19235    async fn a_capped_key_answers_its_own_402_and_the_principal_crosses_the_seam() {
19236        let mock = MockMetering::with_limits(vec![ReserveScript::PrincipalCapped]);
19237        let mut st = fake_worker_state();
19238        st.metering = Some(mock.clone());
19239        let tenant = auth::TenantCtx {
19240            tenant: "acme".into(),
19241            lane_class: auth::LaneClass::Interactive,
19242            rate_limit: None,
19243            key_prefix: Some("mk-acme-testprefix00".into()),
19244        };
19245        let mut request = gate_request(1, 1);
19246        let rejection = admit_tenant_budget(&st, &tenant, &mut request)
19247            .expect_err("a capped key must be refused at admission");
19248        assert!(matches!(rejection, BudgetRejection::PrincipalCapped));
19249        let (response, outcome) = rejection.into_response();
19250        assert_eq!(outcome, "key_spend_cap_reached");
19251        assert_eq!(response.status(), StatusCode::PAYMENT_REQUIRED);
19252        let body = body_value(response).await;
19253        assert_eq!(body["error"]["code"], "key_spend_cap_reached");
19254        assert!(
19255            body["error"]["message"].as_str().unwrap().contains("cap"),
19256            "the 402 must point at the KEY's cap, not tenant credit: {body}"
19257        );
19258        let events = mock.events();
19259        assert!(
19260            events.contains(&MeterEvent::Reserve {
19261                tenant: "acme".into(),
19262                principal: Some("mk-acme-testprefix00".into()),
19263                model: "qwen/qwen3.8-27b".into(),
19264            }),
19265            "the key prefix must reach reserve: {events:?}"
19266        );
19267    }
19268
19269    #[tokio::test]
19270    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19271    async fn streaming_client_disconnect_records_partial_usage_and_cost() {
19272        let _l = drain_lock();
19273        let mut st = fake_worker_state_with_steps(4, std::time::Duration::from_millis(100));
19274        let mock = MockMetering::admit_all();
19275        st.metering = Some(mock.clone());
19276
19277        let response = completions(
19278            State(st),
19279            HeaderMap::new(),
19280            None,
19281            Json(
19282                serde_json::from_value(json!({
19283                    "model": "m",
19284                    "prompt": "disconnect after one delta",
19285                    "stream": true,
19286                }))
19287                .unwrap(),
19288            ),
19289        )
19290        .await;
19291        assert_eq!(response.status(), StatusCode::OK);
19292        let request_id = response.headers()["x-request-id"]
19293            .to_str()
19294            .unwrap()
19295            .to_string();
19296        let mut body = Box::pin(response.into_body().into_data_stream());
19297        let first = std::future::poll_fn(|cx| body.as_mut().poll_next(cx))
19298            .await
19299            .expect("stream ended before first delta")
19300            .expect("stream body failed");
19301        assert!(
19302            is_sse_data_frame(&first),
19303            "first frame was not SSE data: {first:?}"
19304        );
19305        drop(body);
19306
19307        // The receipt died UNFINALIZED with the partial counts recorded — the
19308        // abandoned-client seam contract. Give the dropped stream a beat to unwind.
19309        let mut dropped = None;
19310        for _ in 0..500 {
19311            if let Some(event) = mock
19312                .events()
19313                .into_iter()
19314                .find(|e| matches!(e, MeterEvent::Dropped { .. }))
19315            {
19316                dropped = Some(event);
19317                break;
19318            }
19319            tokio::time::sleep(std::time::Duration::from_millis(5)).await;
19320        }
19321        let events = mock.events();
19322        assert!(
19323            events
19324                .iter()
19325                .any(|e| matches!(e, MeterEvent::Open { request_id: id, .. } if id == &request_id)),
19326            "the receipt was opened under the caller-visible request id: {events:?}"
19327        );
19328        assert_eq!(
19329            dropped,
19330            Some(MeterEvent::Dropped {
19331                prompt: 1,
19332                cached: 0,
19333                completion: 1,
19334            }),
19335            "a client disconnect must leave the partial counts on the dropped receipt \
19336             (the implementation prices that drop): {events:?}"
19337        );
19338    }
19339
19340    #[tokio::test]
19341    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19342    async fn draining_rejects_new_requests_with_503_and_retry_after() {
19343        let _l = drain_lock();
19344        let st = fake_worker_state();
19345        // RAII, not just the trailing reset below: a panic while the flag is up would
19346        // 503 every concurrently-running handler test (they read DRAINING lock-free).
19347        let _down = DrainingRestore;
19348        DRAINING.store(true, std::sync::atomic::Ordering::SeqCst);
19349        // both completion routes: immediate 503 + Retry-After, no slot held.
19350        let resp = chat_completions(
19351            State(st.clone()),
19352            axum::http::HeaderMap::new(),
19353            None,
19354            Json(
19355                serde_json::from_value(serde_json::json!({
19356                    "model": "m", "messages": [{"role": "user", "content": "t"}]
19357                }))
19358                .unwrap(),
19359            ),
19360        )
19361        .await;
19362        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
19363        // The drain 503 obeys the same retry contract as every taxonomy class: an integer
19364        // Retry-After <= 60, the retry-after-ms twin openai-python reads FIRST (its absence
19365        // was a real gap — a client trusting only the ms header saw NO window on memra's most
19366        // predictable outage), both agreeing, and a `code` clients can branch on.
19367        let ra = resp.headers()["retry-after"].to_str().unwrap().to_string();
19368        let ra_s: u64 = ra
19369            .parse()
19370            .expect("Retry-After must be integer delay-seconds");
19371        assert!(
19372            ra_s > 0 && ra_s <= 60,
19373            "Retry-After {ra_s}s is outside the honored window"
19374        );
19375        let ra_ms: u64 = resp.headers()["retry-after-ms"]
19376            .to_str()
19377            .unwrap()
19378            .parse()
19379            .unwrap();
19380        assert_eq!(ra_ms, ra_s * 1000, "the two retry headers must agree");
19381        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
19382            .await
19383            .unwrap();
19384        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19385        assert!(
19386            payload["error"]["message"]
19387                .as_str()
19388                .unwrap()
19389                .contains("draining")
19390        );
19391        assert_eq!(payload["error"]["type"], "server_error");
19392        assert_eq!(payload["error"]["code"], "draining");
19393        let resp = completions(
19394            State(st.clone()),
19395            axum::http::HeaderMap::new(),
19396            None,
19397            Json(
19398                serde_json::from_value(serde_json::json!({
19399                    "model": "m", "prompt": "t"
19400                }))
19401                .unwrap(),
19402            ),
19403        )
19404        .await;
19405        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
19406        assert!(resp.headers().contains_key("retry-after"));
19407        assert_eq!(
19408            st.inflight[0].load(std::sync::atomic::Ordering::SeqCst),
19409            0,
19410            "rejected requests must not hold slots"
19411        );
19412        // /health flips to "draining" but stays 200 — a drain is a HEALTHY shutdown, and 503
19413        // here would invite a supervisor to SIGKILL a process that is finishing streams.
19414        let resp = health_live(State(st.clone())).await.into_response();
19415        assert_eq!(
19416            resp.status(),
19417            StatusCode::OK,
19418            "a drain must not look like a liveness fault"
19419        );
19420        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
19421            .await
19422            .unwrap();
19423        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19424        assert_eq!(payload["status"], "draining");
19425        // Rotation is /readyz's job: unready while draining, so the LB stops sending.
19426        let resp = health_ready(State(st.clone())).await.into_response();
19427        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
19428        let retry_s = drain_deadline_s().clamp(1, 60);
19429        let retry_s_text = retry_s.to_string();
19430        let retry_ms_text = (retry_s * 1000).to_string();
19431        assert_eq!(retry_after(&resp).as_deref(), Some(retry_s_text.as_str()));
19432        assert_eq!(
19433            resp.headers().get("retry-after-ms").unwrap(),
19434            retry_ms_text.as_str()
19435        );
19436        assert_ne!(
19437            resp.headers()
19438                .get("x-should-retry")
19439                .and_then(|v| v.to_str().ok()),
19440            Some("false")
19441        );
19442        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
19443            .await
19444            .unwrap();
19445        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19446        assert_eq!(payload["status"], "not_ready");
19447        assert!(payload["detail"].as_str().unwrap().contains("draining"));
19448        DRAINING.store(false, std::sync::atomic::Ordering::SeqCst);
19449        // flag cleared: requests admit again (the gate is the flag, nothing latent).
19450        let resp = chat_completions(
19451            State(st.clone()),
19452            axum::http::HeaderMap::new(),
19453            None,
19454            Json(
19455                serde_json::from_value(serde_json::json!({
19456                    "model": "m", "messages": [{"role": "user", "content": "t"}]
19457                }))
19458                .unwrap(),
19459            ),
19460        )
19461        .await;
19462        assert_eq!(resp.status(), StatusCode::OK);
19463    }
19464
19465    // ---- G5: /health reports INFERENCE liveness, not process liveness -------------------
19466
19467    #[tokio::test]
19468    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19469    async fn health_is_green_only_while_the_worker_is_alive() {
19470        // /readyz reads the process-global DRAINING flag, which the drain test toggles —
19471        // serialize against it or this races (measured: an interleaved run saw 503 here).
19472        let _l = drain_lock();
19473        let st = fake_worker_state();
19474        // loaded + alive: 200 ok, and the payload explains WHY (phase + heartbeat age vs the
19475        // threshold), so an operator reading a green never has to guess.
19476        let resp = health_live(State(st.clone())).await.into_response();
19477        assert_eq!(resp.status(), StatusCode::OK);
19478        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
19479            .await
19480            .unwrap();
19481        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19482        assert_eq!(payload["status"], "ok");
19483        assert_eq!(payload["worker"]["phase"], "idle");
19484        assert!(payload["worker"]["stall_threshold_ms"].as_u64().unwrap() > 0);
19485        let ready = health_ready(State(st.clone())).await.into_response();
19486        assert_eq!(ready.status(), StatusCode::OK);
19487
19488        // THE REGRESSION THIS PINS. Kill inference the way a panic does — the health handle
19489        // is marked dead, the HTTP task keeps running, the process is entirely fine. The old
19490        // handler returned `{"status":"ok"}` here, forever, on a box answering nothing.
19491        st.health.mark_dead("worker thread panicked: test-injected");
19492        let resp = health_live(State(st.clone())).await.into_response();
19493        assert_eq!(
19494            resp.status(),
19495            StatusCode::SERVICE_UNAVAILABLE,
19496            "a dead worker MUST NOT report a healthy liveness"
19497        );
19498        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
19499            .await
19500            .unwrap();
19501        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19502        assert_eq!(payload["status"], "unhealthy");
19503        // the cause is QUOTED, not inferred — the panic text travels to the operator
19504        assert!(
19505            payload["detail"]
19506                .as_str()
19507                .unwrap()
19508                .contains("test-injected"),
19509            "cause not surfaced: {payload}"
19510        );
19511        let ready = health_ready(State(st.clone())).await.into_response();
19512        assert_eq!(
19513            ready.status(),
19514            StatusCode::SERVICE_UNAVAILABLE,
19515            "dead is also not ready"
19516        );
19517
19518        // Latency of the flip: a fault latch, not a timeout — no staleness threshold to wait
19519        // out, which is what makes this usable as a k8s livenessProbe.
19520        st.health.mark_ready();
19521        assert_eq!(
19522            health_live(State(st.clone()))
19523                .await
19524                .into_response()
19525                .status(),
19526            StatusCode::OK,
19527            "mark_ready must clear the latch (a successful respawn)"
19528        );
19529    }
19530
19531    #[tokio::test]
19532    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19533    async fn readyz_peer_probe_integrity_is_present_and_advisory() {
19534        let _l = drain_lock();
19535        let st = fake_worker_state();
19536
19537        let ready = health_ready(State(st.clone())).await.into_response();
19538        assert_eq!(ready.status(), StatusCode::OK);
19539        let bytes = axum::body::to_bytes(ready.into_body(), usize::MAX)
19540            .await
19541            .unwrap();
19542        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19543        assert_eq!(payload["peer_probe_integrity"], "ok");
19544
19545        st.health.note_peer_probe_deferral(2, false);
19546        let deferred = health_ready(State(st.clone())).await.into_response();
19547        assert_eq!(deferred.status(), StatusCode::OK);
19548        let bytes = axum::body::to_bytes(deferred.into_body(), usize::MAX)
19549            .await
19550            .unwrap();
19551        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19552        assert_eq!(payload["peer_probe_integrity"], "deferred_2");
19553
19554        st.health.note_peer_probe_deferral(4, true);
19555        let degraded = health_ready(State(st.clone())).await.into_response();
19556        assert_eq!(
19557            degraded.status(),
19558            StatusCode::OK,
19559            "peer degradation is advisory while plain serving remains healthy"
19560        );
19561        let bytes = axum::body::to_bytes(degraded.into_body(), usize::MAX)
19562            .await
19563            .unwrap();
19564        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19565        assert_eq!(payload["peer_probe_integrity"], "degraded");
19566
19567        st.health.mark_dead("test-injected worker failure");
19568        let unready = health_ready(State(st)).await.into_response();
19569        assert_eq!(unready.status(), StatusCode::SERVICE_UNAVAILABLE);
19570        let bytes = axum::body::to_bytes(unready.into_body(), usize::MAX)
19571            .await
19572            .unwrap();
19573        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19574        assert_eq!(
19575            payload["peer_probe_integrity"], "degraded",
19576            "the advisory field must also survive an unrelated readiness failure"
19577        );
19578    }
19579
19580    #[tokio::test]
19581    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19582    async fn liveness_failure_obeys_the_retry_contract() {
19583        // drain_lock() serializes AND resets the flag: health_live returns 200 ("draining")
19584        // whenever the process-global DRAINING flag is up, so any test asserting a
19585        // health_live 503 races the drain tests without it (the a_wedged flake, 2026-08-09
19586        // — schedule-dependent).
19587        let _l = drain_lock();
19588        let st = fake_worker_state();
19589        st.health
19590            .mark_dead("worker thread panicked: retry-contract-test");
19591
19592        let resp = health_live(State(st)).await.into_response();
19593        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
19594        assert_eq!(retry_after(&resp).as_deref(), Some("2"));
19595        assert_eq!(resp.headers().get("retry-after-ms").unwrap(), "2000");
19596        assert_ne!(
19597            resp.headers()
19598                .get("x-should-retry")
19599                .and_then(|v| v.to_str().ok()),
19600            Some("false")
19601        );
19602    }
19603
19604    #[tokio::test]
19605    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19606    async fn readiness_failure_obeys_the_retry_contract() {
19607        let _l = drain_lock();
19608        let st = fake_worker_state();
19609        st.health
19610            .mark_dead("worker thread panicked: retry-contract-test");
19611
19612        let resp = health_ready(State(st)).await.into_response();
19613        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
19614        assert_eq!(retry_after(&resp).as_deref(), Some("2"));
19615        assert_eq!(resp.headers().get("retry-after-ms").unwrap(), "2000");
19616        assert_ne!(
19617            resp.headers()
19618                .get("x-should-retry")
19619                .and_then(|v| v.to_str().ok()),
19620            Some("false")
19621        );
19622    }
19623
19624    #[tokio::test]
19625    #[allow(clippy::await_holding_lock)] // allow: DRAIN_LOCK serializes this test against its shared-state peers; holding across the awaits is the point
19626    async fn a_wedged_gpu_flips_health_even_though_the_worker_thread_is_fine() {
19627        // G24: Xid 119/120 hangs nvidia-smi and emits no Xid line; the watcher's probe
19628        // timeout is the alarm. The worker thread may still be looping (blocked in a driver
19629        // call), so the heartbeat alone would never catch this — the GPU latch does.
19630        //
19631        // drain_lock() serializes + resets (2026-08-09 flake): health_live short-circuits to
19632        // 200 ("draining") on the process-global DRAINING flag, so this test's 503 assertions
19633        // race the drain tests when tokio schedules them concurrently — it failed only in
19634        // full-suite runs, never solo, and the same suite on the identical commit passes or
19635        // fails by schedule. Same serialization the other drain-flag readers already take.
19636        let _l = drain_lock();
19637        let st = fake_worker_state();
19638        assert_eq!(
19639            health_live(State(st.clone()))
19640                .await
19641                .into_response()
19642                .status(),
19643            StatusCode::OK
19644        );
19645        st.health
19646            .mark_gpu_fault("nvidia-smi probe exceeded 10s deadline (GSP hang class)");
19647        let resp = health_live(State(st.clone())).await.into_response();
19648        assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
19649        let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
19650            .await
19651            .unwrap();
19652        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
19653        assert!(
19654            payload["detail"]
19655                .as_str()
19656                .unwrap()
19657                .contains("probe exceeded")
19658        );
19659        // A GPU fault survives mark_ready deliberately: a respawned worker on a wedged card
19660        // is not recovery, and only a fresh process (new CUDA context) can be.
19661        st.health.mark_ready();
19662        assert_eq!(
19663            health_live(State(st.clone()))
19664                .await
19665                .into_response()
19666                .status(),
19667            StatusCode::SERVICE_UNAVAILABLE,
19668            "a GPU fault must not be cleared by an in-process respawn"
19669        );
19670    }
19671
19672    #[test]
19673    fn v1_models_entry_keeps_catalog_shape_with_honest_nulls() {
19674        // KNOWN plan metadata populates every OR-schema field from worker truth.
19675        let caps = ModelCaps {
19676            tools_branch: true,
19677            hy3: false,
19678            qwen_think: true,
19679            think_switch: true,
19680            chat_ok: true,
19681            context_length: 262144,
19682            tokenizer: "qwen2".into(),
19683            instruct_type: Some("chatml".into()),
19684            effort_levels: false,
19685            qwen_effort: false,
19686            gemma_think: false,
19687            dsv4: false,
19688            glm5: false,
19689            chat_temperature_default: None,
19690            chat_top_p_default: None,
19691            n_vocab: 151_936,
19692            think_close: Vec::new(),
19693        };
19694        let e = model_entry_v1("main", Some(&caps), None);
19695        assert_eq!(e["id"], "main");
19696        assert_eq!(e["name"], "main");
19697        assert_eq!(e["object"], "model");
19698        assert_eq!(e["context_length"], 262144);
19699        // no metadata -> null prices (unpriced), no cache keys invented.
19700        assert!(e["pricing"]["input"].is_null());
19701        assert!(e["pricing"]["output"].is_null());
19702
19703        // METADATA present -> /v1/models advertises the SAME prices the ledger bills
19704        // (the launch bug: a priced, vision-serving endpoint reported "0" text-only).
19705        let meta = OpenRouterModelMetadata {
19706            pricing: OpenRouterPricing {
19707                prompt: Some("0.00000038".into()),
19708                cached_prompt: Some("0.0000002".into()),
19709                completion: Some("0.0000026".into()),
19710                ..Default::default()
19711            },
19712            input_modalities: vec!["image".into(), "video".into()],
19713            max_output_length: Some(32768),
19714            ..Default::default()
19715        };
19716        let e = model_entry_v1("main", Some(&caps), Some(&meta));
19717        // Contract-v2 pricing: per-1M string prices (decimal shift of the SAME metadata),
19718        // null cache_write (not configured), lifecycle default active, reliability defaults.
19719        assert_eq!(e["pricing"]["currency"], "USD");
19720        assert_eq!(e["pricing"]["unit"], "per_1m_tokens");
19721        assert_eq!(e["pricing"]["input"], "0.38");
19722        assert_eq!(e["pricing"]["output"], "2.60");
19723        assert_eq!(e["pricing"]["cached_input"], "0.20");
19724        assert!(e["pricing"]["cache_write"].is_null());
19725        assert_eq!(e["pricing"]["minimum_request"], "0");
19726        assert_eq!(e["owned_by"], "main");
19727        assert_eq!(e["type"], "chat");
19728        assert_eq!(e["max_output_tokens"], 32768);
19729        assert_eq!(e["endpoints"], json!(["chat/completions"]));
19730        assert_eq!(e["input_modalities"], json!(["text", "image", "video"]));
19731        assert_eq!(e["output_modalities"], json!(["text"]));
19732        assert_eq!(e["capabilities"]["streaming"], true);
19733        assert_eq!(e["capabilities"]["tools"], true);
19734        assert_eq!(e["lifecycle"]["status"], "active");
19735        assert!(e["lifecycle"]["deprecation_at"].is_null());
19736        assert_eq!(e["reliability"]["first_token_timeout_seconds"], 120);
19737        assert_eq!(e["reliability"]["capacity_scope"], "model_region");
19738        // EXACT key set — the contract forbids extra fields ("Do not design a custom
19739        // catalog"): no created, architecture, supported_parameters, top_provider, and
19740        // no legacy per-token pricing keys.
19741        let mut keys: Vec<&str> = e.as_object().unwrap().keys().map(String::as_str).collect();
19742        keys.sort_unstable();
19743        assert_eq!(
19744            keys,
19745            [
19746                "capabilities",
19747                "context_length",
19748                "endpoints",
19749                "id",
19750                "input_modalities",
19751                "lifecycle",
19752                "max_output_tokens",
19753                "name",
19754                "object",
19755                "output_modalities",
19756                "owned_by",
19757                "pricing",
19758                "reliability",
19759                "type",
19760            ],
19761            "unexpected /v1/models entry keys"
19762        );
19763        let mut price_keys: Vec<&str> = e["pricing"]
19764            .as_object()
19765            .unwrap()
19766            .keys()
19767            .map(String::as_str)
19768            .collect();
19769        price_keys.sort_unstable();
19770        assert_eq!(
19771            price_keys,
19772            [
19773                "cache_write",
19774                "cached_input",
19775                "currency",
19776                "input",
19777                "minimum_request",
19778                "output",
19779                "unit",
19780            ],
19781            "unexpected /v1/models pricing keys"
19782        );
19783
19784        // UNKNOWN metadata (no caps / empty fields) -> honest nulls, never invented.
19785        let e = model_entry_v1("m", None, None);
19786        assert!(e["context_length"].is_null());
19787        assert!(e["max_output_tokens"].is_null());
19788        let bare = ModelCaps::default(); // caps present, fields unknown (0/""/None)
19789        let e = model_entry_v1("m", Some(&bare), None);
19790        assert!(e["context_length"].is_null());
19791    }
19792
19793    /// 2026-08-28: qwen3-embedding-8b and qwen3-reranker-8b were published on
19794    /// /v1/models as `type: "chat"`, `endpoints: ["chat/completions"]`, with
19795    /// `tools: true` and `streaming: true`. Neither serves chat at all. A client SDK
19796    /// reading that row calls the wrong endpoint with the wrong body shape, so the
19797    /// declared surface — not a hardcoded literal — decides the row.
19798    #[test]
19799    fn catalog_row_follows_the_declared_surface() {
19800        let caps = ModelCaps {
19801            tools_branch: true,
19802            ..Default::default()
19803        };
19804
19805        let embed = OpenRouterModelMetadata {
19806            surface: Some("embedding".into()),
19807            max_output_length: Some(1),
19808            ..Default::default()
19809        };
19810        let e = model_entry_v1("qwen", Some(&caps), Some(&embed));
19811        assert_eq!(e["type"], "embedding");
19812        assert_eq!(e["endpoints"], json!(["embeddings"]));
19813        assert_eq!(e["output_modalities"], json!(["embeddings"]));
19814        assert_eq!(e["capabilities"]["streaming"], false);
19815        assert_eq!(
19816            e["capabilities"]["tools"], false,
19817            "an embedder has no tools"
19818        );
19819        assert_eq!(e["capabilities"]["reasoning"], false);
19820        assert_eq!(e["capabilities"]["structured_output"], false);
19821        assert_eq!(e["capabilities"]["prompt_caching"], false);
19822        assert!(
19823            e["max_output_tokens"].is_null(),
19824            "a surface that emits no completion tokens must not advertise a ceiling"
19825        );
19826
19827        let rerank = OpenRouterModelMetadata {
19828            surface: Some("rerank".into()),
19829            ..Default::default()
19830        };
19831        let r = model_entry_v1("qwen", Some(&caps), Some(&rerank));
19832        assert_eq!(r["type"], "rerank");
19833        assert_eq!(r["endpoints"], json!(["rerank"]));
19834        assert_eq!(r["output_modalities"], json!(["rerank"]));
19835        assert_eq!(r["capabilities"]["tools"], false);
19836        assert_eq!(r["capabilities"]["reasoning"], false);
19837
19838        // Absent surface stays chat, byte-for-byte with the pre-change row: every
19839        // existing deployment's models.toml omits the field.
19840        let chat = OpenRouterModelMetadata {
19841            max_output_length: Some(32768),
19842            ..Default::default()
19843        };
19844        let c = model_entry_v1("main", Some(&caps), Some(&chat));
19845        assert_eq!(c["type"], "chat");
19846        assert_eq!(c["endpoints"], json!(["chat/completions"]));
19847        assert_eq!(c["output_modalities"], json!(["text"]));
19848        assert_eq!(c["capabilities"]["tools"], true);
19849        assert_eq!(c["max_output_tokens"], 32768);
19850    }
19851
19852    /// The surface is a published contract, so a typo must fail the config load
19853    /// rather than silently publishing a chat row for an embedder.
19854    #[test]
19855    fn unknown_surface_is_rejected_at_config_load() {
19856        let bad = OpenRouterModelMetadata {
19857            surface: Some("embeddings".into()), // plural: the near-miss typo
19858            ..Default::default()
19859        };
19860        let err = validate_openrouter_metadata("qwen/qwen3-embedding-8b", &bad)
19861            .expect_err("an unknown surface must not load");
19862        assert!(err.contains("surface"), "{err}");
19863
19864        for good in ["chat", "embedding", "rerank"] {
19865            let ok = OpenRouterModelMetadata {
19866                surface: Some(good.into()),
19867                ..Default::default()
19868            };
19869            assert!(
19870                validate_openrouter_metadata("m", &ok).is_ok(),
19871                "{good} must load"
19872            );
19873        }
19874    }
19875
19876    #[test]
19877    fn per_million_price_is_exact_decimal_shift() {
19878        // The live prices: per-token strings -> per-1M contract strings, no floats anywhere.
19879        assert_eq!(per_million_price("0.00000038").as_deref(), Some("0.38"));
19880        assert_eq!(per_million_price("0.0000026").as_deref(), Some("2.60"));
19881        assert_eq!(per_million_price("0.0000002").as_deref(), Some("0.20"));
19882        assert_eq!(per_million_price("0").as_deref(), Some("0.00"));
19883        assert_eq!(per_million_price("1.5").as_deref(), Some("1500000.00"));
19884        assert_eq!(per_million_price("0.000000125").as_deref(), Some("0.125"));
19885        assert_eq!(per_million_price("not-a-price"), None);
19886        assert_eq!(per_million_price(""), None);
19887    }
19888
19889    #[test]
19890    fn metadata_provider_block_parses_and_validates() {
19891        let (_, provider) = OpenRouterMetadataFile::parse(
19892            r#"
19893            [provider]
19894            id = "tiyuvta"
19895            status_url = "https://status.tiyuvta.ai"
19896            support_contact = "mailto:support@tiyuvta.ai"
19897            incident_contact = "mailto:incidents@tiyuvta.ai"
19898            regions = ["eu-central"]
19899            "#,
19900        )
19901        .unwrap();
19902        let provider = provider.unwrap();
19903        assert_eq!(provider.id, "tiyuvta");
19904        assert_eq!(provider.regions, vec!["eu-central"]);
19905        // empty id refuses at boot, not at request time
19906        let err = OpenRouterMetadataFile::parse("[provider]\nid = \"\"\n").unwrap_err();
19907        assert!(err.contains("provider.id"), "{err}");
19908        // a bare email is not a URI — the contract wants mailto:/https: schemes
19909        let err = OpenRouterMetadataFile::parse(
19910            "[provider]\nid = \"x\"\nsupport_contact = \"ops@example.com\"\n",
19911        )
19912        .unwrap_err();
19913        assert!(err.contains("must be a URI"), "{err}");
19914        // absent block is not an error
19915        let (_, provider) = OpenRouterMetadataFile::parse("").unwrap();
19916        assert!(provider.is_none());
19917    }
19918
19919    #[test]
19920    fn models_openai_default_body_stays_byte_identical() {
19921        let body = models_openai_body(&["main".into(), "judge".into()]);
19922        let bytes = serde_json::to_vec(&body).unwrap();
19923        assert_eq!(
19924            bytes,
19925            br#"{"object":"list","data":[{"id":"main","object":"model"},{"id":"judge","object":"model"}]}"#
19926        );
19927    }
19928
19929    #[test]
19930    fn canonical_model_id_tolerates_a_marketplace_stripping_the_vendor_prefix() {
19931        // The exact live failure: Onlist listed qwen/qwen3.6-35b-a3b and probed for the bare name.
19932        let loaded = vec![
19933            "qwen/qwen3.6-27b".to_string(),
19934            "qwen/qwen3.6-35b-a3b".to_string(),
19935        ];
19936        assert_eq!(
19937            canonical_model_id(&loaded, "qwen3.6-35b-a3b").as_deref(),
19938            Some("qwen/qwen3.6-35b-a3b"),
19939        );
19940        assert_eq!(
19941            canonical_model_id(&loaded, "qwen3.6-27b").as_deref(),
19942            Some("qwen/qwen3.6-27b"),
19943        );
19944        // An exact alias must keep resolving to itself, unchanged.
19945        assert_eq!(
19946            canonical_model_id(&loaded, "qwen/qwen3.6-35b-a3b").as_deref(),
19947            Some("qwen/qwen3.6-35b-a3b"),
19948        );
19949        // A genuinely unknown id stays unknown, so the worker still emits model_not_found.
19950        assert_eq!(canonical_model_id(&loaded, "gpt-4o"), None);
19951        assert_eq!(canonical_model_id(&loaded, "vendor/qwen3.6-35b-a3b"), None);
19952        assert_eq!(canonical_model_id(&loaded, ""), None);
19953    }
19954
19955    #[test]
19956    fn canonical_model_id_refuses_an_ambiguous_suffix_rather_than_guessing() {
19957        // Two vendors publishing the same model name must NOT be silently disambiguated: routing to
19958        // the wrong weights would also bill under the wrong model's price schedule.
19959        let loaded = vec!["a/shared-name".to_string(), "b/shared-name".to_string()];
19960        assert_eq!(canonical_model_id(&loaded, "shared-name"), None);
19961        // Each exact id still resolves.
19962        assert_eq!(
19963            canonical_model_id(&loaded, "a/shared-name").as_deref(),
19964            Some("a/shared-name")
19965        );
19966        assert_eq!(
19967            canonical_model_id(&loaded, "b/shared-name").as_deref(),
19968            Some("b/shared-name")
19969        );
19970        // An unprefixed alias is matched exactly, not by suffix games.
19971        let bare = vec!["solo".to_string()];
19972        assert_eq!(canonical_model_id(&bare, "solo").as_deref(), Some("solo"));
19973    }
19974
19975    #[test]
19976    fn openrouter_models_entry_serializes_complete_metadata() {
19977        let metadata = OpenRouterMetadataFile::from_toml(
19978            r#"
19979[models.main]
19980hugging_face_id = "Qwen/Qwen3.6-27B"
19981created = 1786032000
19982quantization = "nvfp4"
19983description = "Qwen3.6 27B served by memra."
19984max_prompt_length = 245760
19985max_output_length = 16384
19986default_output_length = 4096
19987is_ready = true
19988is_free = false
19989discount_to_user = 0.1
19990openrouter_slug = "qwen/qwen3.6-27b"
19991datacenters = [{ country_code = "US", region = "us-east" }]
19992zdr = true
19993hipaa = false
19994
19995[models.main.pricing]
19996prompt = "0.000000234"
19997cached_prompt = "0.0000000585"
19998cache_write = "0.000000234"
19999completion = "0.000001872"
20000internal_reasoning = "0.000001872"
20001request = "0.01"
20002
20003[models.main.capacity]
20004prompt_tpm = 1000000
20005cached_prompt_tpm = 2000000
20006completion_tpm = 500000
20007request_rpm = 1000
20008concurrency = 64
20009"#,
20010        )
20011        .unwrap();
20012        let caps = ModelCaps {
20013            tools_branch: true,
20014            qwen_think: true,
20015            think_switch: true,
20016            chat_ok: true,
20017            context_length: 262144,
20018            tokenizer: "qwen2".into(),
20019            instruct_type: Some("chatml".into()),
20020            ..Default::default()
20021        };
20022        let entry = model_entry_openrouter("main", Some(&caps), metadata.get("main"));
20023
20024        assert_eq!(entry["schema_version"], "2.4");
20025        assert_eq!(entry["id"], "main");
20026        assert_eq!(entry["name"], "main");
20027        assert_eq!(entry["hugging_face_id"], "Qwen/Qwen3.6-27B");
20028        assert_eq!(entry["created"], 1786032000u64);
20029        assert_eq!(entry["quantization"], "nvfp4");
20030        assert_eq!(entry["tokenizer"], "qwen2");
20031        assert_eq!(entry["description"], "Qwen3.6 27B served by memra.");
20032        assert!(
20033            entry.get("object").is_none(),
20034            "OpenRouter schema 2.4 rejects unknown OpenAI fields"
20035        );
20036
20037        let input = &entry["input_modalities"][0];
20038        assert_eq!(input["type"], "text");
20039        assert_eq!(
20040            input["supported_inputs"]["max_context_length"]["value"],
20041            262144
20042        );
20043        assert_eq!(
20044            input["supported_inputs"]["max_prompt_length"]["value"],
20045            245760
20046        );
20047        let input_prices = input["pricing"].as_array().unwrap();
20048        let input_price = |kind: &str| {
20049            input_prices
20050                .iter()
20051                .find(|price| price["type"] == kind)
20052                .unwrap()
20053        };
20054        assert_eq!(input_price("prompt")["cost_usd"], "0.000000234");
20055        assert_eq!(input_price("cached_prompt")["cost_usd"], "0.0000000585");
20056        assert_eq!(input_price("cache_write")["cost_usd"], "0.000000234");
20057        assert_eq!(input["capacity"][0]["value"], 1000000);
20058        assert_eq!(input["capacity"][1]["value"], 2000000);
20059
20060        let output = &entry["output_modalities"][0];
20061        assert_eq!(output["type"], "text");
20062        assert_eq!(output["max_length"]["value"], 16384);
20063        assert_eq!(output["streaming"], true);
20064        assert_eq!(output["supported_parameters"]["tools"]["type"], "boolean");
20065        assert_eq!(
20066            output["supported_parameters"]["structured_outputs"]["type"],
20067            "boolean"
20068        );
20069        assert_eq!(
20070            output["supported_parameters"]["reasoning"]["type"],
20071            "boolean"
20072        );
20073        assert_eq!(output["pricing"][0]["type"], "completion");
20074        assert_eq!(output["pricing"][0]["cost_usd"], "0.000001872");
20075        assert_eq!(output["pricing"][1]["type"], "internal_reasoning");
20076        assert_eq!(output["capacity"][0]["value"], 500000);
20077        assert_eq!(output["capacity"][1]["type"], "concurrency");
20078        assert_eq!(output["capacity"][1]["value"], 64);
20079
20080        assert_eq!(entry["pricing"][0]["type"], "request");
20081        assert_eq!(entry["pricing"][0]["cost_usd"], "0.01");
20082        assert_eq!(entry["capacity"][0]["value"], 1000);
20083        assert_eq!(entry["is_ready"], true);
20084        assert_eq!(entry["is_free"], false);
20085        assert_eq!(entry["discount_to_user"], 0.1);
20086        assert_eq!(entry["openrouter"]["slug"], "qwen/qwen3.6-27b");
20087        assert_eq!(entry["datacenters"][0]["country_code"], "US");
20088        assert_eq!(entry["compliance"]["zdr"], true);
20089        assert_eq!(entry["compliance"]["hipaa"], false);
20090    }
20091
20092    /// The deploy registry moved to the private operations repo (owner boundary call,
20093    /// 2026-08-16); the SHAPE these tests pin is engine contract, so they keep a local
20094    /// fixture with the same staged/active structure and the same values the assertions
20095    /// below already publish.
20096    const GATEWAY_REGISTRY_FIXTURE: &str = r#"
20097[models."qwen/qwen3.6-35b-a3b"]
20098hugging_face_id = "Qwen/Qwen3.6-35B-A3B"
20099created = 1777260255
20100quantization = "int4"
20101description = "Qwen3.6 35B-A3B fixture entry."
20102max_prompt_length = 262144
20103max_output_length = 262144
20104default_output_length = 8192
20105is_ready = true
20106is_free = false
20107discount_to_user = 0.0
20108openrouter_slug = "qwen/qwen3.6-35b-a3b"
20109zdr = false
20110hipaa = false
20111
20112[[models."qwen/qwen3.6-35b-a3b".datacenters]]
20113country_code = "CA"
20114region = "Ontario"
20115
20116[models."qwen/qwen3.6-35b-a3b".pricing]
20117prompt = "0.0000000931"
20118cached_prompt = "0.0000000652"
20119completion = "0.0000009025"
20120
20121[models."qwen/qwen3.6-35b-a3b".capacity]
20122prompt_tpm = 780000
20123cached_prompt_tpm = 310000
20124completion_tpm = 9600
20125request_rpm = 160
20126concurrency = 16
20127
20128[planned_models."qwen/qwen3.8-27b"]
20129description = "Planned fixture entry; must never be emitted."
20130max_prompt_length = 262144
20131max_output_length = 262144
20132default_output_length = 8192
20133is_ready = false
20134is_free = false
20135discount_to_user = 0.0
20136openrouter_slug = "qwen/qwen3.8-27b"
20137zdr = false
20138hipaa = false
20139
20140[planned_models."qwen/qwen3.8-27b".pricing]
20141prompt = "0.0000002745"
20142cached_prompt = "0.0000001922"
20143completion = "0.0000022800"
20144
20145[planned_models."google/gemma-4-26b-a4b-it"]
20146hugging_face_id = "google/gemma-4-26B-A4B-it"
20147created = 1775227989
20148quantization = "int4"
20149description = "Planned fixture entry; must never be emitted."
20150max_prompt_length = 262144
20151max_output_length = 262144
20152default_output_length = 8192
20153is_ready = false
20154is_free = false
20155discount_to_user = 0.0
20156openrouter_slug = "google/gemma-4-26b-a4b-it"
20157zdr = false
20158hipaa = false
20159
20160[planned_models."google/gemma-4-26b-a4b-it".pricing]
20161prompt = "0.0000000665"
20162cached_prompt = "0.0000000466"
20163completion = "0.0000003230"
20164"#;
20165
20166    #[test]
20167    fn gateway_registry_generates_the_staged_active_shape() {
20168        let metadata = OpenRouterMetadataFile::from_toml(GATEWAY_REGISTRY_FIXTURE).unwrap();
20169        let caps = ModelCaps {
20170            tools_branch: true,
20171            qwen_think: true,
20172            think_switch: true,
20173            chat_ok: true,
20174            context_length: 262144,
20175            tokenizer: "qwen2".into(),
20176            instruct_type: Some("chatml".into()),
20177            ..Default::default()
20178        };
20179        let q35_entry = model_entry_openrouter(
20180            "qwen/qwen3.6-35b-a3b",
20181            Some(&caps),
20182            metadata.get("qwen/qwen3.6-35b-a3b"),
20183        );
20184        assert_eq!(q35_entry["created"], 1777260255u64);
20185        assert_eq!(q35_entry["quantization"], "int4");
20186        assert_eq!(q35_entry["is_ready"], true);
20187        assert_eq!(
20188            q35_entry["input_modalities"][0]["supported_inputs"]["max_context_length"]["value"],
20189            262144
20190        );
20191        assert_eq!(
20192            q35_entry["input_modalities"][0]["supported_inputs"]["max_prompt_length"]["value"],
20193            262144
20194        );
20195        assert_eq!(
20196            q35_entry["output_modalities"][0]["max_length"]["value"],
20197            262144
20198        );
20199        let prices = q35_entry["input_modalities"][0]["pricing"]
20200            .as_array()
20201            .unwrap();
20202        assert_eq!(prices[0]["cost_usd"], "0.0000000931");
20203        assert_eq!(prices[1]["cost_usd"], "0.0000000652");
20204        // Capacity is the MEASURED sold-shape floor (2026-08-13, research/canonflip-20260813):
20205        // 4,860-token prompt + 60 output, single RTX PRO 6000 WS. These five move together and
20206        // only with a measurement — see the comment block in deploy/gateway/q27-models.toml.
20207        assert_eq!(
20208            q35_entry["input_modalities"][0]["capacity"][0]["value"],
20209            780000
20210        );
20211        assert_eq!(
20212            q35_entry["input_modalities"][0]["capacity"][1]["value"],
20213            310000
20214        );
20215        assert_eq!(
20216            q35_entry["output_modalities"][0]["supported_parameters"]["max_tokens"]["max"],
20217            262144
20218        );
20219        assert_eq!(
20220            q35_entry["output_modalities"][0]["capacity"][0]["value"],
20221            9600
20222        );
20223        assert_eq!(
20224            q35_entry["output_modalities"][0]["capacity"][1]["value"],
20225            16
20226        );
20227        assert_eq!(
20228            q35_entry["output_modalities"][0]["pricing"][0]["cost_usd"],
20229            "0.0000009025"
20230        );
20231        assert_eq!(q35_entry["capacity"][0]["value"], 160); // request_rpm, sold-shape floor
20232        assert_eq!(q35_entry["datacenters"][0]["country_code"], "CA");
20233
20234        assert_eq!(
20235            metadata.len(),
20236            1,
20237            "planned models must never enter the active map"
20238        );
20239        assert!(!metadata.contains_key("qwen/qwen3.6-27b"));
20240        assert!(!metadata.contains_key("qwen/qwen3.8-27b"));
20241        assert!(!metadata.contains_key("google/gemma-4-26b-a4b-it"));
20242
20243        let openmodels = model_entry_openmodels(
20244            "qwen/qwen3.6-35b-a3b",
20245            Some(&caps),
20246            metadata.get("qwen/qwen3.6-35b-a3b"),
20247        )
20248        .unwrap();
20249        assert_eq!(openmodels["currency"], "USD");
20250        assert_eq!(openmodels["max_output_length"], 262144);
20251        assert_eq!(openmodels["is_ready"], true);
20252        assert_eq!(openmodels["is_free"], false);
20253        assert_eq!(openmodels["discount_to_user"], 0.0);
20254    }
20255
20256    #[test]
20257    fn gateway_registry_limits_are_live_request_limits() {
20258        let metadata_file = OpenRouterMetadataFile::from_toml(GATEWAY_REGISTRY_FIXTURE).unwrap();
20259        let metadata = metadata_file.get("qwen/qwen3.6-35b-a3b").unwrap();
20260        let caps = ModelCaps {
20261            context_length: 262_144,
20262            ..Default::default()
20263        };
20264        let build = |value: serde_json::Value| {
20265            let req: CompletionReq = serde_json::from_value(value).unwrap();
20266            let (tx, _rx) = worker::event_channel();
20267            build_request(&req, tx, lanes::Lane::Interactive, None)
20268        };
20269
20270        let mut omitted = build(json!({
20271            "model": "qwen/qwen3.6-35b-a3b",
20272            "prompt_ids": [1, 2, 3]
20273        }));
20274        apply_model_request_limits(&mut omitted, Some(metadata), Some(&caps)).unwrap();
20275        assert_eq!(omitted.params.max_new, 8_192);
20276        assert_eq!(omitted.max_prompt_tokens, Some(262_144));
20277
20278        let mut field_top = build(json!({
20279            "model": "qwen/qwen3.6-35b-a3b",
20280            "prompt_ids": [1],
20281            "max_tokens": 262144
20282        }));
20283        apply_model_request_limits(&mut field_top, Some(metadata), Some(&caps)).unwrap();
20284        assert_eq!(field_top.params.max_new, 262_144);
20285        assert_eq!(
20286            budget_completion_bound(&field_top, 100, Some(&caps)).unwrap(),
20287            262_044,
20288            "the field-top output request is accepted but bounded by remaining trained context",
20289        );
20290
20291        let mut too_much_output = build(json!({
20292            "model": "qwen/qwen3.6-35b-a3b",
20293            "prompt_ids": [1],
20294            "max_tokens": 262145
20295        }));
20296        let (message, param) =
20297            apply_model_request_limits(&mut too_much_output, Some(metadata), Some(&caps))
20298                .unwrap_err();
20299        assert_eq!(param, "max_tokens");
20300        assert!(message.contains("262145"));
20301
20302        let mut oversized_allocation = build(json!({
20303            "model": "qwen/qwen3.6-35b-a3b",
20304            "prompt_ids": [1],
20305            "max_tokens": 1,
20306            "max_ctx": 262145
20307        }));
20308        let (_, param) =
20309            apply_model_request_limits(&mut oversized_allocation, Some(metadata), Some(&caps))
20310                .unwrap_err();
20311        assert_eq!(param, "max_ctx");
20312    }
20313
20314    #[test]
20315    fn planned_registry_entries_are_validated_but_never_activated() {
20316        let parsed = OpenRouterMetadataFile::from_toml(
20317            r#"
20318[planned_models.future]
20319max_output_length = 262144
20320default_output_length = 8192
20321
20322[planned_models.future.pricing]
20323prompt = "0.0000001"
20324"#,
20325        )
20326        .unwrap();
20327        assert!(parsed.is_empty());
20328
20329        let error = OpenRouterMetadataFile::from_toml(
20330            r#"
20331[planned_models.future]
20332default_output_length = 8192
20333"#,
20334        )
20335        .unwrap_err();
20336        assert!(error.contains("requires max_output_length"));
20337    }
20338
20339    /// The reviewer's catch on PR #61: gating only /v1/models would have left the
20340    /// two feeds the SITE and llms.txt advertise publishing the same wrong contract
20341    /// for the same model. All three feeds resolve the surface through
20342    /// `declared_surface`, so they cannot disagree.
20343    #[test]
20344    fn every_catalog_feed_honours_the_declared_surface() {
20345        let metadata = OpenRouterMetadataFile::from_toml(
20346            r#"
20347[models."qwen/qwen3-embedding-8b"]
20348surface = "embedding"
20349created = 1787961600
20350max_output_length = 1
20351is_ready = true
20352is_free = false
20353discount_to_user = 0.0
20354
20355[models."qwen/qwen3-embedding-8b".pricing]
20356prompt = "0.00000001"
20357cached_prompt = "0.0"
20358completion = "0.0"
20359
20360[models."main"]
20361created = 1787443200
20362max_output_length = 32768
20363is_ready = true
20364is_free = false
20365discount_to_user = 0.0
20366
20367[models."main".pricing]
20368prompt = "0.00000025"
20369cached_prompt = "0.00000009"
20370completion = "0.0000012"
20371"#,
20372        )
20373        .unwrap();
20374        let caps = ModelCaps {
20375            tools_branch: true,
20376            qwen_think: true,
20377            // A switchless thinker (GLM-5.3-Flash, step35) legitimately advertises no
20378            // structured output — the grammar can never close the unconditional <think>
20379            // tail. This fixture is the SERVED shape: a qwen with the enable_thinking
20380            // switch, which honours response_format, so the chat assertions below stand.
20381            think_switch: true,
20382            chat_ok: true,
20383            context_length: 32768,
20384            ..Default::default()
20385        };
20386        let embed = metadata.get("qwen/qwen3-embedding-8b");
20387        let chat = metadata.get("main");
20388
20389        // /models?schema=openrouter — the feed the site and llms.txt advertise
20390        let or = model_entry_openrouter("qwen/qwen3-embedding-8b", Some(&caps), embed);
20391        let out = &or["output_modalities"][0];
20392        assert_eq!(out["type"], "embeddings", "openrouter feed: {or}");
20393        assert!(
20394            out.get("streaming").is_none(),
20395            "the embeddings branch declares no streaming property (additionalProperties:false): {out}"
20396        );
20397        // EVERY completion-request field is absent, not just tools/reasoning:
20398        // /v1/embeddings takes {input, dimensions, encoding_format} and nothing here.
20399        // Publishing max_tokens/structured_outputs for an embedder would contradict
20400        // /v1/models, which reports structured_output=false for the same model.
20401        let params = &out["supported_parameters"];
20402        assert_eq!(
20403            params.as_object().map(|o| o.len()),
20404            Some(0),
20405            "no completion parameter belongs on an embedder row: {params}"
20406        );
20407        for field in [
20408            "tools",
20409            "tool_choice",
20410            "reasoning",
20411            "max_tokens",
20412            "json_mode",
20413            "structured_outputs",
20414            "stop",
20415            "temperature",
20416            "seed",
20417        ] {
20418            assert!(params[field].is_null(), "{field} leaked onto an embedder");
20419        }
20420        assert!(
20421            out["max_length"].is_null(),
20422            "a surface emitting no completion tokens advertises no ceiling: {out}"
20423        );
20424
20425        // /models?schema=openmodels
20426        let om = model_entry_openmodels("qwen/qwen3-embedding-8b", Some(&caps), embed)
20427            .expect("openmodels entry builds");
20428        assert_eq!(om["output_modalities"], json!(["embeddings"]));
20429        let features = om["supported_features"].as_array().unwrap();
20430        assert!(
20431            !features
20432                .iter()
20433                .any(|f| f == "tool_calling" || f == "reasoning"),
20434            "chat-only features leaked onto an embedder: {features:?}"
20435        );
20436
20437        // /v1/models — the surface this change started from
20438        let v1 = model_entry_v1("qwen/qwen3-embedding-8b", Some(&caps), embed);
20439        assert_eq!(v1["type"], "embedding");
20440        assert_eq!(v1["capabilities"]["tools"], false);
20441
20442        // and a chat model keeps every chat affordance on all three
20443        let or_chat = model_entry_openrouter("main", Some(&caps), chat);
20444        let out_chat = &or_chat["output_modalities"][0];
20445        assert_eq!(out_chat["type"], "text");
20446        assert_eq!(out_chat["streaming"], true);
20447        assert!(!out_chat["supported_parameters"]["tools"].is_null());
20448        assert!(!out_chat["supported_parameters"]["max_tokens"].is_null());
20449        assert!(!out_chat["supported_parameters"]["structured_outputs"].is_null());
20450        assert_eq!(out_chat["max_length"]["value"], 32768u64);
20451        let om_chat = model_entry_openmodels("main", Some(&caps), chat).expect("chat entry builds");
20452        assert_eq!(om_chat["output_modalities"], json!(["text"]));
20453        assert!(
20454            om_chat["supported_features"]
20455                .as_array()
20456                .unwrap()
20457                .iter()
20458                .any(|f| f == "tool_calling")
20459        );
20460        assert_eq!(model_entry_v1("main", Some(&caps), chat)["type"], "chat");
20461    }
20462
20463    /// The values on the openrouter feed are NOT ours to choose: they must match the
20464    /// Provider Monitor 2.4 schema this feed stamps itself with. Round 3 of review #61
20465    /// caught `embedding`/`score`/`streaming:false` — all invented by analogy with the
20466    /// text modality, all rejected by the vendored schema's closed `OutputModality`
20467    /// oneOf. This test reads that pinned file, so the next invented value fails here
20468    /// instead of in a provider's validator.
20469    #[test]
20470    fn openrouter_output_modality_matches_the_vendored_2_4_schema() {
20471        let raw = std::fs::read_to_string(concat!(
20472            env!("CARGO_MANIFEST_DIR"),
20473            "/../../research/gateway-20260812/raw/sources/",
20474            "openrouter-provider-schema-v2.4-20260812.json"
20475        ))
20476        .expect("vendored Provider Monitor 2.4 schema is in-tree");
20477        let schema: serde_json::Value = serde_json::from_str(&raw).expect("schema parses");
20478        let branches = schema["components"]["schemas"]["OutputModality"]["oneOf"]
20479            .as_array()
20480            .expect("OutputModality is a oneOf");
20481
20482        let metadata = OpenRouterMetadataFile::from_toml(
20483            r#"
20484[models."embed"]
20485surface = "embedding"
20486created = 1787961600
20487max_output_length = 1
20488is_ready = true
20489is_free = false
20490discount_to_user = 0.0
20491
20492[models."embed".pricing]
20493prompt = "0.00000001"
20494cached_prompt = "0.0"
20495completion = "0.0"
20496
20497[models."rr"]
20498surface = "rerank"
20499created = 1787961600
20500max_output_length = 1
20501is_ready = true
20502is_free = false
20503discount_to_user = 0.0
20504
20505[models."rr".pricing]
20506prompt = "0.00000003"
20507cached_prompt = "0.0"
20508completion = "0.0"
20509
20510[models."chatty"]
20511created = 1787443200
20512max_output_length = 32768
20513is_ready = true
20514is_free = false
20515discount_to_user = 0.0
20516
20517[models."chatty".pricing]
20518prompt = "0.00000025"
20519cached_prompt = "0.00000009"
20520completion = "0.0000012"
20521"#,
20522        )
20523        .unwrap();
20524        let caps = ModelCaps {
20525            tools_branch: true,
20526            qwen_think: true,
20527            chat_ok: true,
20528            context_length: 32768,
20529            ..Default::default()
20530        };
20531
20532        for (alias, want_type) in [
20533            ("embed", "embeddings"),
20534            ("rr", "rerank"),
20535            ("chatty", "text"),
20536        ] {
20537            let row = model_entry_openrouter(alias, Some(&caps), metadata.get(alias));
20538            let modality = &row["output_modalities"][0];
20539            assert_eq!(modality["type"], want_type, "{alias}: {row}");
20540
20541            // exactly one branch may accept this type, and it must accept every key we emit
20542            let branch = branches
20543                .iter()
20544                .find(|b| b["properties"]["type"]["enum"][0] == want_type)
20545                .unwrap_or_else(|| panic!("{want_type:?} is not an OutputModality branch"));
20546            let allowed: std::collections::BTreeSet<&str> = branch["properties"]
20547                .as_object()
20548                .expect("branch properties")
20549                .keys()
20550                .map(String::as_str)
20551                .collect();
20552            for key in modality.as_object().expect("modality object").keys() {
20553                assert!(
20554                    allowed.contains(key.as_str()),
20555                    "{alias}: {key:?} is not a property of the {want_type:?} branch \
20556                     (additionalProperties:false); allowed = {allowed:?}"
20557                );
20558            }
20559            for req in branch["required"].as_array().into_iter().flatten() {
20560                let req = req.as_str().expect("required entry is a string");
20561                assert!(
20562                    modality.get(req).is_some(),
20563                    "{alias}: required property {req:?} missing from the {want_type:?} branch"
20564                );
20565            }
20566        }
20567    }
20568
20569    #[test]
20570    fn openrouter_models_entry_omits_undeclared_optional_fields() {
20571        let entry = model_entry_openrouter("minimal", None, None);
20572        let object = entry.as_object().unwrap();
20573        for field in [
20574            "hugging_face_id",
20575            "created",
20576            "quantization",
20577            "tokenizer",
20578            "description",
20579            "pricing",
20580            "capacity",
20581            "is_ready",
20582            "is_free",
20583            "discount_to_user",
20584            "openrouter",
20585            "datacenters",
20586            "compliance",
20587        ] {
20588            assert!(
20589                !object.contains_key(field),
20590                "optional field {field} must be absent, not null"
20591            );
20592        }
20593        assert_eq!(entry["schema_version"], "2.4");
20594        assert_eq!(entry["input_modalities"][0]["type"], "text");
20595        assert!(
20596            entry["input_modalities"][0]
20597                .get("supported_inputs")
20598                .is_none()
20599        );
20600        assert!(entry["input_modalities"][0].get("pricing").is_none());
20601        assert!(entry["input_modalities"][0].get("capacity").is_none());
20602        assert_eq!(entry["output_modalities"][0]["type"], "text");
20603        assert_eq!(entry["output_modalities"][0]["streaming"], true);
20604        assert!(entry["output_modalities"][0]["supported_parameters"].is_object());
20605        assert!(entry["output_modalities"][0].get("max_length").is_none());
20606        assert!(entry["output_modalities"][0].get("pricing").is_none());
20607        assert!(entry["output_modalities"][0].get("capacity").is_none());
20608    }
20609
20610    #[test]
20611    fn openmodels_entry_serializes_standard_provider_shape() {
20612        let metadata = OpenRouterMetadataFile::from_toml(
20613            r#"
20614[models."qwen/qwen3.6-27b"]
20615created = 1786032000
20616max_output_length = 16384
20617is_ready = true
20618is_free = false
20619discount_to_user = 0.05
20620
20621[models."qwen/qwen3.6-27b".pricing]
20622prompt = "0.000000291"
20623cached_prompt = "0.000000291"
20624completion = "0.000002763"
20625request = "0"
20626"#,
20627        )
20628        .unwrap();
20629        let caps = ModelCaps {
20630            tools_branch: true,
20631            qwen_think: true,
20632            chat_ok: true,
20633            context_length: 262144,
20634            ..Default::default()
20635        };
20636        let entry = model_entry_openmodels(
20637            "qwen/qwen3.6-27b",
20638            Some(&caps),
20639            metadata.get("qwen/qwen3.6-27b"),
20640        )
20641        .unwrap();
20642
20643        assert_eq!(entry["id"], "qwen/qwen3.6-27b");
20644        assert_eq!(entry["name"], "qwen/qwen3.6-27b");
20645        assert_eq!(entry["created"], 1786032000u64);
20646        assert_eq!(entry["input_modalities"], json!(["text"]));
20647        assert_eq!(entry["output_modalities"], json!(["text"]));
20648        assert_eq!(entry["context_length"], 262144u64);
20649        assert_eq!(entry["max_output_length"], 16384u64);
20650        assert_eq!(entry["currency"], "USD");
20651        assert_eq!(entry["pricing"]["prompt"], "0.000000291");
20652        assert_eq!(entry["pricing"]["completion"], "0.000002763");
20653        assert_eq!(entry["pricing"]["input_cache_read"], "0.000000291");
20654        assert_eq!(entry["pricing"]["request"], "0");
20655        assert_eq!(
20656            entry["supported_features"],
20657            json!(["tool_calling", "reasoning"])
20658        );
20659        assert_eq!(entry["is_ready"], true);
20660        assert_eq!(entry["is_free"], false);
20661        assert_eq!(entry["discount_to_user"], 0.05);
20662        assert!(entry.get("schema_version").is_none());
20663        assert!(entry.get("quantization").is_none());
20664    }
20665
20666    #[test]
20667    fn openmodels_entry_rejects_missing_operator_metadata() {
20668        let caps = ModelCaps {
20669            context_length: 262144,
20670            ..Default::default()
20671        };
20672        let error = model_entry_openmodels("qwen/qwen3.6-27b", Some(&caps), None).unwrap_err();
20673        assert_eq!(
20674            error,
20675            "OpenModels feed requires MEMRA_MODEL_METADATA for model \"qwen/qwen3.6-27b\""
20676        );
20677    }
20678
20679    #[tokio::test]
20680    async fn blocking_response_excludes_stop_text_across_token_events() {
20681        let (tx, rx) = worker::event_channel();
20682        tx.send(Event::Token {
20683            id: 1,
20684            text: "answer\nPro".into(),
20685        })
20686        .unwrap();
20687        tx.send(Event::Token {
20688            id: 2,
20689            text: "blem: leaked prompt".into(),
20690        })
20691        .unwrap();
20692        tx.send(Event::Done {
20693            stop_reason: "Callback".into(),
20694            n_tokens: 2,
20695            n_prompt: 8,
20696            n_cached: 0,
20697            elapsed_s: 0.5,
20698            spec: None,
20699        })
20700        .unwrap();
20701        drop(tx);
20702        let response = blocking_response(
20703            rx,
20704            "plain_quant".into(),
20705            false,
20706            vec!["Problem:".into()],
20707            None,
20708            Envelope::new(false),
20709        )
20710        .await;
20711        assert_eq!(response.status(), StatusCode::OK);
20712        let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
20713            .await
20714            .unwrap();
20715        let payload: serde_json::Value = serde_json::from_slice(&bytes).unwrap();
20716        assert_eq!(payload["text"], "answer\n");
20717        assert_eq!(payload["stop_reason"], "Callback");
20718    }
20719
20720    /// step37 content walker (lane/step37-vision): the vendor template's separator law
20721    /// plus the exact per-image expansion, on a real (embedded) 64x64 PNG data URI —
20722    /// square and small, so the plan is tile-free: <im_start> + 169 pads + <im_end>.
20723    #[test]
20724    fn step_walker_expansion_and_separator_law() {
20725        // 64x64 flat-color PNG, pre-encoded (no base64 dep in this crate).
20726        const PNG64: &str = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAY0lEQVR4nO3PQQ3AIADAQEANmlCD9IngcVnSU9DOe/b4s6UDXjWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgfeKYAYIDsx/LAAAAAElFTkSuQmCC";
20727        let uri = format!("data:image/png;base64,{PNG64}");
20728        let content = serde_json::json!([
20729            {"type": "text", "text": "look at"},
20730            {"type": "text", "text": "this:"},
20731            {"type": "image_url", "image_url": {"url": uri}},
20732            {"type": "text", "text": "what is it?"},
20733        ]);
20734        let mut pending: Vec<PendingStepImage> = Vec::new();
20735        let out = content_to_text_vision_step(&content, &mut pending).unwrap();
20736        let mut expansion = String::from("<im_start>");
20737        for _ in 0..memra_engine::vision_step::SV_MAIN_ROWS {
20738            expansion.push_str("<im_patch>");
20739        }
20740        expansion.push_str("<im_end>");
20741        // adjacent text parts join with ONE space; the image resets the separator, so
20742        // the trailing text abuts the expansion with no space.
20743        assert_eq!(out, format!("look at this:{expansion}what is it?"));
20744        assert_eq!(pending.len(), 1);
20745        assert_eq!(pending[0].plan.n_tiles, 0);
20746        assert_eq!(pending[0].plan.n_prompt_tokens(), 171);
20747
20748        // video parts refuse (step37 is image-only), http URLs refuse (SSRF off).
20749        let vid = serde_json::json!([{ "type": "video_url", "video_url": {"url": uri} }]);
20750        assert!(content_to_text_vision_step(&vid, &mut Vec::new()).is_err());
20751        let http = serde_json::json!([
20752            {"type": "image_url", "image_url": {"url": "http://example.com/x.png"}}
20753        ]);
20754        assert!(content_to_text_vision_step(&http, &mut Vec::new()).is_err());
20755    }
20756}
20757
20758/// The `system_fingerprint` identity gates (lane/real-system-fingerprint-20260901).
20759///
20760/// These exist because the field's only assertion used to be `starts_with("memra-")`, which
20761/// `memra-unknown` satisfies. Prod served that literal to every customer request for a
20762/// deploy generation and the test suite was green the whole time.
20763#[cfg(test)]
20764mod build_identity_tests {
20765    use super::{BUILD_GIT_SHA, BUILD_ID_NOTE, BUILD_ID_SRC, SYSTEM_FINGERPRINT, build_id};
20766
20767    /// The baked fingerprint a customer sees: present, shaped, and not the degraded label.
20768    #[test]
20769    fn baked_fingerprint_is_real_and_well_formed() {
20770        assert!(!SYSTEM_FINGERPRINT.is_empty());
20771        assert_ne!(SYSTEM_FINGERPRINT, "memra-unknown");
20772        assert!(
20773            !SYSTEM_FINGERPRINT.contains("unknown"),
20774            "fingerprint {SYSTEM_FINGERPRINT:?} still carries the degraded literal"
20775        );
20776        assert!(
20777            build_id::fingerprint_is_well_formed(SYSTEM_FINGERPRINT),
20778            "fingerprint {SYSTEM_FINGERPRINT:?} is not memra-<version>-<12 hex>"
20779        );
20780        // The documented shape names the crate version, so a version bump is visible in the
20781        // field without reading the id.
20782        assert!(
20783            SYSTEM_FINGERPRINT.starts_with(concat!("memra-", env!("CARGO_PKG_VERSION"), "-")),
20784            "fingerprint {SYSTEM_FINGERPRINT:?} does not name this crate version"
20785        );
20786    }
20787
20788    /// Regression pin on the exact value that shipped, plus the OLD shape it replaced:
20789    /// `memra-<sha>` must not validate either, or a stale-git build could pass the gate.
20790    #[test]
20791    fn the_shape_check_rejects_what_shipped_to_prod() {
20792        assert!(!build_id::fingerprint_is_well_formed("memra-unknown"));
20793        assert!(!build_id::fingerprint_is_well_formed(
20794            "memra-0.123.0-unknown"
20795        ));
20796        // The pre-lane form: bare 12-hex git sha, no version component. Assembled rather
20797        // than written out because `tools/public-boundary-policy.toml`'s `live_fingerprint`
20798        // rule treats a literal `memra-<12 hex>` as deployment identity leaking into the
20799        // public repo, and it is right to: that shape used to BE a serving build's id.
20800        let old_form = format!("memra-{}", "0".repeat(12));
20801        assert!(!build_id::fingerprint_is_well_formed(&old_form));
20802        assert!(!build_id::fingerprint_is_well_formed(""));
20803        assert!(!build_id::fingerprint_is_well_formed("memra-"));
20804        assert!(!build_id::fingerprint_is_well_formed("memra-0.123.0-"));
20805        // Wrong id width, and uppercase hex (the renderer emits lowercase).
20806        assert!(!build_id::fingerprint_is_well_formed("memra-0.123.0-abc"));
20807        assert!(!build_id::fingerprint_is_well_formed(
20808            "memra-0.123.0-ABCDEF012345"
20809        ));
20810        assert!(!build_id::fingerprint_is_well_formed(
20811            "memra-0.123.0-zzzzzzzzzzzz"
20812        ));
20813        // ...and accepts the real shape.
20814        assert!(build_id::fingerprint_is_well_formed(
20815            "memra-0.123.0-4b1f9c02d7a3"
20816        ));
20817    }
20818
20819    /// The identity is a FUNCTION OF THE SOURCE, so two builds of the same tree agree.
20820    ///
20821    /// A test cannot run cargo twice, so it does the equivalent and stronger thing: it
20822    /// re-derives the id from the working tree with the same implementation `build.rs`
20823    /// used, in a different process, at a different time, from a different working
20824    /// directory. If the baked id were a function of the build ENVIRONMENT (which a git
20825    /// lookup is) this would not match.
20826    #[test]
20827    fn build_id_is_rederivable_from_the_source_tree() {
20828        let root = build_id::workspace_root(env!("CARGO_MANIFEST_DIR"));
20829        let scan = root.as_deref().and_then(build_id::content_id);
20830        match scan {
20831            Some(scan) => {
20832                assert_eq!(
20833                    BUILD_ID_SRC,
20834                    build_id::BUILD_ID_SRC_TREE,
20835                    "the source tree is readable, so the baked id must come from it"
20836                );
20837                assert!(BUILD_ID_NOTE.is_empty(), "note set on a non-degraded build");
20838                let expected =
20839                    format!(concat!("memra-", env!("CARGO_PKG_VERSION"), "-{}"), scan.id);
20840                assert_eq!(
20841                    SYSTEM_FINGERPRINT,
20842                    expected,
20843                    "baked fingerprint disagrees with a re-derivation over {} files: the id \
20844                     is not a pure function of the source tree, or the build script did not \
20845                     re-run after an edit",
20846                    scan.files.len()
20847                );
20848                assert!(scan.files.len() > 100, "suspiciously small hashed file set");
20849            }
20850            None => {
20851                // Not a pass by omission: an unreadable tree MUST have produced the
20852                // degraded marker and a stated reason, and the fingerprint must still be
20853                // shaped (asserted by baked_fingerprint_is_real_and_well_formed).
20854                assert_eq!(BUILD_ID_SRC, build_id::BUILD_ID_SRC_DEGRADED);
20855                assert!(
20856                    !BUILD_ID_NOTE.is_empty(),
20857                    "a degraded build must state its reason so the boot WARN can print it"
20858                );
20859            }
20860        }
20861    }
20862
20863    /// The id is not the git sha, in either direction: the identity must not be history, and
20864    /// the sha must stay available as a separate extra field.
20865    #[test]
20866    fn identity_is_independent_of_git_history() {
20867        let id = SYSTEM_FINGERPRINT.rsplit_once('-').unwrap().1;
20868        assert_ne!(
20869            id, BUILD_GIT_SHA,
20870            "the content id equals the git sha; the identity must not be history, it has to \
20871             survive a rewrite that changes every commit"
20872        );
20873        assert!(
20874            !SYSTEM_FINGERPRINT.contains(BUILD_GIT_SHA),
20875            "the git sha leaked into the customer-visible fingerprint {SYSTEM_FINGERPRINT:?}"
20876        );
20877        // The extra field is still populated: either a repo was visible to this build, or it
20878        // honestly reads `unknown`. Never empty, and never the identity.
20879        assert!(!BUILD_GIT_SHA.is_empty());
20880    }
20881
20882    /// Determinism of the digest itself: same bytes in, same id out, and any change in
20883    /// content, path, or ordering-relevant input changes it.
20884    #[test]
20885    fn content_digest_is_deterministic_and_change_sensitive() {
20886        let a = build_id::degraded_build_id("memra-server", "0.123.0");
20887        let b = build_id::degraded_build_id("memra-server", "0.123.0");
20888        assert_eq!(a, b, "the digest is not deterministic");
20889        assert_eq!(a.len(), build_id::BUILD_ID_HEX);
20890        assert!(
20891            a.chars()
20892                .all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c))
20893        );
20894        assert_ne!(a, build_id::degraded_build_id("memra-server", "0.123.1"));
20895        assert_ne!(a, build_id::degraded_build_id("memra-serve", "r0.123.0"));
20896        // Fixed width even when the leading nibbles are zero.
20897        assert_eq!(build_id::render_build_id(0).len(), build_id::BUILD_ID_HEX);
20898        assert_eq!(
20899            build_id::render_build_id(0),
20900            "0".repeat(build_id::BUILD_ID_HEX)
20901        );
20902    }
20903
20904    /// Two scans of the same unchanged tree in one process agree: the in-process half of
20905    /// "stable across two builds of the same source".
20906    #[test]
20907    fn two_scans_of_one_tree_agree() {
20908        let Some(root) = build_id::workspace_root(env!("CARGO_MANIFEST_DIR")) else {
20909            assert_eq!(BUILD_ID_SRC, build_id::BUILD_ID_SRC_DEGRADED);
20910            return;
20911        };
20912        let first = build_id::content_id(&root).expect("first scan");
20913        let second = build_id::content_id(&root).expect("second scan");
20914        assert_eq!(first.id, second.id);
20915        assert_eq!(first.files.len(), second.files.len());
20916    }
20917}
20918
20919/// memra #25: the vision PLACEMENT decision applies to every family whose overlay path reads
20920/// `MEMRA_VISION_OVERLAY_PUBLISH`, not glm5 alone. step37 serves vision in production; with
20921/// a glm5-only guard it could boot clean and 500 mid-prefill. The decision gates MEDIA PARTS
20922/// only: the family switches route the content walkers (step37's text-separator law lives in
20923/// its walker alone), so text-only prompt bytes never move with the placement.
20924#[cfg(test)]
20925mod vision_placement_gate_tests {
20926    use super::vision_media_admissible;
20927
20928    #[test]
20929    fn a_media_part_is_admitted_only_when_the_placement_admits() {
20930        assert_eq!(vision_media_admissible(true, "image"), Ok(()));
20931        assert_eq!(vision_media_admissible(true, "video"), Ok(()));
20932        let err = vision_media_admissible(false, "image").unwrap_err();
20933        assert!(
20934            err.starts_with("image input is not enabled on this deployment"),
20935            "same named refusal the armed-off path gives, so clients see one contract: {err}"
20936        );
20937        assert!(
20938            err.contains("placement"),
20939            "the refusal names its cause: {err}"
20940        );
20941        let err = vision_media_admissible(false, "video").unwrap_err();
20942        assert!(
20943            err.starts_with("video input is not enabled on this deployment"),
20944            "{err}"
20945        );
20946    }
20947
20948    fn live_src() -> String {
20949        let src: String = include_str!("lib.rs")
20950            .lines()
20951            .map(|l| match l.find("//") {
20952                Some(i) => &l[..i],
20953                None => l,
20954            })
20955            .collect::<Vec<_>>()
20956            .join("\n");
20957        let end = src
20958            .find("\nmod vision_placement_gate_tests")
20959            .expect("this test module exists");
20960        src[..end].to_string()
20961    }
20962
20963    /// The comment-stripped body of one top-level item, from `head` to the first column-0 `}`.
20964    fn item_body<'a>(live: &'a str, head: &str) -> &'a str {
20965        let start = live
20966            .find(head)
20967            .unwrap_or_else(|| panic!("{head} not found — did it get renamed?"));
20968        let body = &live[start..];
20969        let end = body.find("\n}\n").expect("item body closes");
20970        &body[..end]
20971    }
20972
20973    /// A char-boundary-safe prefix of at most `n` chars.
20974    fn head_of(s: &str, n: usize) -> &str {
20975        match s.char_indices().nth(n) {
20976            Some((i, _)) => &s[..i],
20977            None => s,
20978        }
20979    }
20980
20981    /// The family switches select the content walker, and step37's TEXT separator law exists
20982    /// only in its walker; a switch that folds the placement in changes rendered prompt bytes
20983    /// for text-only requests whenever the placement is inadmissible (revuto finding on #46).
20984    /// Anchored on comment-stripped source (wiring-assertions law).
20985    #[test]
20986    fn no_family_switch_reads_the_placement_decision() {
20987        let live = live_src();
20988        for switch in [
20989            "fn vision_enabled()",
20990            "fn gemma_vision_enabled()",
20991            "fn step_vision_enabled()",
20992        ] {
20993            let body = item_body(&live, switch);
20994            assert!(
20995                !body.contains("vision_placement_serving")
20996                    && !body.contains("vision_placement_admits"),
20997                "{switch} routes text rendering; it must stay keyed on the operator knobs alone"
20998            );
20999        }
21000        let walker = item_body(&live, "fn content_to_text_vision(");
21001        assert!(
21002            walker.contains(
21003                "if step_vision_enabled() {\n        return content_to_text_vision_step(v, step_images);"
21004            ),
21005            "the step walker dispatch is keyed on the armed switch alone"
21006        );
21007    }
21008
21009    /// Every arm that ACCEPTS a media part passes the placement gate before it plans anything,
21010    /// so an inadmissible placement refuses at the waist for every family, never mid-prefill.
21011    #[test]
21012    fn every_media_accepting_arm_passes_the_placement_gate() {
21013        let live = live_src();
21014        let step = item_body(&live, "fn content_to_text_vision_step(");
21015        let arm = step
21016            .split("Some(\"image_url\") => {")
21017            .nth(1)
21018            .expect("the step walker has an image arm");
21019        assert!(
21020            head_of(arm, 120).contains("vision_placement_admits(\"image\")?;"),
21021            "the step image arm must pass the placement gate first: {}",
21022            head_of(arm, 120)
21023        );
21024        let walker = item_body(&live, "fn content_to_text_vision(");
21025        for (head, kind) in [
21026            (
21027                "Some(\"image_url\") if gemma_vision_enabled() => {",
21028                "image",
21029            ),
21030            ("Some(\"image_url\") => {", "image"),
21031            ("Some(\"video_url\") => {", "video"),
21032        ] {
21033            let arm = walker
21034                .split(head)
21035                .nth(1)
21036                .unwrap_or_else(|| panic!("{head} is not an arm of the walker"));
21037            let window = head_of(arm, 400);
21038            assert!(
21039                window.contains(&format!("vision_placement_admits(\"{kind}\")?;")),
21040                "{head} must pass the placement gate before planning anything: {window}"
21041            );
21042        }
21043        // glm5 needs no arm-level gate: its switch reads GLM5_VISION_SERVING, which the worker
21044        // stores as `tower loaded && placement admissible`, so on an inadmissible placement the
21045        // glm5 arm never fires and the part falls through to the generic named refusal.
21046        assert!(live.contains("GLM5_VISION_SERVING.load(std::sync::atomic::Ordering::Acquire)"));
21047        // The live wrapper feeds the worker's published decision to the pure gate.
21048        let gate = item_body(&live, "fn vision_placement_admits(");
21049        assert!(gate.contains("vision_media_admissible(vision_placement_serving(), kind)"));
21050    }
21051}