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
// SPDX-License-Identifier: MIT OR Apache-2.0
//! 2026-09-26: Read-only per-stream context for the `StreamEvent` arms. The streaming
//! closure owns it and lends it to each handler beside `&mut StreamState`.
//!
//! Owner: server streaming API.
//! Invariants: none beyond the types.
use std::collections::HashSet;
use std::sync::Arc;
use crate::AppState;
use crate::tool_parser;
pub(super) struct StreamCtx {
/// 2026-09-26: Holds the active-requests gauge for the stream's lifetime. `StreamCtx`
/// is moved into the streaming closure, so the guard drops once, whether the stream
/// completes, fails, or is dropped when the client disconnects. Never read.
pub(super) _active_guard: crate::metrics::ActiveRequestGuard,
pub(super) state: Arc<AppState>,
pub(super) model: String,
pub(super) id: String,
pub(super) prompt_len: usize,
pub(super) enable_thinking: bool,
pub(super) tool_defs_for_backfill: Vec<tool_parser::ToolDefinition>,
pub(super) cwd_for_normalize: Option<String>,
pub(super) stop_strings: Vec<String>,
/// 2026-09-26: Always `false` (`mod.rs`), so the retry and chunk-buffering branches in
/// `tool_handlers.rs` never run.
pub(super) tool_retry_enabled: bool,
/// 2026-09-26: The rendered prompt tokens, shared with the scheduler request. Never
/// read.
pub(super) prompt_tokens: Arc<Vec<u32>>,
/// 2026-09-26: Always empty (`mod.rs`). Never read.
pub(super) prompt_vocab: Arc<HashSet<String>>,
pub(super) grammar_spec: Option<crate::api::inference_types::GrammarSpec>,
pub(super) max_tokens: usize,
pub(super) timeout_at: Option<std::time::Instant>,
/// 2026-09-26: Bytes held back from each delta for stop-string matching: the longest
/// stop string's byte length minus one, so a stop string split across two decoded
/// chunks is never sent in part. 0 when `stop_strings` is empty.
pub(super) stop_string_buffer_len: usize,
pub(super) leak_markers: tool_parser::LeakMarkers,
/// 2026-09-26: Whether the tool parser wants schema-driven coercion of parsed
/// arguments (`ToolCallParser::wants_typed_arguments`; true for `qwen3_xml`,
/// `qwen3_coder`, `poolside_v1` and `deepseek_v4_dsml`).
pub(super) wants_typed_arguments: bool,
/// 2026-10-08: The tool parser's `call_policy`; `Repairing` when there is no
/// parser. Under `FailClosed` the detector judges calls itself and
/// `tool_dispatch::push_keepalive_if_due` keeps a held call's stream alive.
pub(super) call_policy: tool_parser::CallPolicy,
/// 2026-10-08: The reasoning parser's `tool_call_closes_reasoning`; false without
/// one. `handle_token` then ends the reasoning at `<tool_call>` too, and keeps
/// marker text inside a call.
pub(super) tool_call_closes_reasoning: bool,
pub(super) max_tool_calls_per_response: usize,
pub(super) req_return_token_ids: bool,
pub(super) req_ctx: Option<crate::rate_limiter::RequestContext>,
pub(super) dump_seq: Option<u64>,
}