增加LLM相关错误处理

This commit is contained in:
xiaoski 2026-05-13 08:59:23 +08:00
parent 3d42f22f83
commit 99a57a816a
3 changed files with 80 additions and 9 deletions

View File

@ -2,6 +2,7 @@ use async_trait::async_trait;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
use crate::bus::message::ContentBlock;
use super::{ChatCompletionRequest, ChatCompletionResponse, LLMProvider, Tool, ToolCall};
@ -9,6 +10,8 @@ use super::traits::Usage;
use std::sync::Arc;
use crate::storage::Storage;
const LLM_REQUEST_TIMEOUT_SECS: u64 = 300;
fn convert_content_blocks(blocks: &[ContentBlock]) -> Vec<serde_json::Value> {
blocks.iter().map(|b| match b {
ContentBlock::Text { text } => {
@ -72,7 +75,10 @@ impl AnthropicProvider {
model_extra: HashMap<String, serde_json::Value>,
) -> Self {
Self {
client: Client::new(),
client: Client::builder()
.timeout(Duration::from_secs(LLM_REQUEST_TIMEOUT_SECS))
.build()
.unwrap_or_else(|_| Client::new()),
name,
api_key,
base_url,
@ -238,7 +244,19 @@ impl LLMProvider for AnthropicProvider {
let req_body_str = serde_json::to_string_pretty(&body).unwrap_or_default();
tracing::debug!(req_body = %req_body_str, "LLM request");
let resp = req_builder.json(&body).send().await?;
let resp = req_builder.json(&body).send().await
.inspect_err(|e| {
let is_timeout = e.is_timeout();
tracing::error!(
provider = %self.name,
model = %self.model_id,
url = %url,
timeout = is_timeout,
error = %e,
elapsed_ms = %start.elapsed().as_millis(),
"LLM API request failed"
);
})?;
let status = resp.status();
let body_text = resp.text().await?;
@ -254,6 +272,14 @@ impl LLMProvider for AnthropicProvider {
.map(|s| s.to_string())
})
.unwrap_or_else(|| body_text.clone());
tracing::error!(
provider = %self.name,
model = %self.model_id,
http_status = %status,
error = %error_msg,
elapsed_ms = %start.elapsed().as_millis(),
"LLM API returned error"
);
if let Some(ref storage) = self.storage {
let _ = storage.append_llm_call(
&self.name, &self.model_id, &req_body_str,

View File

@ -3,6 +3,7 @@ use reqwest::Client;
use serde::Deserialize;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::time::Duration;
use crate::bus::message::ContentBlock;
use super::{ChatCompletionRequest, ChatCompletionResponse, LLMProvider, ToolCall};
@ -10,6 +11,8 @@ use super::traits::Usage;
use std::sync::Arc;
use crate::storage::Storage;
const LLM_REQUEST_TIMEOUT_SECS: u64 = 300;
fn convert_content_blocks(blocks: &[ContentBlock]) -> Value {
if blocks.len() == 1
&& let ContentBlock::Text { text } = &blocks[0] {
@ -48,7 +51,10 @@ impl OpenAIProvider {
model_extra: HashMap<String, serde_json::Value>,
) -> Self {
Self {
client: Client::new(),
client: Client::builder()
.timeout(Duration::from_secs(LLM_REQUEST_TIMEOUT_SECS))
.build()
.unwrap_or_else(|_| Client::new()),
name,
api_key,
base_url,
@ -206,7 +212,19 @@ impl LLMProvider for OpenAIProvider {
let req_body_str = serde_json::to_string_pretty(&body).unwrap_or_default();
tracing::debug!(req_body = %req_body_str, "LLM request");
let resp = req_builder.json(&body).send().await?;
let resp = req_builder.json(&body).send().await
.inspect_err(|e| {
let is_timeout = e.is_timeout();
tracing::error!(
provider = %self.name,
model = %self.model_id,
url = %url,
timeout = is_timeout,
error = %e,
elapsed_ms = %start.elapsed().as_millis(),
"LLM API request failed"
);
})?;
let status = resp.status();
let text = resp.text().await?;
@ -214,6 +232,14 @@ impl LLMProvider for OpenAIProvider {
if !status.is_success() {
let error = format!("API error {}: {}", status, text);
tracing::error!(
provider = %self.name,
model = %self.model_id,
http_status = %status,
error = %error,
elapsed_ms = %start.elapsed().as_millis(),
"LLM API returned error"
);
if let Some(ref storage) = self.storage
&& let Err(e) = storage.append_llm_call(
&self.name, &self.model_id, &req_body_str,

View File

@ -1360,7 +1360,10 @@ impl SessionManager {
let result = session_guard.compressor
.compress_if_needed(history)
.await?;
.await
.inspect_err(|e| {
tracing::warn!(error = %e, "Context compression failed in handle_message");
})?;
if result.created_timelines {
session_guard.last_compressed_message_at = Some(chrono::Utc::now().timestamp_millis());
}
@ -1401,9 +1404,19 @@ impl SessionManager {
}
let mut retry = retry_result.history;
retry.insert(0, ChatMessage::system(system_prompt));
agent.process(retry).await?
agent.process(retry).await
.inspect_err(|e| {
tracing::error!(error = %e, "Agent retry after context compression failed");
})?
}
Err(e) => return Err(e),
Err(e) => {
tracing::error!(
error = %e,
elapsed = ?"LLM call in handle_message failed",
"Agent processing error — propagating to caller"
);
return Err(e);
},
};
for msg in result.emitted_messages {
@ -1515,13 +1528,19 @@ impl SessionManager {
// in context compression (system prompt is dynamic and should not be persisted).
let mut history = session_guard.compressor
.compress_if_needed(history)
.await?
.await
.inspect_err(|e| {
tracing::warn!(error = %e, "Context compression failed in handle_cron_message");
})?
.history;
history.insert(0, ChatMessage::system(full_system_prompt));
let agent = session_guard.create_agent_with_notify(notify_tx)?;
let result = agent.process(history).await?;
let result = agent.process(history).await
.inspect_err(|e| {
tracing::error!(error = %e, "Agent processing error in handle_cron_message");
})?;
for msg in result.emitted_messages {
session_guard.add_message(msg, true).await