aprender-serve 0.65.2

Pure Rust ML inference engine built from scratch - model serving for GGUF and safetensors
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
//! PP-27 falsifiers: an SSE stream must DECLARE how it is produced, and the
//! terminal chunk must carry the token counts (and the server's phase timings
//! when it measured them).
//!
//! Why this is a correctness claim and not a nicety: two builders in this crate
//! serve `stream: true`, and they are not the same thing.
//! `true_streaming_sse_response` writes a delta as each token leaves the decode
//! loop; `pregenerated_sse_response` generates the WHOLE completion first and
//! then replays it. A client measuring time-to-first-token and inter-token
//! latency against the second one is measuring the SSE writer, so a receipt
//! built over it records the wrong quantity while looking exactly like a
//! correct one. Before this, neither builder said which it was, and the
//! `--batch` GPU path (a cached model) silently used the replaying one.
//!
//! These drive the REAL router wherever a backend exists on CPU, because the
//! defect lives in what a client receives, not in what a builder returns.

use axum::{
    body::Body,
    http::{Request, StatusCode},
};
use tower::util::ServiceExt;

use crate::api::{create_router, AppState};

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

async fn post_sse(state: AppState, uri: &str, json: &str) -> (StatusCode, String) {
    let response = create_router(state)
        .oneshot(
            Request::builder()
                .method("POST")
                .uri(uri)
                .header("content-type", "application/json")
                .body(Body::from(json.to_string()))
                .expect("build request"),
        )
        .await
        .expect("dispatch");
    let status = response.status();
    let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
        .await
        .expect("read body");
    (status, String::from_utf8_lossy(&bytes).into_owned())
}

/// Every `data:` frame that is not the `[DONE]` sentinel, parsed as JSON.
fn sse_frames(body: &str) -> Vec<serde_json::Value> {
    body.lines()
        .filter_map(|line| line.strip_prefix("data: "))
        .filter(|payload| payload.trim() != "[DONE]")
        .filter_map(|payload| serde_json::from_str::<serde_json::Value>(payload).ok())
        .collect()
}

const STREAM_BODY: &str = r#"{"model":"default","messages":[{"role":"user","content":"token5 token6"}],"max_tokens":4,"temperature":0.0,"stream":true}"#;

// ---------------------------------------------------------------------------
// Claim 1: a live stream says so, and closes with usage
// ---------------------------------------------------------------------------

/// The CPU quantized deployment — what `apr serve run model.gguf` builds
/// without an accelerator — streams through `true_streaming_sse_response`.
/// Its first chunk must declare `live`, and only its first.
#[cfg(feature = "gpu")]
#[tokio::test]
async fn live_quantized_stream_declares_live_and_carries_usage_on_terminal_chunk() {
    use super::native_routes_2376::quantized_state;

    let (status, body) = post_sse(quantized_state(), "/v1/chat/completions", STREAM_BODY).await;
    assert_eq!(status, StatusCode::OK, "stream request failed: {body}");

    let frames = sse_frames(&body);
    assert!(frames.len() >= 2, "expected an opening and a terminal chunk:\n{body}");

    assert_eq!(
        frames[0]["stream_mode"].as_str(),
        Some("live"),
        "the first chunk of a live stream must declare it; got {}\n{body}",
        frames[0]
    );
    for (i, frame) in frames.iter().enumerate().skip(1) {
        assert!(
            frame["stream_mode"].is_null(),
            "chunk {i} re-declared stream_mode; it belongs on the FIRST chunk only:\n{frame}"
        );
    }

    let terminal = frames.last().expect("terminal chunk");
    assert!(
        !terminal["usage"].is_null(),
        "the terminal chunk must carry usage:\n{terminal}"
    );
    // The deltas a client reassembles ARE the completion, so the count it is
    // told must be the count it received.
    let delta_chunks = frames
        .iter()
        .filter(|f| f["choices"][0]["delta"]["content"].is_string())
        .count();
    assert_eq!(
        terminal["usage"]["completion_tokens"].as_u64(),
        Some(delta_chunks as u64),
        "usage.completion_tokens must equal the deltas actually streamed \
         ({delta_chunks}):\n{terminal}"
    );
    assert_eq!(
        terminal["usage"]["total_tokens"].as_u64(),
        Some(
            terminal["usage"]["prompt_tokens"].as_u64().unwrap_or_default()
                + terminal["usage"]["completion_tokens"].as_u64().unwrap_or_default()
        ),
        "total_tokens must be the sum it claims to be:\n{terminal}"
    );

    // Non-terminal chunks must NOT carry usage: a client that sums them would
    // double-count.
    for (i, frame) in frames.iter().enumerate().take(frames.len() - 1) {
        assert!(
            frame["usage"].is_null(),
            "chunk {i} carried usage; it belongs on the terminal chunk only:\n{frame}"
        );
    }
}

