wip: telemetry progress

This commit is contained in:
Yeachan-Heo
2026-04-01 04:40:21 +00:00
parent 5170718306
commit e7e3ae2875
4 changed files with 67 additions and 21 deletions

1
rust/Cargo.lock generated
View File

@@ -1097,6 +1097,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
"telemetry",
"tokio", "tokio",
"walkdir", "walkdir",
] ]

View File

@@ -161,6 +161,7 @@ where
self self
} }
#[allow(clippy::too_many_lines)]
pub fn run_turn( pub fn run_turn(
&mut self, &mut self,
user_input: impl Into<String>, user_input: impl Into<String>,
@@ -197,7 +198,13 @@ where
return Err(error); return Err(error);
} }
}; };
let (assistant_message, usage) = build_assistant_message(events)?; let (assistant_message, usage) = match build_assistant_message(events) {
Ok(result) => result,
Err(error) => {
self.record_turn_failed(iterations, &error);
return Err(error);
}
};
if let Some(usage) = usage { if let Some(usage) = usage {
self.usage_tracker.record(usage); self.usage_tracker.record(usage);
} }
@@ -211,7 +218,11 @@ where
_ => None, _ => None,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.record_assistant_iteration(iterations, &assistant_message, pending_tool_uses.len()); self.record_assistant_iteration(
iterations,
&assistant_message,
pending_tool_uses.len(),
);
self.session.messages.push(assistant_message.clone()); self.session.messages.push(assistant_message.clone());
assistant_messages.push(assistant_message); assistant_messages.push(assistant_message);
@@ -359,7 +370,10 @@ where
"iteration".to_string(), "iteration".to_string(),
Value::from(u64::try_from(iteration).unwrap_or(u64::MAX)), Value::from(u64::try_from(iteration).unwrap_or(u64::MAX)),
); );
attributes.insert("tool_name".to_string(), Value::String(tool_name.to_string())); attributes.insert(
"tool_name".to_string(),
Value::String(tool_name.to_string()),
);
tracer.record("tool_execution_started", attributes); tracer.record("tool_execution_started", attributes);
} }
} }
@@ -396,9 +410,7 @@ where
let mut attributes = Map::new(); let mut attributes = Map::new();
attributes.insert( attributes.insert(
"assistant_message_count".to_string(), "assistant_message_count".to_string(),
Value::from( Value::from(u64::try_from(summary.assistant_messages.len()).unwrap_or(u64::MAX)),
u64::try_from(summary.assistant_messages.len()).unwrap_or(u64::MAX),
),
); );
attributes.insert( attributes.insert(
"tool_result_count".to_string(), "tool_result_count".to_string(),
@@ -554,6 +566,8 @@ mod tests {
use crate::session::{ContentBlock, MessageRole, Session}; use crate::session::{ContentBlock, MessageRole, Session};
use crate::usage::TokenUsage; use crate::usage::TokenUsage;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use telemetry::{MemoryTelemetrySink, SessionTracer, TelemetryEvent};
struct ScriptedApiClient { struct ScriptedApiClient {
call_count: usize, call_count: usize,
@@ -666,6 +680,39 @@ mod tests {
)); ));
} }
#[test]
fn records_runtime_session_trace_events() {
let sink = Arc::new(MemoryTelemetrySink::default());
let tracer = SessionTracer::new("session-runtime", sink.clone());
let mut runtime = ConversationRuntime::new(
Session::new(),
ScriptedApiClient { call_count: 0 },
StaticToolExecutor::new().register("add", |_input| Ok("4".to_string())),
PermissionPolicy::new(PermissionMode::WorkspaceWrite),
vec!["system".to_string()],
)
.with_session_tracer(tracer);
runtime
.run_turn("what is 2 + 2?", Some(&mut PromptAllowOnce))
.expect("conversation loop should succeed");
let events = sink.events();
let trace_names = events
.iter()
.filter_map(|event| match event {
TelemetryEvent::SessionTrace(trace) => Some(trace.name.as_str()),
_ => None,
})
.collect::<Vec<_>>();
assert!(trace_names.contains(&"turn_started"));
assert!(trace_names.contains(&"assistant_iteration_completed"));
assert!(trace_names.contains(&"tool_execution_started"));
assert!(trace_names.contains(&"tool_execution_finished"));
assert!(trace_names.contains(&"turn_completed"));
}
#[test] #[test]
fn records_denied_tool_results_when_prompt_rejects() { fn records_denied_tool_results_when_prompt_rejects() {
struct RejectPrompter; struct RejectPrompter;

View File

@@ -1237,6 +1237,7 @@ impl LiveCli {
let message_count = session.messages.len(); let message_count = session.messages.len();
self.runtime = build_runtime( self.runtime = build_runtime(
session, session,
&self.session.id,
model.clone(), model.clone(),
self.system_prompt.clone(), self.system_prompt.clone(),
true, true,
@@ -1342,7 +1343,7 @@ impl LiveCli {
let message_count = session.messages.len(); let message_count = session.messages.len();
self.runtime = build_runtime( self.runtime = build_runtime(
session, session,
&self.session.id, &handle.id,
self.model.clone(), self.model.clone(),
self.system_prompt.clone(), self.system_prompt.clone(),
true, true,
@@ -1922,6 +1923,7 @@ fn build_runtime_feature_config(
.clone()) .clone())
} }
#[allow(clippy::too_many_arguments)]
fn build_runtime( fn build_runtime(
session: Session, session: Session,
session_id: &str, session_id: &str,
@@ -1935,14 +1937,13 @@ fn build_runtime(
{ {
let session_tracer = build_session_tracer(session_id)?; let session_tracer = build_session_tracer(session_id)?;
let api_client = match session_tracer.clone() { let api_client = match session_tracer.clone() {
Some(session_tracer) => AnthropicRuntimeClient::new( Some(session_tracer) => {
model, AnthropicRuntimeClient::new(model, enable_tools, emit_output, allowed_tools.clone())?
enable_tools, .with_session_tracer(session_tracer)
emit_output, }
allowed_tools.clone(), None => {
)? AnthropicRuntimeClient::new(model, enable_tools, emit_output, allowed_tools.clone())?
.with_session_tracer(session_tracer), }
None => AnthropicRuntimeClient::new(model, enable_tools, emit_output, allowed_tools.clone())?,
}; };
let runtime = ConversationRuntime::new_with_features( let runtime = ConversationRuntime::new_with_features(
session, session,

View File

@@ -489,15 +489,12 @@ mod tests {
#[test] #[test]
fn jsonl_sink_persists_events() { fn jsonl_sink_persists_events() {
let path = std::env::temp_dir().join(format!( let path =
"telemetry-jsonl-{}.log", std::env::temp_dir().join(format!("telemetry-jsonl-{}.log", current_timestamp_ms()));
current_timestamp_ms()
));
let sink = JsonlTelemetrySink::new(&path).expect("sink should create file"); let sink = JsonlTelemetrySink::new(&path).expect("sink should create file");
sink.record(TelemetryEvent::Analytics( sink.record(TelemetryEvent::Analytics(
AnalyticsEvent::new("cli", "turn_completed") AnalyticsEvent::new("cli", "turn_completed").with_property("ok", Value::Bool(true)),
.with_property("ok", Value::Bool(true)),
)); ));
let contents = std::fs::read_to_string(&path).expect("telemetry log should be readable"); let contents = std::fs::read_to_string(&path).expect("telemetry log should be readable");