sokuto_server/handlers/
systemone.rs1use std::sync::Arc;
7use std::time::Instant;
8
9use axum::extract::State;
10use axum::http::HeaderMap;
11use axum::response::IntoResponse;
12use sokuto_core::schema::{SystemOneRequest, SystemOneResponse};
13
14use crate::error::{ErrorResponse, ServerError};
15use crate::metrics::{record_http_duration, record_http_request};
16use crate::service::InferenceService;
17use crate::state::AppState;
18
19#[utoipa::path(
24 post,
25 path = "/v1/systemone",
26 tag = "Inference",
27 request_body = SystemOneRequest,
28 responses(
29 (status = 200, description = "推論および決定解決成功", body = SystemOneResponse),
30 (status = 400, description = "スキーマまたはパラメータ検証違反", body = ErrorResponse),
31 (status = 413, description = "質問数またはペイロード上限超過", body = ErrorResponse),
32 (status = 500, description = "推論エンジンまたはサーバー内部障害", body = ErrorResponse)
33 )
34)]
35pub async fn system_one_handler(
36 State(state): State<Arc<AppState>>,
37 headers: HeaderMap,
38 axum::Json(req): axum::Json<SystemOneRequest>,
39) -> Result<impl IntoResponse, ServerError> {
40 let start_time = Instant::now();
41
42 req.validate()?;
43
44 let client_hash_str = headers
45 .get("x-state-hash")
46 .or_else(|| headers.get("X-State-Hash"))
47 .and_then(|v| v.to_str().ok())
48 .map(str::to_string)
49 .or_else(|| req.state_hash.clone());
50
51 let inference_service = InferenceService::new(state);
52 let output = inference_service.execute(req, client_hash_str).await?;
53
54 let resp_headers = output.to_headers();
55 let mut response = SystemOneResponse::new(output.answers, output.usage);
56 if let Some(summary) = output.routing_summary {
57 response = response.with_routing(summary);
58 }
59
60 let total_duration = start_time.elapsed().as_secs_f64();
61 record_http_duration("/v1/systemone", total_duration);
62 record_http_request("/v1/systemone", 200);
63
64 Ok((resp_headers, axum::Json(response)))
65}