增加LLM相关错误处理
This commit is contained in:
parent
3d42f22f83
commit
99a57a816a
@ -2,6 +2,7 @@ use async_trait::async_trait;
|
|||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::bus::message::ContentBlock;
|
use crate::bus::message::ContentBlock;
|
||||||
use super::{ChatCompletionRequest, ChatCompletionResponse, LLMProvider, Tool, ToolCall};
|
use super::{ChatCompletionRequest, ChatCompletionResponse, LLMProvider, Tool, ToolCall};
|
||||||
@ -9,6 +10,8 @@ use super::traits::Usage;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
|
|
||||||
|
const LLM_REQUEST_TIMEOUT_SECS: u64 = 300;
|
||||||
|
|
||||||
fn convert_content_blocks(blocks: &[ContentBlock]) -> Vec<serde_json::Value> {
|
fn convert_content_blocks(blocks: &[ContentBlock]) -> Vec<serde_json::Value> {
|
||||||
blocks.iter().map(|b| match b {
|
blocks.iter().map(|b| match b {
|
||||||
ContentBlock::Text { text } => {
|
ContentBlock::Text { text } => {
|
||||||
@ -72,7 +75,10 @@ impl AnthropicProvider {
|
|||||||
model_extra: HashMap<String, serde_json::Value>,
|
model_extra: HashMap<String, serde_json::Value>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
client: Client::new(),
|
client: Client::builder()
|
||||||
|
.timeout(Duration::from_secs(LLM_REQUEST_TIMEOUT_SECS))
|
||||||
|
.build()
|
||||||
|
.unwrap_or_else(|_| Client::new()),
|
||||||
name,
|
name,
|
||||||
api_key,
|
api_key,
|
||||||
base_url,
|
base_url,
|
||||||
@ -238,7 +244,19 @@ impl LLMProvider for AnthropicProvider {
|
|||||||
let req_body_str = serde_json::to_string_pretty(&body).unwrap_or_default();
|
let req_body_str = serde_json::to_string_pretty(&body).unwrap_or_default();
|
||||||
tracing::debug!(req_body = %req_body_str, "LLM request");
|
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 status = resp.status();
|
||||||
let body_text = resp.text().await?;
|
let body_text = resp.text().await?;
|
||||||
@ -254,6 +272,14 @@ impl LLMProvider for AnthropicProvider {
|
|||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| body_text.clone());
|
.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 {
|
if let Some(ref storage) = self.storage {
|
||||||
let _ = storage.append_llm_call(
|
let _ = storage.append_llm_call(
|
||||||
&self.name, &self.model_id, &req_body_str,
|
&self.name, &self.model_id, &req_body_str,
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use reqwest::Client;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::bus::message::ContentBlock;
|
use crate::bus::message::ContentBlock;
|
||||||
use super::{ChatCompletionRequest, ChatCompletionResponse, LLMProvider, ToolCall};
|
use super::{ChatCompletionRequest, ChatCompletionResponse, LLMProvider, ToolCall};
|
||||||
@ -10,6 +11,8 @@ use super::traits::Usage;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
|
|
||||||
|
const LLM_REQUEST_TIMEOUT_SECS: u64 = 300;
|
||||||
|
|
||||||
fn convert_content_blocks(blocks: &[ContentBlock]) -> Value {
|
fn convert_content_blocks(blocks: &[ContentBlock]) -> Value {
|
||||||
if blocks.len() == 1
|
if blocks.len() == 1
|
||||||
&& let ContentBlock::Text { text } = &blocks[0] {
|
&& let ContentBlock::Text { text } = &blocks[0] {
|
||||||
@ -48,7 +51,10 @@ impl OpenAIProvider {
|
|||||||
model_extra: HashMap<String, serde_json::Value>,
|
model_extra: HashMap<String, serde_json::Value>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
client: Client::new(),
|
client: Client::builder()
|
||||||
|
.timeout(Duration::from_secs(LLM_REQUEST_TIMEOUT_SECS))
|
||||||
|
.build()
|
||||||
|
.unwrap_or_else(|_| Client::new()),
|
||||||
name,
|
name,
|
||||||
api_key,
|
api_key,
|
||||||
base_url,
|
base_url,
|
||||||
@ -206,7 +212,19 @@ impl LLMProvider for OpenAIProvider {
|
|||||||
let req_body_str = serde_json::to_string_pretty(&body).unwrap_or_default();
|
let req_body_str = serde_json::to_string_pretty(&body).unwrap_or_default();
|
||||||
tracing::debug!(req_body = %req_body_str, "LLM request");
|
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 status = resp.status();
|
||||||
let text = resp.text().await?;
|
let text = resp.text().await?;
|
||||||
@ -214,6 +232,14 @@ impl LLMProvider for OpenAIProvider {
|
|||||||
|
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
let error = format!("API error {}: {}", status, text);
|
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
|
if let Some(ref storage) = self.storage
|
||||||
&& let Err(e) = storage.append_llm_call(
|
&& let Err(e) = storage.append_llm_call(
|
||||||
&self.name, &self.model_id, &req_body_str,
|
&self.name, &self.model_id, &req_body_str,
|
||||||
|
|||||||
@ -1360,7 +1360,10 @@ impl SessionManager {
|
|||||||
|
|
||||||
let result = session_guard.compressor
|
let result = session_guard.compressor
|
||||||
.compress_if_needed(history)
|
.compress_if_needed(history)
|
||||||
.await?;
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
tracing::warn!(error = %e, "Context compression failed in handle_message");
|
||||||
|
})?;
|
||||||
if result.created_timelines {
|
if result.created_timelines {
|
||||||
session_guard.last_compressed_message_at = Some(chrono::Utc::now().timestamp_millis());
|
session_guard.last_compressed_message_at = Some(chrono::Utc::now().timestamp_millis());
|
||||||
}
|
}
|
||||||
@ -1401,9 +1404,19 @@ impl SessionManager {
|
|||||||
}
|
}
|
||||||
let mut retry = retry_result.history;
|
let mut retry = retry_result.history;
|
||||||
retry.insert(0, ChatMessage::system(system_prompt));
|
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 {
|
for msg in result.emitted_messages {
|
||||||
@ -1515,13 +1528,19 @@ impl SessionManager {
|
|||||||
// in context compression (system prompt is dynamic and should not be persisted).
|
// in context compression (system prompt is dynamic and should not be persisted).
|
||||||
let mut history = session_guard.compressor
|
let mut history = session_guard.compressor
|
||||||
.compress_if_needed(history)
|
.compress_if_needed(history)
|
||||||
.await?
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
tracing::warn!(error = %e, "Context compression failed in handle_cron_message");
|
||||||
|
})?
|
||||||
.history;
|
.history;
|
||||||
|
|
||||||
history.insert(0, ChatMessage::system(full_system_prompt));
|
history.insert(0, ChatMessage::system(full_system_prompt));
|
||||||
|
|
||||||
let agent = session_guard.create_agent_with_notify(notify_tx)?;
|
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 {
|
for msg in result.emitted_messages {
|
||||||
session_guard.add_message(msg, true).await
|
session_guard.add_message(msg, true).await
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user