mirror of
https://github.com/lWolvesl/claw-code.git
synced 2026-04-02 19:21:51 +08:00
wip: telemetry progress
This commit is contained in:
1
rust/Cargo.lock
generated
1
rust/Cargo.lock
generated
@@ -1097,6 +1097,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"telemetry",
|
||||||
"tokio",
|
"tokio",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user