use lc_core::observability::{MetricsSink, ObsError, ObsEvent};
#[derive(Clone)]
pub struct MongoSink {
collection: mongodb::Collection<serde_json::Value>,
}
impl MongoSink {
pub async fn new(uri: &str, database: &str, collection: &str) -> Result<Self, ObsError> {
let client = mongodb::Client::with_uri_str(uri)
.await
.map_err(|e| ObsError::Transport(format!("mongo: connect {uri}: {e}")))?;
Ok(Self::with_client(&client, database, collection))
}
pub fn with_client(client: &mongodb::Client, database: &str, collection: &str) -> Self {
Self {
collection: client
.database(database)
.collection::<serde_json::Value>(collection),
}
}
}
#[async_trait::async_trait]
impl MetricsSink for MongoSink {
async fn export(&self, event: &ObsEvent) -> Result<(), ObsError> {
let doc = serde_json::to_value(event).map_err(|e| ObsError::Encode(e.to_string()))?;
self.collection
.insert_one(doc, None)
.await
.map_err(|e| ObsError::Transport(format!("mongo: insert: {e}")))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use lc_core::language_models::TokenUsage;
#[tokio::test]
#[ignore = "requires a running MongoDB (set MONGODB_URI)"]
async fn mongo_inserts_document() {
let uri =
std::env::var("MONGODB_URI").unwrap_or_else(|_| "mongodb://localhost:27017".into());
let sink = MongoSink::new(&uri, "lc_obs_test", "events").await.unwrap();
sink.export(&ObsEvent::TokenUsage(TokenUsage {
prompt_tokens: 1,
completion_tokens: 1,
total_tokens: 2,
}))
.await
.unwrap();
}
}