/// The `/v1/chat/completions/stream` sibling route serves the same backend and
/// must make the same declaration — two routes on one server disagreeing about
/// how the stream is produced would be worse than neither declaring.
#[cfg(feature = "gpu")]
#[tokio::test]
async fn the_stream_route_declares_the_same_mode_as_the_stream_flag() {
    use super::native_routes_2376::quantized_state;

    let (_, via_flag) = post_sse(quantized_state(), "/v1/chat/completions", STREAM_BODY).await;
    let (_, via_route) =
        post_sse(quantized_state(), "/v1/chat/completions/stream", STREAM_BODY).await;

    let flag_mode = sse_frames(&via_flag)
        .first()
        .and_then(|f| f["stream_mode"].as_str().map(str::to_string));
    let route_mode = sse_frames(&via_route)
        .first()
        .and_then(|f| f["stream_mode"].as_str().map(str::to_string));
    assert_eq!(flag_mode.as_deref(), Some("live"));
    assert_eq!(
        flag_mode, route_mode,
        "the two chat routes declared different stream modes for the same backend"
    );
}

// ---------------------------------------------------------------------------
// Claim 2: a replayed stream says THAT, and is distinguishable
// ---------------------------------------------------------------------------

/// The dense `Model` backend generates the whole completion, then replays it.
/// It must say `replayed` — this is the case a receipt has to be able to refuse
/// `ttft`/`itl_p95` from.
#[tokio::test]
async fn replayed_stream_declares_replayed() {
    let state = AppState::demo().expect("dense demo AppState");
    let (status, body) = post_sse(state, "/v1/chat/completions", STREAM_BODY).await;
    assert_eq!(status, StatusCode::OK, "stream request failed: {body}");

    let frames = sse_frames(&body);
    assert_eq!(
        frames[0]["stream_mode"].as_str(),
        Some("replayed"),
        "the pre-generated builder must declare `replayed`:\n{}",
        frames[0]
    );
    let terminal = frames.last().expect("terminal chunk");
    assert!(
        !terminal["usage"].is_null(),
        "a replayed stream still owes the client its token counts:\n{terminal}"
    );
    // §3: no phase split exists on this path — generation was over before the
    // first byte was written. Absent, not zero.
    assert!(
        terminal["timings"].is_null(),
        "a replayed stream cannot have measured a prefill phase:\n{terminal}"
    );
}

/// The two modes must be DISTINGUISHABLE on the wire. A declaration that reads
/// the same for both paths would satisfy every assertion above and discharge
/// nothing.
#[cfg(feature = "gpu")]
#[tokio::test]
async fn live_and_replayed_are_different_declarations() {
    use super::native_routes_2376::quantized_state;

    let (_, live) = post_sse(quantized_state(), "/v1/chat/completions", STREAM_BODY).await;
    let (_, replayed) = post_sse(
        AppState::demo().expect("dense demo AppState"),
        "/v1/chat/completions",
        STREAM_BODY,
    )
    .await;

    let live_mode = sse_frames(&live)[0]["stream_mode"].as_str().map(str::to_string);
    let replayed_mode = sse_frames(&replayed)[0]["stream_mode"]
        .as_str()
        .map(str::to_string);
    assert_eq!(live_mode.as_deref(), Some("live"));
    assert_eq!(replayed_mode.as_deref(), Some("replayed"));
    assert_ne!(
        live_mode, replayed_mode,
        "the two SSE mechanisms must not declare the same mode"
    );
}

