Skip to main content

sokuto_server/handlers/
systemone.rs

1//! # Jev 互換推論エンドポイントモジュール
2//!
3//! `POST /v1/systemone` に対するリクエスト検証、推論サービス呼び出し、
4//! および型付き判定結果のシリアライズを提供する。
5
6use 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/// TypeSafe AI Jev 互換の非自己回帰型判断推論エンドポイント。
20///
21/// 共通 State と複数質問群を受け取り、単一フォワードパスおよび
22/// 決定プリミティブ数理に基づく型付き確率決定を一括返却する。
23#[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}