|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| use std::sync::Arc;
|
|
|
| use axum::{
|
| extract::State,
|
| http::StatusCode,
|
| response::IntoResponse,
|
| routing::post,
|
| Json, Router,
|
| };
|
| use serde::{Deserialize, Serialize};
|
| use tokio::sync::{mpsc, oneshot};
|
|
|
| use crate::types::{DaemonConfig, VerifyRequest, VerifyResponse, Verdict};
|
|
|
|
|
|
|
| #[derive(Debug, Deserialize)]
|
| pub struct VerifyBody {
|
| pub premise: String,
|
| pub hypothesis: String,
|
| pub chunk_id: String,
|
| }
|
|
|
| #[derive(Debug, Serialize)]
|
| pub struct VerifyReply {
|
| pub score: f32,
|
| pub verdict: String,
|
| pub hash: String,
|
| }
|
|
|
|
|
|
|
| pub struct AppState {
|
| pub tx: mpsc::Sender<VerifyRequest>,
|
| pub cfg: Arc<DaemonConfig>,
|
| pub tokenizer: Arc<tokenizers::Tokenizer>,
|
| }
|
|
|
|
|
|
|
|
|
|
|
| fn encode_pair(
|
| tokenizer: &tokenizers::Tokenizer,
|
| premise: &str,
|
| hypothesis: &str,
|
| max_length: usize,
|
| ) -> (Vec<i64>, Vec<i64>, Vec<i64>) {
|
| use tokenizers::EncodeInput;
|
|
|
| let encoding = tokenizer
|
| .encode(
|
| EncodeInput::Dual(
|
| tokenizers::InputSequence::Raw(premise.into()),
|
| tokenizers::InputSequence::Raw(hypothesis.into()),
|
| ),
|
| true,
|
| )
|
| .expect("tokenisation failed");
|
|
|
| let ids: Vec<i64> = encoding.get_ids().iter().map(|&x| x as i64).collect();
|
| let mask: Vec<i64> = encoding.get_attention_mask().iter().map(|&x| x as i64).collect();
|
| let types: Vec<i64> = encoding.get_type_ids().iter().map(|&x| x as i64).collect();
|
|
|
|
|
| let trunc = |v: Vec<i64>| v.into_iter().take(max_length).collect::<Vec<_>>();
|
| (trunc(ids), trunc(mask), trunc(types))
|
| }
|
|
|
|
|
|
|
| async fn verify_handler(
|
| State(state): State<Arc<AppState>>,
|
| Json(body): Json<VerifyBody>,
|
| ) -> impl IntoResponse {
|
| let (input_ids, attention_mask, token_type_ids) = encode_pair(
|
| &state.tokenizer,
|
| &body.premise,
|
| &body.hypothesis,
|
| 512,
|
| );
|
|
|
| let (resp_tx, resp_rx) = oneshot::channel::<VerifyResponse>();
|
|
|
| let request = VerifyRequest {
|
| input_ids,
|
| attention_mask,
|
| token_type_ids,
|
| chunk_id: body.chunk_id,
|
| claim_text: body.hypothesis,
|
| responder: resp_tx,
|
| };
|
|
|
| if state.tx.send(request).await.is_err() {
|
| return (
|
| StatusCode::SERVICE_UNAVAILABLE,
|
| Json(serde_json::json!({"error": "inference daemon unavailable"})),
|
| );
|
| }
|
|
|
| match resp_rx.await {
|
| Ok(resp) => {
|
| let hash_hex = resp.attestation_hash
|
| .iter()
|
| .map(|b| format!("{:02x}", b))
|
| .collect::<String>();
|
| (
|
| StatusCode::OK,
|
| Json(serde_json::json!({
|
| "score": resp.entailment_score,
|
| "verdict": format!("{:?}", resp.label),
|
| "hash": hash_hex,
|
| })),
|
| )
|
| }
|
| Err(_) => (
|
| StatusCode::INTERNAL_SERVER_ERROR,
|
| Json(serde_json::json!({"error": "inference worker dropped"})),
|
| ),
|
| }
|
| }
|
|
|
|
|
|
|
| async fn health_handler() -> impl IntoResponse {
|
| Json(serde_json::json!({"status": "ok"}))
|
| }
|
|
|
|
|
|
|
| pub fn build_router(state: Arc<AppState>) -> Router {
|
| Router::new()
|
| .route("/verify", post(verify_handler))
|
| .route("/health", axum::routing::get(health_handler))
|
| .with_state(state)
|
| }
|
|
|