use crate::models::{EnrichedSession, Id, ModelStats, Observation, Session, SummaryStats, Trace};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
pub enum StoreError {
#[error("storage error: {0}")]
Storage(String),
#[error("serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("not found: {0}")]
NotFound(String),
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceQuery {
pub session_id: Option<String>,
pub user_id: Option<String>,
pub name: Option<String>,
pub environment: Option<String>,
pub tags: Vec<String>,
pub from_timestamp: Option<DateTime<Utc>>,
pub to_timestamp: Option<DateTime<Utc>>,
pub page: Option<u32>,
pub page_size: Option<u32>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaginatedResponse<T> {
pub data: Vec<T>,
pub page: u32,
pub page_size: u32,
pub total_count: u64,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DailyStats {
pub date: String,
pub trace_count: u64,
pub observation_count: u64,
pub total_tokens: u64,
pub total_cost: f64,
pub total_duration_ms: u64,
}
#[async_trait::async_trait]
pub trait TraceStore: Send + Sync + 'static {
async fn upsert_trace(&self, trace: &Trace) -> Result<(), StoreError>;
async fn insert_observation(&self, observation: &Observation) -> Result<(), StoreError>;
async fn upsert_session(&self, session: &Session) -> Result<(), StoreError>;
async fn get_trace(&self, id: Id) -> Result<Option<TraceWithObservations>, StoreError>;
async fn query_traces(
&self,
query: &TraceQuery,
) -> Result<PaginatedResponse<Trace>, StoreError>;
async fn get_session(&self, id: &str) -> Result<Option<Session>, StoreError>;
async fn query_sessions(
&self,
page: u32,
page_size: u32,
) -> Result<PaginatedResponse<Session>, StoreError>;
async fn get_daily_stats(
&self,
from: DateTime<Utc>,
to: DateTime<Utc>,
) -> Result<Vec<DailyStats>, StoreError>;
async fn get_model_stats(&self) -> Result<Vec<ModelStats>, StoreError>;
async fn get_summary_stats(&self) -> Result<SummaryStats, StoreError>;
async fn query_enriched_sessions(
&self,
page: u32,
page_size: u32,
) -> Result<PaginatedResponse<EnrichedSession>, StoreError>;
async fn flush(&self) -> Result<(), StoreError>;
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TraceWithObservations {
pub trace: Trace,
pub observations: Vec<Observation>,
}