From 8371f412f8f87cad7f2a71a515ce3613cb1e0c71 Mon Sep 17 00:00:00 2001 From: Chummy Date: Tue, 17 Feb 2026 17:57:34 +0800 Subject: [PATCH] feat(observability): propagate optional cost_usd on agent end --- src/agent/agent.rs | 1 + src/agent/loop_.rs | 1 + src/observability/log.rs | 5 ++++- src/observability/noop.rs | 2 ++ src/observability/otel.rs | 6 ++++++ src/observability/traits.rs | 1 + 6 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/agent/agent.rs b/src/agent/agent.rs index 05a9837..23c0cbf 100644 --- a/src/agent/agent.rs +++ b/src/agent/agent.rs @@ -557,6 +557,7 @@ pub async fn run( agent.observer.record_event(&ObserverEvent::AgentEnd { duration: start.elapsed(), tokens_used: None, + cost_usd: None, }); Ok(()) diff --git a/src/agent/loop_.rs b/src/agent/loop_.rs index 47d02a6..8356d33 100644 --- a/src/agent/loop_.rs +++ b/src/agent/loop_.rs @@ -1048,6 +1048,7 @@ pub async fn run( observer.record_event(&ObserverEvent::AgentEnd { duration, tokens_used: None, + cost_usd: None, }); Ok(final_output) diff --git a/src/observability/log.rs b/src/observability/log.rs index 9e3d062..b932fe0 100644 --- a/src/observability/log.rs +++ b/src/observability/log.rs @@ -48,9 +48,10 @@ impl Observer for LogObserver { ObserverEvent::AgentEnd { duration, tokens_used, + cost_usd, } => { let ms = u64::try_from(duration.as_millis()).unwrap_or(u64::MAX); - info!(duration_ms = ms, tokens = ?tokens_used, "agent.end"); + info!(duration_ms = ms, tokens = ?tokens_used, cost_usd = ?cost_usd, "agent.end"); } ObserverEvent::ToolCallStart { tool } => { info!(tool = %tool, "tool.start"); @@ -133,10 +134,12 @@ mod tests { obs.record_event(&ObserverEvent::AgentEnd { duration: Duration::from_millis(500), tokens_used: Some(100), + cost_usd: Some(0.0015), }); obs.record_event(&ObserverEvent::AgentEnd { duration: Duration::ZERO, tokens_used: None, + cost_usd: None, }); obs.record_event(&ObserverEvent::ToolCallStart { tool: "shell".into(), diff --git a/src/observability/noop.rs b/src/observability/noop.rs index 1189490..004af21 100644 --- a/src/observability/noop.rs +++ b/src/observability/noop.rs @@ -48,10 +48,12 @@ mod tests { obs.record_event(&ObserverEvent::AgentEnd { duration: Duration::from_millis(100), tokens_used: Some(42), + cost_usd: Some(0.001), }); obs.record_event(&ObserverEvent::AgentEnd { duration: Duration::ZERO, tokens_used: None, + cost_usd: None, }); obs.record_event(&ObserverEvent::ToolCallStart { tool: "shell".into(), diff --git a/src/observability/otel.rs b/src/observability/otel.rs index 5e0c37e..ae4932d 100644 --- a/src/observability/otel.rs +++ b/src/observability/otel.rs @@ -227,6 +227,7 @@ impl Observer for OtelObserver { ObserverEvent::AgentEnd { duration, tokens_used, + cost_usd, } => { let secs = duration.as_secs_f64(); let start_time = SystemTime::now() @@ -243,6 +244,9 @@ impl Observer for OtelObserver { if let Some(t) = tokens_used { span.set_attribute(KeyValue::new("tokens_used", *t as i64)); } + if let Some(c) = cost_usd { + span.set_attribute(KeyValue::new("cost_usd", *c)); + } span.end(); self.agent_duration.record(secs, &[]); @@ -394,10 +398,12 @@ mod tests { obs.record_event(&ObserverEvent::AgentEnd { duration: Duration::from_millis(500), tokens_used: Some(100), + cost_usd: Some(0.0015), }); obs.record_event(&ObserverEvent::AgentEnd { duration: Duration::ZERO, tokens_used: None, + cost_usd: None, }); obs.record_event(&ObserverEvent::ToolCallStart { tool: "shell".into(), diff --git a/src/observability/traits.rs b/src/observability/traits.rs index a1eb10f..6fb114f 100644 --- a/src/observability/traits.rs +++ b/src/observability/traits.rs @@ -27,6 +27,7 @@ pub enum ObserverEvent { AgentEnd { duration: Duration, tokens_used: Option, + cost_usd: Option, }, /// A tool call is about to be executed. ToolCallStart {