feat(agent): record tool/turn/provider metrics

This commit is contained in:
xiaoxixi 2026-07-24 17:21:44 +08:00
parent 115e77ff27
commit 5d0cf5b070

View File

@ -491,16 +491,41 @@ impl AgentLoop {
iteration: u32, iteration: u32,
turn: Option<&AgentTurnContext>, turn: Option<&AgentTurnContext>,
) -> Result<ChatCompletionResponse, AgentError> { ) -> Result<ChatCompletionResponse, AgentError> {
let mut provider_stream = self.provider.stream(request).await.map_err(|error| { let metrics = crate::observability::metrics::global_metrics();
tracing::error!(error = %error, "LLM request failed"); let provider_name = self.provider.name().to_string();
AgentError::LlmError(error.to_string()) let provider_model = self.provider.model_id().to_string();
})?; let start = Instant::now();
let mut provider_stream = match self.provider.stream(request).await {
Ok(stream) => stream,
Err(error) => {
tracing::error!(error = %error, "LLM request failed");
metrics.record_provider(
&provider_name,
&provider_model,
None,
start.elapsed().as_millis() as u64,
true,
);
return Err(AgentError::LlmError(error.to_string()));
}
};
let mut accumulator = ProviderResponseAccumulator::default(); let mut accumulator = ProviderResponseAccumulator::default();
while let Some(chunk) = provider_stream.next().await { while let Some(chunk) = provider_stream.next().await {
let chunk = chunk.map_err(|error| { let chunk = match chunk {
tracing::error!(error = %error, "LLM stream failed"); Ok(chunk) => chunk,
AgentError::LlmError(error.to_string()) Err(error) => {
})?; tracing::error!(error = %error, "LLM stream failed");
metrics.record_provider(
&provider_name,
&provider_model,
None,
start.elapsed().as_millis() as u64,
true,
);
return Err(AgentError::LlmError(error.to_string()));
}
};
if let Some(turn) = turn { if let Some(turn) = turn {
let event = match &chunk { let event = match &chunk {
ProviderChunk::Reasoning(delta) => Some(TurnEvent::ReasoningDelta { ProviderChunk::Reasoning(delta) => Some(TurnEvent::ReasoningDelta {
@ -521,7 +546,11 @@ impl AgentLoop {
} }
accumulator.push(chunk); accumulator.push(chunk);
} }
Ok(accumulator.finish()) let response = accumulator.finish();
let latency_ms = start.elapsed().as_millis() as u64;
metrics.record_provider(&provider_name, &provider_model, None, latency_ms, false);
metrics.record_provider_tokens(&provider_name, &response.usage);
Ok(response)
} }
fn annotate_message( fn annotate_message(
@ -615,6 +644,8 @@ impl AgentLoop {
mut messages: Vec<ChatMessage>, mut messages: Vec<ChatMessage>,
turn: Option<AgentTurnContext>, turn: Option<AgentTurnContext>,
) -> Result<AgentProcessResult, AgentError> { ) -> Result<AgentProcessResult, AgentError> {
let turn_start = Instant::now();
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
tracing::debug!( tracing::debug!(
history_len = messages.len(), history_len = messages.len(),
@ -703,6 +734,10 @@ impl AgentLoop {
assistant_message.provider_state = response.provider_state; assistant_message.provider_state = response.provider_state;
Self::annotate_message(&mut assistant_message, turn.as_ref(), iteration, true); Self::annotate_message(&mut assistant_message, turn.as_ref(), iteration, true);
emitted_messages.push(assistant_message.clone()); emitted_messages.push(assistant_message.clone());
crate::observability::metrics::global_metrics().record_turn(
Some(&accumulated_usage),
turn_start.elapsed().as_millis() as u64,
);
return Ok(AgentProcessResult { return Ok(AgentProcessResult {
final_response: assistant_message, final_response: assistant_message,
emitted_messages, emitted_messages,
@ -844,6 +879,10 @@ impl AgentLoop {
true, true,
); );
emitted_messages.push(assistant_message.clone()); emitted_messages.push(assistant_message.clone());
crate::observability::metrics::global_metrics().record_turn(
Some(&accumulated_usage),
turn_start.elapsed().as_millis() as u64,
);
Ok(AgentProcessResult { Ok(AgentProcessResult {
final_response: assistant_message, final_response: assistant_message,
emitted_messages, emitted_messages,
@ -876,6 +915,11 @@ impl AgentLoop {
let mut final_message = ChatMessage::assistant(fallback); let mut final_message = ChatMessage::assistant(fallback);
Self::annotate_message(&mut final_message, turn.as_ref(), summary_iteration, true); Self::annotate_message(&mut final_message, turn.as_ref(), summary_iteration, true);
emitted_messages.push(final_message.clone()); emitted_messages.push(final_message.clone());
let turn_usage = (accumulated_usage.total_tokens > 0).then_some(&accumulated_usage);
crate::observability::metrics::global_metrics().record_turn(
turn_usage,
turn_start.elapsed().as_millis() as u64,
);
Ok(AgentProcessResult { Ok(AgentProcessResult {
final_response: final_message, final_response: final_message,
emitted_messages, emitted_messages,
@ -1016,6 +1060,9 @@ impl AgentLoop {
}); });
} }
crate::observability::metrics::global_metrics()
.record_tool_call(&tool_name, result.success);
// Apply duration // Apply duration
Ok(ToolExecutionOutcome { duration, ..result }) Ok(ToolExecutionOutcome { duration, ..result })
} }