use std::sync::Arc;
use std::time::Instant;
use async_trait::async_trait;
use llmkit_core::{
ChatRequest, ChatResponse, ChatStream, CostEstimate, EmbedRequest, EmbedResponse, LlmProvider,
LlmResult,
};
use crate::layer::LlmLayer;
#[derive(Debug, Clone, Copy, Default)]
pub struct TracingLayer;
impl TracingLayer {
pub fn new() -> Self {
Self
}
}
impl LlmLayer for TracingLayer {
type Provider = Tracing;
fn layer(self, inner: Arc<dyn LlmProvider>) -> Tracing {
Tracing { inner }
}
}
pub struct Tracing {
inner: Arc<dyn LlmProvider>,
}
#[async_trait]
impl LlmProvider for Tracing {
async fn chat(&self, req: ChatRequest) -> LlmResult<ChatResponse> {
let span = tracing::info_span!("llm.chat", provider = self.inner.name(), model = self.inner.model());
let _e = span.enter();
let start = Instant::now();
let result = self.inner.chat(req).await;
let latency_ms = start.elapsed().as_millis() as u64;
match &result {
Ok(resp) => tracing::info!(
latency_ms,
prompt_tokens = resp.usage.prompt,
completion_tokens = resp.usage.completion,
"chat completed"
),
Err(e) => tracing::warn!(latency_ms, error = %e, "chat failed"),
}
result
}
async fn chat_stream(&self, req: ChatRequest) -> LlmResult<ChatStream> {
let span = tracing::info_span!("llm.chat_stream", provider = self.inner.name(), model = self.inner.model());
let _e = span.enter();
tracing::info!("stream opened");
self.inner.chat_stream(req).await
}
async fn embed(&self, req: EmbedRequest) -> LlmResult<EmbedResponse> {
let span = tracing::info_span!("llm.embed", provider = self.inner.name());
let _e = span.enter();
let start = Instant::now();
let result = self.inner.embed(req).await;
let latency_ms = start.elapsed().as_millis() as u64;
match &result {
Ok(resp) => tracing::info!(latency_ms, count = resp.embeddings.len(), "embed completed"),
Err(e) => tracing::warn!(latency_ms, error = %e, "embed failed"),
}
result
}
fn name(&self) -> &'static str {
self.inner.name()
}
fn model(&self) -> &str {
self.inner.model()
}
fn estimate_cost(&self, req: &ChatRequest) -> Option<CostEstimate> {
self.inner.estimate_cost(req)
}
}