#![allow(unused_imports, dead_code)]
use axum::extract::State;
use axum::extract::rejection::JsonRejection;
use axum::http::StatusCode;
use axum::response::sse::{Event, KeepAlive};
use axum::response::{IntoResponse, Json, Response, Sse};
use futures::StreamExt;
use std::sync::Arc;
use tokio_stream::wrappers::ReceiverStream;
use crate::AppState;
use crate::openai::{
ChatCompletionChunk, ChatCompletionRequest, ChatCompletionResponse, CompletionChunk,
CompletionRequest, CompletionResponse, ModelInfo, ModelListResponse, Usage,
};
use crate::tool_parser;
use super::compact::{compact_messages, openai_error_response, openai_error_response_with_param};
use super::completions::not_supported;
use super::inference_types::{
GrammarSpec, InferenceRequest, InferenceResponse, StreamEvent, TokenLogprobs,
};
use super::sanitizer::{
F7_STALL_REFUSE_THRESHOLD, F7_STALL_WARN_THRESHOLD, F7StallBuckets, ToolKind, classify_tool,
extract_bash_final_action, primary_arg_for_tool, sanitize_content_chunk,
};
use super::chat::chat_completions_inner;
use super::strip::strip_thinking_tags;
use super::inference_types::*;
use super::sanitizer::*;
impl InferenceRequest {
pub fn prompt_len(&self) -> usize {
match self {
InferenceRequest::Blocking { prompt_tokens, .. } => prompt_tokens.len(),
InferenceRequest::Streaming { prompt_tokens, .. } => prompt_tokens.len(),
}
}
pub fn has_image_pixels(&self) -> bool {
match self {
InferenceRequest::Blocking { image_pixels, .. } => !image_pixels.is_empty(),
InferenceRequest::Streaming { image_pixels, .. } => !image_pixels.is_empty(),
}
}
pub fn take_image_pixels(&mut self) -> Vec<metrale_model_layers::VisionItem> {
match self {
InferenceRequest::Blocking { image_pixels, .. } => std::mem::take(image_pixels),
InferenceRequest::Streaming { image_pixels, .. } => std::mem::take(image_pixels),
}
}
pub fn image_pixels_ref(&self) -> &[metrale_model_layers::VisionItem] {
match self {
InferenceRequest::Blocking { image_pixels, .. } => image_pixels.as_slice(),
InferenceRequest::Streaming { image_pixels, .. } => image_pixels.as_slice(),
}
}
pub fn take_stop_tokens(&mut self) -> Vec<u32> {
match self {
InferenceRequest::Blocking { stop_tokens, .. } => std::mem::take(stop_tokens),
InferenceRequest::Streaming { stop_tokens, .. } => std::mem::take(stop_tokens),
}
}
pub fn top_k(&self) -> u32 {
match self {
InferenceRequest::Blocking { top_k, .. } => *top_k,
InferenceRequest::Streaming { top_k, .. } => *top_k,
}
}
pub fn top_p(&self) -> f32 {
match self {
InferenceRequest::Blocking { top_p, .. } => *top_p,
InferenceRequest::Streaming { top_p, .. } => *top_p,
}
}
pub fn top_n_sigma(&self) -> f32 {
match self {
InferenceRequest::Blocking { top_n_sigma, .. } => *top_n_sigma,
InferenceRequest::Streaming { top_n_sigma, .. } => *top_n_sigma,
}
}
pub fn min_p(&self) -> f32 {
match self {
InferenceRequest::Blocking { min_p, .. } => *min_p,
InferenceRequest::Streaming { min_p, .. } => *min_p,
}
}
pub fn repetition_penalty(&self) -> f32 {
match self {
InferenceRequest::Blocking {
repetition_penalty, ..
} => *repetition_penalty,
InferenceRequest::Streaming {
repetition_penalty, ..
} => *repetition_penalty,
}
}
pub fn presence_penalty(&self) -> f32 {
match self {
InferenceRequest::Blocking {
presence_penalty, ..
} => *presence_penalty,
InferenceRequest::Streaming {
presence_penalty, ..
} => *presence_penalty,
}
}
pub fn frequency_penalty(&self) -> f32 {
match self {
InferenceRequest::Blocking {
frequency_penalty, ..
} => *frequency_penalty,
InferenceRequest::Streaming {
frequency_penalty, ..
} => *frequency_penalty,
}
}
pub fn dry_multiplier(&self) -> f32 {
match self {
InferenceRequest::Blocking { dry_multiplier, .. } => *dry_multiplier,
InferenceRequest::Streaming { dry_multiplier, .. } => *dry_multiplier,
}
}
pub fn lz_penalty(&self) -> f32 {
match self {
InferenceRequest::Blocking { lz_penalty, .. } => *lz_penalty,
InferenceRequest::Streaming { lz_penalty, .. } => *lz_penalty,
}
}
pub fn dry_base(&self) -> f32 {
match self {
InferenceRequest::Blocking { dry_base, .. } => *dry_base,
InferenceRequest::Streaming { dry_base, .. } => *dry_base,
}
}
pub fn dry_allowed_length(&self) -> u32 {
match self {
InferenceRequest::Blocking {
dry_allowed_length, ..
} => *dry_allowed_length,
InferenceRequest::Streaming {
dry_allowed_length, ..
} => *dry_allowed_length,
}
}
pub fn logit_bias(&self) -> &[(u32, f32)] {
match self {
InferenceRequest::Blocking { logit_bias, .. } => logit_bias,
InferenceRequest::Streaming { logit_bias, .. } => logit_bias,
}
}
pub fn session_hash(&self) -> u64 {
match self {
InferenceRequest::Blocking { session_hash, .. } => *session_hash,
InferenceRequest::Streaming { session_hash, .. } => *session_hash,
}
}
pub fn lookup_tenant(&self) -> Option<crate::auth::LookupTenant> {
match self {
InferenceRequest::Blocking { lookup_tenant, .. }
| InferenceRequest::Streaming { lookup_tenant, .. } => *lookup_tenant,
}
}
pub fn adapter_slot(&self) -> i32 {
match self {
InferenceRequest::Blocking { adapter_slot, .. } => *adapter_slot,
InferenceRequest::Streaming { adapter_slot, .. } => *adapter_slot,
}
}
pub fn src_lang_id(&self) -> u32 {
match self {
InferenceRequest::Blocking { src_lang_id, .. } => *src_lang_id,
InferenceRequest::Streaming { src_lang_id, .. } => *src_lang_id,
}
}
pub fn tgt_lang_id(&self) -> u32 {
match self {
InferenceRequest::Blocking { tgt_lang_id, .. } => *tgt_lang_id,
InferenceRequest::Streaming { tgt_lang_id, .. } => *tgt_lang_id,
}
}
pub fn num_beams(&self) -> u32 {
match self {
InferenceRequest::Blocking { num_beams, .. } => *num_beams,
InferenceRequest::Streaming { num_beams, .. } => *num_beams,
}
}
pub fn length_penalty(&self) -> f32 {
match self {
InferenceRequest::Blocking { length_penalty, .. } => *length_penalty,
InferenceRequest::Streaming { length_penalty, .. } => *length_penalty,
}
}
pub fn early_stopping(&self) -> bool {
match self {
InferenceRequest::Blocking { early_stopping, .. } => *early_stopping,
InferenceRequest::Streaming { early_stopping, .. } => *early_stopping,
}
}
pub fn max_tokens(&self) -> usize {
match self {
InferenceRequest::Blocking { max_tokens, .. } => *max_tokens,
InferenceRequest::Streaming { max_tokens, .. } => *max_tokens,
}
}
pub fn prompt_tokens_arc(&self) -> std::sync::Arc<Vec<u32>> {
match self {
InferenceRequest::Blocking { prompt_tokens, .. } => prompt_tokens.clone(),
InferenceRequest::Streaming { prompt_tokens, .. } => prompt_tokens.clone(),
}
}
pub fn enable_thinking(&self) -> bool {
match self {
InferenceRequest::Blocking {
enable_thinking, ..
} => *enable_thinking,
InferenceRequest::Streaming {
enable_thinking, ..
} => *enable_thinking,
}
}
pub fn thinking_budget(&self) -> Option<u32> {
match self {
InferenceRequest::Blocking {
thinking_budget, ..
} => *thinking_budget,
InferenceRequest::Streaming {
thinking_budget, ..
} => *thinking_budget,
}
}
pub fn repetition_detection(
&self,
) -> Option<crate::api::inference_types::RepetitionDetectionParams> {
match self {
InferenceRequest::Blocking {
repetition_detection,
..
} => *repetition_detection,
InferenceRequest::Streaming {
repetition_detection,
..
} => *repetition_detection,
}
}
pub fn require_tool_call(&self) -> bool {
match self {
InferenceRequest::Blocking {
require_tool_call, ..
} => *require_tool_call,
InferenceRequest::Streaming {
require_tool_call, ..
} => *require_tool_call,
}
}
pub fn tools_present(&self) -> bool {
match self {
InferenceRequest::Blocking { tools_present, .. } => *tools_present,
InferenceRequest::Streaming { tools_present, .. } => *tools_present,
}
}
pub fn suppress_tool_call(&self) -> bool {
match self {
InferenceRequest::Blocking {
suppress_tool_call, ..
} => *suppress_tool_call,
InferenceRequest::Streaming {
suppress_tool_call, ..
} => *suppress_tool_call,
}
}
pub fn disable_mtp(&self) -> bool {
match self {
InferenceRequest::Blocking { disable_mtp, .. } => *disable_mtp,
InferenceRequest::Streaming { disable_mtp, .. } => *disable_mtp,
}
}
pub fn seed(&self) -> Option<u64> {
match self {
InferenceRequest::Blocking { seed, .. } => *seed,
InferenceRequest::Streaming { seed, .. } => *seed,
}
}
pub fn take_grammar_spec(&mut self) -> Option<GrammarSpec> {
match self {
InferenceRequest::Blocking { grammar_spec, .. } => grammar_spec.take(),
InferenceRequest::Streaming { grammar_spec, .. } => grammar_spec.take(),
}
}
pub fn min_tokens(&self) -> usize {
match self {
InferenceRequest::Blocking { min_tokens, .. } => *min_tokens,
InferenceRequest::Streaming { min_tokens, .. } => *min_tokens,
}
}
pub fn top_logprobs(&self) -> Option<u8> {
match self {
InferenceRequest::Blocking { top_logprobs, .. } => *top_logprobs,
InferenceRequest::Streaming { top_logprobs, .. } => *top_logprobs,
}
}
pub fn prompt_logprobs(&self) -> Option<u8> {
match self {
InferenceRequest::Blocking {
prompt_logprobs, ..
} => *prompt_logprobs,
InferenceRequest::Streaming {
prompt_logprobs, ..
} => *prompt_logprobs,
}
}
pub fn timeout_at(&self) -> Option<std::time::Instant> {
match self {
InferenceRequest::Blocking { timeout_at, .. } => *timeout_at,
InferenceRequest::Streaming { timeout_at, .. } => *timeout_at,
}
}
}
pub(crate) fn tokenize_stop_sequences(
tokenizer: &crate::tokenizer::ChatTokenizer,
stops: &[String],
) -> Vec<u32> {
let mut tokens = Vec::new();
for s in stops {
match tokenizer.encode(s) {
Ok(ids) if ids.len() == 1 => tokens.push(ids[0]),
Ok(ids) if ids.len() > 1 => {
tracing::info!(
"Multi-token stop '{}' ({} tokens) — use string matching",
s,
ids.len()
);
}
Ok(_) => {}
Err(e) => tracing::warn!("Failed to tokenize stop '{}': {e}", s),
}
}
tokens.sort_unstable();
tokens.dedup();
tokens
}
pub(crate) fn strip_stop_sequences(text: String, stops: &[String]) -> String {
strip_stop_sequences_matched(text, stops).0
}
pub(crate) fn strip_stop_sequences_matched(
mut text: String,
stops: &[String],
) -> (String, Option<String>) {
let mut sorted: Vec<&String> = stops.iter().collect();
sorted.sort_by_key(|s| std::cmp::Reverse(s.len()));
for s in sorted {
if let Some(stripped) = text.strip_suffix(s.as_str()) {
text.truncate(stripped.len());
return (text, Some(s.clone()));
}
}
(text, None)
}
pub(crate) fn extract_thinking(
text: &str,
enable_thinking: bool,
parser: Option<&dyn crate::reasoning_parser::ReasoningParser>,
) -> (Option<String>, String) {
if let Some(p) = parser {
p.extract_thinking(text, enable_thinking)
} else {
(None, text.to_string())
}
}