tirea_agentos_server/service/
api.rs

1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
3use axum::Json;
4use std::sync::Arc;
5use tirea_agentos::contracts::storage::{MailboxStore, ThreadReader};
6use tirea_agentos::runtime::{AgentOs, AgentOsRunError};
7
8use super::mailbox_service::MailboxService;
9
10#[derive(Clone)]
11pub struct AppState {
12    pub os: Arc<AgentOs>,
13    pub read_store: Arc<dyn ThreadReader>,
14    pub mailbox_service: Arc<MailboxService>,
15}
16
17impl AppState {
18    pub fn new(
19        os: Arc<AgentOs>,
20        read_store: Arc<dyn ThreadReader>,
21        mailbox_service: Arc<MailboxService>,
22    ) -> Self {
23        Self {
24            os,
25            read_store,
26            mailbox_service,
27        }
28    }
29
30    pub fn mailbox_store(&self) -> &Arc<dyn MailboxStore> {
31        self.mailbox_service.mailbox_store()
32    }
33}
34
35#[derive(Debug, thiserror::Error)]
36pub enum ApiError {
37    #[error("agent not found: {0}")]
38    AgentNotFound(String),
39
40    #[error("thread not found: {0}")]
41    ThreadNotFound(String),
42
43    #[error("run not found: {0}")]
44    RunNotFound(String),
45
46    #[error("bad request: {0}")]
47    BadRequest(String),
48
49    #[error("internal error: {0}")]
50    Internal(String),
51}
52
53impl IntoResponse for ApiError {
54    fn into_response(self) -> Response {
55        let (code, msg) = match &self {
56            ApiError::AgentNotFound(_) => (StatusCode::NOT_FOUND, self.to_string()),
57            ApiError::ThreadNotFound(_) => (StatusCode::NOT_FOUND, self.to_string()),
58            ApiError::RunNotFound(_) => (StatusCode::NOT_FOUND, self.to_string()),
59            ApiError::BadRequest(_) => (StatusCode::BAD_REQUEST, self.to_string()),
60            ApiError::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
61        };
62        let body = Json(serde_json::json!({ "error": msg }));
63        (code, body).into_response()
64    }
65}
66
67impl From<AgentOsRunError> for ApiError {
68    fn from(e: AgentOsRunError) -> Self {
69        match e {
70            AgentOsRunError::Resolve(
71                tirea_agentos::runtime::AgentOsResolveError::AgentNotFound(id),
72            ) => ApiError::AgentNotFound(id),
73            AgentOsRunError::Resolve(other) => ApiError::BadRequest(other.to_string()),
74            other => ApiError::Internal(other.to_string()),
75        }
76    }
77}
78
79pub fn normalize_optional_id(value: Option<String>) -> Option<String> {
80    value.and_then(|raw| {
81        let trimmed = raw.trim();
82        if trimmed.is_empty() {
83            None
84        } else {
85            Some(trimmed.to_string())
86        }
87    })
88}