use axum::http::HeaderMap;
use axum::response::Response;
use crate::worker::{Cmd, Event};
use crate::{
AppState, ChatCompletionReq, Envelope, InflightGuard, RateLimit, auth, constrained, ledger,
toolcall::{ParsedToolCall, Piece, ToolStreamParser},
worker,
};
pub(crate) struct Admission {
pub rx: tokio::sync::mpsc::UnboundedReceiver<Event>,
pub receipt: Option<ledger::PendingReceipt>,
pub guard: InflightGuard,
pub rl: RateLimit,
pub parser: Option<ToolStreamParser>,
pub stop_strings: Vec<String>,
}
pub(crate) fn authenticate_candidates(
api_auth: &crate::ApiAuth,
candidates: &[Option<&str>],
) -> Result<auth::TenantCtx, auth::AuthDenied> {
let present: Vec<&str> = candidates.iter().filter_map(|c| *c).collect();
if present.is_empty() {
return auth::authenticate_with(api_auth.keyring, api_auth.single_key.as_deref(), None);
}
let mut last = auth::AuthDenied::Unknown;
for candidate in present {
match auth::authenticate_with(
api_auth.keyring,
api_auth.single_key.as_deref(),
Some(candidate),
) {
Ok(tenant) => return Ok(tenant),
Err(why) => last = why,
}
}
Err(last)
}
pub(crate) async fn admit_translated(
st: &AppState,
headers: &HeaderMap,
env: &Envelope,
tenant: &auth::TenantCtx,
req: ChatCompletionReq,
route: &'static str,
ttft: Option<std::sync::Arc<crate::ttft::Trace>>,
) -> Result<Admission, Response> {
let cache_ns = match crate::tenant_namespace(tenant, &req.cache_salt) {
Ok(ns) => ns,
Err(msg) => return Err(crate::bad_request(msg, Some("cache_salt"))),
};
if req.messages.is_empty()
|| req.messages.iter().any(|message| {
!matches!(
message.role.as_str(),
"system" | "developer" | "user" | "assistant" | "tool"
)
})
{
return Err(crate::bad_request(
"messages must use system/developer/user/assistant/tool roles",
Some("messages"),
));
}
let lane = match crate::lane_for_tenant(headers, tenant) {
Ok(l) => l,
Err(resp) => return Err(resp),
};
let model = req.model.clone();
let stream = req.stream;
let capture_prompt = st
.capture
.as_ref()
.filter(|store| store.is_armed(&tenant.tenant))
.map(|_| crate::capture_chat_messages(&req.messages));
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<Event>();
let affinity = crate::affinity_key(&req.session_id, &req.user, headers);
let mut plan = match crate::build_chat_request_with_trace(
req,
st.caps.get(&model),
tx,
lane,
affinity,
ttft,
) {
Ok(plan) => plan,
Err(err) => return Err(crate::bad_request(&err, None)),
};
plan.request.cache_ns = cache_ns;
if let Err((message, param)) = crate::apply_model_request_limits(
&mut plan.request,
st.openrouter_metadata.get(&model),
st.caps.get(&model),
) {
return Err(crate::bad_request(&message, Some(param)));
}
if crate::draining() {
let receipt =
crate::start_request_receipt(st, env, tenant, &model, route, lane, stream, None);
return Err(crate::ledger_rejected(
receipt,
crate::drain_response(),
"draining",
&env.id,
));
}
let budget_permit = match crate::admit_tenant_budget(st, tenant, &mut plan.request) {
Ok(permit) => permit,
Err(rejection) => {
let (response, error_code) = rejection.into_response();
let receipt =
crate::start_request_receipt(st, env, tenant, &model, route, lane, stream, None);
return Err(crate::ledger_rejected(
receipt, response, error_code, &env.id,
));
}
};
let receipt =
crate::start_request_receipt(st, env, tenant, &model, route, lane, stream, budget_permit);
let receipt = if let Some(prompt) = capture_prompt {
crate::arm_capture(receipt, st, tenant, move || prompt)
} else {
receipt
};
let constraint_ready = if plan.request.grammar.is_some() {
let (ready_tx, ready_rx) = tokio::sync::oneshot::channel();
plan.request.constraint_ready = Some(ready_tx);
Some(ready_rx)
} else {
None
};
let (guard, rl) = match crate::acquire_request_slot(st, lane, tenant, env) {
Ok(slot) => slot,
Err(resp) => {
return Err(crate::ledger_rejected(
receipt,
resp,
"rate_limit_exceeded",
&env.id,
));
}
};
crate::meter_admit(env, tenant, &model, lane);
let stop_strings = plan.request.stop_strings.clone();
let parser = plan.parser;
worker::PENDING_ADMITS.fetch_add(1, std::sync::atomic::Ordering::Release);
if st
.cmd_tx
.send(Cmd::Generate(Box::new(plan.request)))
.is_err()
{
worker::PENDING_ADMITS.fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
return Err(crate::ledger_rejected(
receipt,
rl.attach(crate::worker_unavailable_response()),
"worker_unavailable",
&env.id,
));
}
if let Some(ready) = constraint_ready {
match tokio::time::timeout(constrained::CONSTRAINT_COMPILE_TIMEOUT, ready).await {
Ok(Ok(Ok(()))) => {}
Ok(Ok(Err(err))) => {
return Err(crate::ledger_rejected(
receipt,
rl.attach(crate::engine_error_response(&err)),
crate::engine_error_code(err.class),
&env.id,
));
}
Ok(Err(_)) => {
return Err(crate::ledger_rejected(
receipt,
rl.attach(crate::worker_unavailable_response()),
"worker_unavailable",
&env.id,
));
}
Err(_) => {
return Err(crate::ledger_rejected(
receipt,
rl.attach(crate::engine_error_response(
&worker::constraint_timeout_error(),
)),
"constraint_compile_timeout",
&env.id,
));
}
}
}
let rx = match crate::peek_admission(rx).await {
Ok(rx) => rx,
Err((resp, error_code)) => {
return Err(crate::ledger_rejected(
receipt,
rl.attach(resp),
error_code,
&env.id,
));
}
};
Ok(Admission {
rx,
receipt,
guard,
rl,
parser,
stop_strings,
})
}
pub(crate) struct FinalChat {
pub text: String,
pub reasoning: String,
pub calls: Vec<ParsedToolCall>,
pub stop_reason: String,
pub matched_stop: Option<String>,
pub n_tokens: usize,
pub n_prompt: usize,
pub n_cached: usize,
#[allow(dead_code)]
pub elapsed_s: f64,
#[allow(dead_code)]
pub spec: Option<worker::SpecUsage>,
}
pub(crate) enum CollectError {
Ledger,
Engine(worker::EngineError),
}
pub(crate) async fn collect_final(
rx: &mut tokio::sync::mpsc::UnboundedReceiver<Event>,
receipt: &mut Option<ledger::PendingReceipt>,
mut parser: Option<ToolStreamParser>,
stop_strings: &[String],
env: &Envelope,
) -> Result<FinalChat, CollectError> {
let mut text = String::new();
let mut reasoning = String::new();
let mut calls: Vec<ParsedToolCall> = Vec::new();
let consume = |pieces: Vec<Piece>,
text: &mut String,
reasoning: &mut String,
calls: &mut Vec<ParsedToolCall>| {
for piece in pieces {
match piece {
Piece::Content(t) => text.push_str(&t),
Piece::Reasoning(t) => reasoning.push_str(&t),
Piece::Call(c) => calls.push(c),
}
}
};
while let Some(ev) = rx.recv().await {
match ev {
Event::PromptUsage { n_prompt, n_cached } => {
if let Some(receipt) = receipt.as_mut()
&& let Err(err) = receipt.record_prompt_usage(n_prompt as u64, n_cached as u64)
{
eprintln!(
"[ledger] ERROR: request {} partial prompt receipt failed: {err}",
env.id
);
return Err(CollectError::Ledger);
}
}
Event::Token { id: _, text: delta } => {
if let Some(receipt) = receipt.as_mut()
&& let Err(err) = receipt.record_completion_token()
{
eprintln!(
"[ledger] ERROR: request {} partial completion receipt failed: {err}",
env.id
);
return Err(CollectError::Ledger);
}
if let Some(receipt) = receipt.as_mut() {
receipt.capture_completion_delta(&delta);
}
match parser.as_mut() {
Some(p) => consume(p.push(&delta), &mut text, &mut reasoning, &mut calls),
None => text.push_str(&delta),
}
}
Event::TokenSnapshot(_) => {}
Event::Done {
stop_reason,
n_tokens,
n_prompt,
n_cached,
elapsed_s,
spec,
} => {
if let Some(p) = parser.as_mut() {
consume(p.finish(), &mut text, &mut reasoning, &mut calls);
}
let matched_stop = stop_strings
.iter()
.filter_map(|stop| text.find(stop).map(|at| (at, stop)))
.min_by_key(|(at, _)| *at)
.map(|(at, stop)| {
text.truncate(at);
stop.clone()
});
if let Some(receipt) = receipt.as_mut()
&& let Err(err) = receipt.complete(
ledger::Usage {
prompt_tokens: n_prompt as u64,
cached_prompt_tokens: n_cached as u64,
completion_tokens: n_tokens as u64,
},
elapsed_s,
)
{
eprintln!(
"[ledger] ERROR: request {} completion receipt failed: {err}",
env.id
);
return Err(CollectError::Ledger);
}
return Ok(FinalChat {
text,
reasoning,
calls,
stop_reason,
matched_stop,
n_tokens,
n_prompt,
n_cached,
elapsed_s,
spec,
});
}
Event::Error(err) => {
if let Some(receipt) = receipt.as_mut()
&& let Err(ledger_err) = receipt.reject(
crate::class_http(err.class).0.as_u16(),
crate::engine_error_code(err.class),
)
{
eprintln!(
"[ledger] ERROR: request {} failure receipt failed: {ledger_err}",
env.id
);
return Err(CollectError::Ledger);
}
return Err(CollectError::Engine(err));
}
}
}
let e = worker::EngineError::overloaded(
"worker closed the stream without completing (worker restart in progress)",
);
if let Some(receipt) = receipt.as_mut()
&& let Err(ledger_err) = receipt.reject(
crate::class_http(e.class).0.as_u16(),
crate::engine_error_code(e.class),
)
{
eprintln!(
"[ledger] ERROR: request {} closed-stream receipt failed: {ledger_err}",
env.id
);
return Err(CollectError::Ledger);
}
Err(CollectError::Engine(e))
}
pub(crate) struct SurfaceScrubber {
stops: Vec<String>,
buf: String,
matched: Option<String>,
}
impl SurfaceScrubber {
pub fn new(stops: Vec<String>) -> Self {
Self {
stops,
buf: String::new(),
matched: None,
}
}
pub fn push(&mut self, text: &str) -> String {
if self.matched.is_some() {
return String::new();
}
self.buf.push_str(text);
if let Some((i, stop)) = self
.stops
.iter()
.filter_map(|s| self.buf.find(s.as_str()).map(|at| (at, s.clone())))
.min_by_key(|(at, _)| *at)
{
self.matched = Some(stop);
let out = self.buf[..i].to_string();
self.buf.clear();
return out;
}
let keep = self
.stops
.iter()
.map(|s| crate::partial_stop_suffix(&self.buf, s))
.max()
.unwrap_or(0);
let emit_to = self.buf.len() - keep;
let out = self.buf[..emit_to].to_string();
self.buf.drain(..emit_to);
out
}
pub fn finish(&mut self) -> String {
if self.matched.is_some() {
self.buf.clear();
return String::new();
}
std::mem::take(&mut self.buf)
}
pub fn matched(&self) -> Option<&str> {
self.matched.as_deref()
}
}
#[cfg(test)]
pub(crate) async fn sse_frames(resp: Response) -> Vec<(String, serde_json::Value)> {
let bytes = axum::body::to_bytes(resp.into_body(), usize::MAX)
.await
.expect("sse body");
let text = String::from_utf8(bytes.to_vec()).expect("utf8 sse body");
let mut frames = Vec::new();
for chunk in text.split("\n\n") {
let mut event = String::new();
let mut data: Option<serde_json::Value> = None;
for line in chunk.lines() {
if let Some(name) = line.strip_prefix("event: ") {
event = name.trim().to_string();
} else if let Some(payload) = line.strip_prefix("data: ") {
data = serde_json::from_str(payload).ok();
}
}
if let Some(data) = data {
frames.push((event, data));
}
}
frames
}
#[cfg(test)]
pub(crate) fn test_envelope(id: &str) -> Envelope {
Envelope {
id: id.to_string(),
created: 1,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scrubber_holds_back_partial_stops_and_reports_the_match() {
let mut sc = SurfaceScrubber::new(vec!["STOP".into()]);
assert_eq!(sc.push("hello S"), "hello "); assert_eq!(sc.push("T"), ""); assert_eq!(sc.push("ill going"), "STill going"); assert_eq!(sc.push(" STOP more"), " ");
assert_eq!(sc.matched(), Some("STOP"));
assert_eq!(sc.push("anything"), ""); assert_eq!(sc.finish(), "");
}
#[test]
fn scrubber_finish_releases_heldback_text_when_no_stop_fired() {
let mut sc = SurfaceScrubber::new(vec!["<end>".into()]);
assert_eq!(sc.push("tail <e"), "tail ");
assert_eq!(sc.finish(), "<e");
assert_eq!(sc.matched(), None);
}
#[tokio::test]
async fn collect_final_truncates_at_stop_and_names_the_matched_sequence() {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
tx.send(Event::PromptUsage {
n_prompt: 7,
n_cached: 2,
})
.unwrap();
tx.send(Event::Token {
id: 1,
text: "one STOP two".into(),
})
.unwrap();
tx.send(Event::Done {
stop_reason: "Callback".into(),
n_tokens: 4,
n_prompt: 7,
n_cached: 2,
elapsed_s: 0.1,
spec: None,
})
.unwrap();
drop(tx);
let mut receipt = None;
let fin = collect_final(
&mut rx,
&mut receipt,
None,
&["STOP".to_string()],
&test_envelope("t"),
)
.await
.ok()
.expect("final");
assert_eq!(fin.text, "one ");
assert_eq!(fin.matched_stop.as_deref(), Some("STOP"));
assert_eq!((fin.n_prompt, fin.n_cached, fin.n_tokens), (7, 2, 4));
}
#[tokio::test]
async fn collect_final_surfaces_engine_faults_as_classified_errors() {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
tx.send(Event::Error(worker::EngineError::overloaded("no room")))
.unwrap();
drop(tx);
let mut receipt = None;
match collect_final(&mut rx, &mut receipt, None, &[], &test_envelope("t")).await {
Err(CollectError::Engine(e)) => assert_eq!(e.message, "no room"),
_ => panic!("expected engine error"),
}
}
#[test]
fn authenticate_candidates_accepts_either_header_and_stays_open_when_unconfigured() {
let open = crate::ApiAuth::default();
assert!(authenticate_candidates(&open, &[]).is_ok());
assert!(authenticate_candidates(&open, &[Some("whatever")]).is_ok());
let keyed = crate::ApiAuth {
keyring: None,
single_key: Some(std::sync::Arc::from("sk-test")),
};
assert!(authenticate_candidates(&keyed, &[Some("sk-test"), None]).is_ok());
assert!(authenticate_candidates(&keyed, &[Some("wrong"), Some("sk-test")]).is_ok());
assert!(authenticate_candidates(&keyed, &[Some("wrong")]).is_err());
assert!(authenticate_candidates(&keyed, &[]).is_err());
}
}