// ---------------------------------------------------------------------------
// Claim 3: `stream_options` is accepted, and does not gate the emission
// ---------------------------------------------------------------------------

/// An OpenAI client that sends `stream_options: {"include_usage": true}` must
/// not be rejected as malformed...
#[tokio::test]
async fn stream_options_include_usage_is_accepted() {
    let state = AppState::demo().expect("dense demo AppState");
    let body = r#"{"model":"default","messages":[{"role":"user","content":"token5"}],"max_tokens":2,"temperature":0.0,"stream":true,"stream_options":{"include_usage":true}}"#;
    let (status, response) = post_sse(state, "/v1/chat/completions", body).await;
    assert_eq!(
        status,
        StatusCode::OK,
        "stream_options must be accepted, got {status}: {response}"
    );
    let frames = sse_frames(&response);
    assert!(
        !frames.last().expect("terminal chunk")["usage"].is_null(),
        "usage must be present when the client asked for it"
    );
}

/// ...and a client that does NOT send it still gets usage. PP-27 needs the
/// counts on every run: a harness cannot retro-fit an opt-in flag onto a band
/// that already happened, and llama-server emits its `timings` unconditionally
/// for the same reason.
#[tokio::test]
async fn usage_is_emitted_without_the_opt_in() {
    let state = AppState::demo().expect("dense demo AppState");
    let (_, response) = post_sse(state, "/v1/chat/completions", STREAM_BODY).await;
    let frames = sse_frames(&response);
    assert!(
        !frames.last().expect("terminal chunk")["usage"].is_null(),
        "usage must be emitted regardless of stream_options"
    );
}

// ---------------------------------------------------------------------------
// Claim 4: §3 timings are measured or absent — never zero
// ---------------------------------------------------------------------------

/// A backend that did not separate prefill from decode reports NO `timings`
/// key. `0.0` would enter `prefill_ratio` as a measurement.
#[cfg(feature = "gpu")]
#[tokio::test]
async fn timings_absent_is_null_not_zero() {
    use super::native_routes_2376::quantized_state;

    let nonstream = r#"{"model":"default","messages":[{"role":"user","content":"token5 token6"}],"max_tokens":4,"temperature":0.0}"#;
    let (status, body) = post_sse(quantized_state(), "/v1/chat/completions", nonstream).await;
    assert_eq!(status, StatusCode::OK, "{body}");
    let parsed: serde_json::Value = serde_json::from_str(&body).expect("json body");
    assert!(
        parsed["timings"].is_null(),
        "the CPU quantized backend does not measure a phase split; it must report \
         no timings rather than zeros:\n{parsed}"
    );
    assert!(
        !body.contains("\"prompt_ms\":0"),
        "a zero prefill duration must never reach the wire:\n{body}"
    );

    // Streaming form of the same claim.
    let (_, stream_body) = post_sse(quantized_state(), "/v1/chat/completions", STREAM_BODY).await;
    let frames = sse_frames(&stream_body);
    assert!(
        frames.last().expect("terminal chunk")["timings"].is_null(),
        "an unmeasured phase split must be absent on the terminal chunk too"
    );
}

