#![allow(unused_imports, dead_code)]
use crate::main_modules::model_host::CurrentModel;
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 super::chat_stream::run_chat_stream;
use super::responses_stream::responses_endpoint_stream;
use super::responses_translate::{
build_responses_usage, emit, find_frame_end, translate_chat_response_to_responses,
};
use super::stored::assistant_incoming_from_ir;
use crate::AppState;
use crate::openai::{
ChatCompletionChunk, ChatCompletionRequest, ChatCompletionResponse, CompletionChunk,
CompletionRequest, CompletionResponse, ModelInfo, ModelListResponse, Usage,
};
use crate::tool_parser;
use super::chat::chat_completions_inner;
use super::compact::{compact_messages, openai_error_response, openai_error_response_with_param};
use super::completions::not_supported;
use super::inference_impl::{extract_thinking, strip_stop_sequences, tokenize_stop_sequences};
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::strip::strip_thinking_tags;
use super::inference_types::*;
use super::sanitizer::*;
pub async fn cancel_response(axum::extract::Path(id): axum::extract::Path<String>) -> Response {
openai_error_response_with_param(
StatusCode::BAD_REQUEST,
format!(
"Response '{id}' cannot be cancelled: Metrale Engine completes responses synchronously. Cancel only applies when the request was created with `background: true`, which this server does not support."
),
Some("id"),
Some("response_not_cancellable"),
)
}
pub async fn metrics_handler() -> impl IntoResponse {
use prometheus::Encoder;
use std::fmt::Write;
let encoder = prometheus::TextEncoder::new();
let metric_families = prometheus::gather();
let mut buffer = Vec::new();
encoder.encode(&metric_families, &mut buffer).unwrap();
let mut text = String::from_utf8(buffer).unwrap_or_default();
let hits = metrale_telemetry::prefix_cache::cache_hit_count();
let misses = metrale_telemetry::prefix_cache::cache_miss_count();
let hit_tokens = metrale_telemetry::prefix_cache::cache_hit_tokens_total();
let total = hits + misses;
let hit_rate = if total > 0 {
hits as f64 / total as f64
} else {
0.0
};
let _ = write!(
text,
"\
# HELP metrale_prefix_cache_hits_total Prefix cache lookups that found cached blocks\n\
# TYPE metrale_prefix_cache_hits_total counter\n\
metrale_prefix_cache_hits_total {hits}\n\
# HELP metrale_prefix_cache_misses_total Prefix cache lookups with no match\n\
# TYPE metrale_prefix_cache_misses_total counter\n\
metrale_prefix_cache_misses_total {misses}\n\
# HELP metrale_prefix_cache_hit_tokens_total Tokens reused from prefix cache\n\
# TYPE metrale_prefix_cache_hit_tokens_total counter\n\
metrale_prefix_cache_hit_tokens_total {hit_tokens}\n\
# HELP metrale_prefix_cache_hit_rate Prefix cache hit rate (0-1)\n\
# TYPE metrale_prefix_cache_hit_rate gauge\n\
metrale_prefix_cache_hit_rate {hit_rate:.4}\n"
);
let entropy = metrale_sampling::last_entropy();
let low_entropy = metrale_sampling::low_entropy_token_count();
let total_sampled = metrale_sampling::total_sampled_token_count();
let low_ratio = if total_sampled > 0 {
low_entropy as f64 / total_sampled as f64
} else {
0.0
};
let unresolved = metrale_telemetry::kernel_audit::unresolved_lookups();
let _ = write!(
text,
"\
# HELP metrale_kernel_lookups_unresolved Kernel lookups that did not resolve for the live model\n\
# TYPE metrale_kernel_lookups_unresolved gauge\n\
metrale_kernel_lookups_unresolved {unresolved}\n"
);
let _ = write!(
text,
"\
# HELP metrale_token_entropy_last Most recent per-token entropy (nats)\n\
# TYPE metrale_token_entropy_last gauge\n\
metrale_token_entropy_last {entropy:.4}\n\
# HELP metrale_low_entropy_tokens_total Tokens with entropy below 0.3\n\
# TYPE metrale_low_entropy_tokens_total counter\n\
metrale_low_entropy_tokens_total {low_entropy}\n\
# HELP metrale_low_entropy_ratio Fraction of tokens with entropy below 0.3\n\
# TYPE metrale_low_entropy_ratio gauge\n\
metrale_low_entropy_ratio {low_ratio:.4}\n"
);
text.push_str(&metrale_telemetry::export::prometheus::render(
metrale_telemetry::global(),
&metrale_telemetry::launch_trace::kernel_name,
));
(
[(
axum::http::header::CONTENT_TYPE,
"text/plain; version=0.0.4; charset=utf-8",
)],
text,
)
}
pub(crate) fn readiness(
model: Option<&str>,
fault: Option<&str>,
) -> (StatusCode, serde_json::Value) {
if let Some(reason) = fault {
return (
StatusCode::SERVICE_UNAVAILABLE,
serde_json::json!({"status": "faulted", "reason": reason}),
);
}
match model {
Some(name) => (
StatusCode::OK,
serde_json::json!({"status": "ready", "model": name}),
),
None => (
StatusCode::SERVICE_UNAVAILABLE,
serde_json::json!({"status": "loading"}),
),
}
}
pub async fn health(
State(host): State<Arc<crate::main_modules::model_host::ModelHost>>,
) -> Response {
let state = host.current();
let (code, body) = readiness(
state.as_ref().map(|s| s.model_name.as_str()),
metrale_core::fault::global().fault(),
);
(code, Json(body)).into_response()
}
pub async fn health_live() -> Response {
match metrale_core::fault::global().fault() {
None => "ok".into_response(),
Some(reason) => (
StatusCode::SERVICE_UNAVAILABLE,
Json(serde_json::json!({"status": "faulted", "reason": reason})),
)
.into_response(),
}
}
pub async fn hardware() -> Response {
let hw = tokio::task::spawn_blocking(metrale_bench::hardware::Hardware::probe)
.await
.unwrap_or_else(|_| metrale_bench::hardware::Hardware::unknown());
Json(hw).into_response()
}
pub async fn serve_config() -> Response {
let id =
tokio::task::spawn_blocking(|| metrale_bench::serve_identity::this_process().clone()).await;
match id {
Ok(id) => Json(id).into_response(),
Err(e) => (
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({"error": format!("{e}")})),
)
.into_response(),
}
}
pub async fn forward(CurrentModel(state): CurrentModel) -> Response {
Json(state.forward.clone()).into_response()
}
pub async fn memory(CurrentModel(state): CurrentModel) -> Response {
Json(state.memory.report(state.memory.readings())).into_response()
}
pub async fn tokenize(
CurrentModel(state): CurrentModel,
req: Result<Json<crate::openai::TokenizeRequest>, JsonRejection>,
) -> Response {
let Json(req) = match req {
Ok(r) => r,
Err(e) => {
return openai_error_response(
StatusCode::BAD_REQUEST,
format!("Invalid request JSON: {e}"),
);
}
};
let tokens = if let Some(ref prompt) = req.prompt {
match state.tokenizer.encode(prompt) {
Ok(t) => t,
Err(e) => {
return openai_error_response(
StatusCode::BAD_REQUEST,
format!("Tokenization error: {e}"),
);
}
}
} else if let Some(ref messages) = req.messages {
let json_messages: Vec<serde_json::Value> = messages
.iter()
.map(|m| serde_json::json!({"role": m.role, "content": m.content.text}))
.collect();
match state.tokenizer.apply_chat_template_jinja_with_effort(
&json_messages,
None,
false,
state.behavior.disable_tool_steering,
None,
crate::tokenizer::ThinkingVars {
preserve_thinking: state.behavior.preserve_thinking,
thinking: None,
},
) {
Ok(t) => t,
Err(e) => {
return openai_error_response(
StatusCode::BAD_REQUEST,
format!("Tokenization error: {e}"),
);
}
}
} else {
return openai_error_response(
StatusCode::BAD_REQUEST,
"Either 'prompt' or 'messages' is required".to_string(),
);
};
let count = tokens.len();
Json(crate::openai::TokenizeResponse { tokens, count }).into_response()
}
#[derive(serde::Deserialize)]
pub struct DetokenizeRequest {
tokens: Vec<u32>,
}
pub async fn detokenize(
CurrentModel(state): CurrentModel,
req: Result<Json<DetokenizeRequest>, JsonRejection>,
) -> Response {
let Json(req) = match req {
Ok(r) => r,
Err(e) => {
return openai_error_response(
StatusCode::BAD_REQUEST,
format!("Invalid request JSON: {e}"),
);
}
};
match state.tokenizer.decode(&req.tokens) {
Ok(text) => Json(serde_json::json!({"text": text})).into_response(),
Err(e) => (
StatusCode::BAD_REQUEST,
Json(serde_json::json!({"error": e.to_string()})),
)
.into_response(),
}
}