/// When a backend DOES measure, the block appears with llama.cpp's key names,
/// so one client parser serves both lanes.
///
/// Driven through `build_chat_response` — the single function every
/// non-streaming backend returns through — because no CPU backend in this crate
/// measures a phase split, and asserting the shape on a fabricated CUDA run
/// would prove less than nothing.
#[tokio::test]
async fn nonstream_response_carries_timings_when_measured() {
    use crate::api::PhaseTimings;

    let measured = PhaseTimings {
        prefill_ms: Some(40.0),
        decode_ms: Some(200.0),
    };
    let timings = measured
        .to_timings(512, 128)
        .expect("both phases measured, so a wire block is representable");

    let response = crate::api::openai_handlers::build_chat_response(
        "chatcmpl-test".to_string(),
        "test-model".to_string(),
        "hello".to_string(),
        512,
        128,
        128,
        None,
        None,
        std::time::Duration::from_millis(240),
        None,
        None,
        Some(timings),
    );
    let bytes = axum::body::to_bytes(response.into_body(), usize::MAX)
        .await
        .expect("read body");
    let parsed: serde_json::Value =
        serde_json::from_slice(&bytes).expect("json body");

    let t = &parsed["timings"];
    assert!(!t.is_null(), "measured timings must reach the wire:\n{parsed}");
    // llama.cpp's key names, verbatim.
    assert_eq!(t["prompt_n"].as_u64(), Some(512));
    assert_eq!(t["prompt_ms"].as_f64(), Some(40.0));
    assert_eq!(t["predicted_n"].as_u64(), Some(128));
    assert_eq!(t["predicted_ms"].as_f64(), Some(200.0));
    // 512 tokens in 40 ms = 12 800 tok/s; 128 in 200 ms = 640 tok/s.
    assert!(
        (t["prompt_per_second"].as_f64().expect("prompt rate") - 12_800.0).abs() < 1e-6,
        "prompt_per_second must be prompt_n/prompt_ms, got {t}"
    );
    assert!(
        (t["predicted_per_second"].as_f64().expect("decode rate") - 640.0).abs() < 1e-6,
        "predicted_per_second must be predicted_n/predicted_ms, got {t}"
    );
    assert!(
        t["clock"].as_str().is_some_and(|c| c.contains("Instant")),
        "the block must state which clock produced it, got {t}"
    );

    // usage and timings must AGREE about the prompt: a receipt divides one by
    // the other.
    assert_eq!(
        parsed["usage"]["prompt_tokens"].as_u64(),
        t["prompt_n"].as_u64(),
        "timings.prompt_n and usage.prompt_tokens must be the same number"
    );
}

// ---------------------------------------------------------------------------
// The conversion rule itself
// ---------------------------------------------------------------------------

#[cfg(test)]
mod phase_timings_rules {
    use crate::api::{PhaseTimings, Timings};

    /// One measured phase is not a phase split. Filling the other with `0.0`
    /// would put a fabricated numerator into a gated ratio.
    #[test]
    fn a_half_measured_split_produces_no_wire_block() {
        assert!(PhaseTimings {
            prefill_ms: Some(40.0),
            decode_ms: None,
        }
        .to_timings(512, 128)
        .is_none());
        assert!(PhaseTimings {
            prefill_ms: None,
            decode_ms: Some(200.0),
        }
        .to_timings(512, 128)
        .is_none());
        assert!(PhaseTimings::default().to_timings(512, 128).is_none());
        assert!(PhaseTimings {
            prefill_ms: Some(40.0),
            decode_ms: Some(200.0),
        }
        .to_timings(512, 128)
        .is_some());
    }

    /// A rate over a zero-length interval is undefined; the key is omitted
    /// rather than reported as `0.0`, which would read as "infinitely slow".
    #[test]
    fn a_zero_duration_yields_no_rate() {
        let t = Timings::from_phases(512, 0.0, 128, 200.0);
        assert!(t.prompt_per_second.is_none());
        assert!(t.predicted_per_second.is_some());
        let json = serde_json::to_value(&t).expect("serialize");
        assert!(
            json.get("prompt_per_second").is_none(),
            "an undefined rate must not appear as a key: {json}"
        );
        assert_eq!(json["prompt_ms"].as_f64(), Some(0.0));
    }

    /// The rate is per SECOND from a duration in MILLISECONDS — a factor-1000
    /// slip here would make every prefill ratio look like a thousandfold win.
    #[test]
    fn the_rate_is_per_second_not_per_millisecond() {
        let t = Timings::from_phases(1000, 1000.0, 10, 100.0);
        assert_eq!(t.prompt_per_second, Some(1000.0));
        assert_eq!(t.predicted_per_second, Some(100.0));
    }
}