Adding logging when we crash in bedrock, adding open AI request translator changes and AI page settings cleanup
This commit is contained in:
@@ -268,6 +268,15 @@ impl BedrockClient {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
super::crash_log::log_crash(
|
||||
"BedrockApiError",
|
||||
&msg,
|
||||
&effective_model_id,
|
||||
messages.len(),
|
||||
None,
|
||||
);
|
||||
|
||||
if msg.contains("AccessDenied") || msg.contains("access denied") {
|
||||
BedrockError::AccessDenied(msg)
|
||||
} else if msg.contains("ThrottlingException") || msg.contains("throttl") {
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use chrono::Local;
|
||||
|
||||
const CRASH_LOG_DIR: &str = "crash-logs";
|
||||
const MAX_CRASH_LOGS: usize = 20;
|
||||
|
||||
fn crash_log_dir() -> Option<PathBuf> {
|
||||
galaxy_core::paths::galaxy_home_config_dir().map(|dir| dir.join(CRASH_LOG_DIR))
|
||||
}
|
||||
|
||||
pub fn log_crash(
|
||||
error_type: &str,
|
||||
error_message: &str,
|
||||
model_id: &str,
|
||||
message_count: usize,
|
||||
context_tokens: Option<u32>,
|
||||
) {
|
||||
let Some(dir) = crash_log_dir() else {
|
||||
log::warn!("[crash-log] Could not determine crash log directory");
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(e) = fs::create_dir_all(&dir) {
|
||||
log::warn!("[crash-log] Failed to create crash log directory: {e}");
|
||||
return;
|
||||
}
|
||||
|
||||
rotate_logs(&dir);
|
||||
|
||||
let timestamp = Local::now();
|
||||
let filename = format!("crash_{}.log", timestamp.format("%Y%m%d_%H%M%S"));
|
||||
let path = dir.join(&filename);
|
||||
|
||||
let content = format!(
|
||||
"=== Galaxy Crash Log ===\n\
|
||||
Timestamp: {}\n\
|
||||
Error Type: {}\n\
|
||||
Model: {}\n\
|
||||
Message Count: {}\n\
|
||||
Context Tokens: {}\n\
|
||||
\n\
|
||||
Error Details:\n\
|
||||
{}\n\
|
||||
========================\n",
|
||||
timestamp.format("%Y-%m-%d %H:%M:%S %Z"),
|
||||
error_type,
|
||||
model_id,
|
||||
message_count,
|
||||
context_tokens
|
||||
.map(|t| t.to_string())
|
||||
.unwrap_or_else(|| "unknown".to_string()),
|
||||
error_message,
|
||||
);
|
||||
|
||||
match OpenOptions::new().create(true).write(true).open(&path) {
|
||||
Ok(mut file) => {
|
||||
if let Err(e) = file.write_all(content.as_bytes()) {
|
||||
log::warn!("[crash-log] Failed to write crash log: {e}");
|
||||
} else {
|
||||
log::info!("[crash-log] Wrote crash log to {}", path.display());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("[crash-log] Failed to open crash log file: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate_logs(dir: &std::path::Path) {
|
||||
let Ok(entries) = fs::read_dir(dir) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut logs: Vec<PathBuf> = entries
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|e| e.path())
|
||||
.filter(|p| {
|
||||
p.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.is_some_and(|n| n.starts_with("crash_") && n.ends_with(".log"))
|
||||
})
|
||||
.collect();
|
||||
|
||||
logs.sort();
|
||||
|
||||
while logs.len() >= MAX_CRASH_LOGS {
|
||||
if let Some(oldest) = logs.first() {
|
||||
let _ = fs::remove_file(oldest);
|
||||
logs.remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod client;
|
||||
pub mod convert;
|
||||
pub mod crash_log;
|
||||
pub mod diagnostic;
|
||||
pub mod external_config;
|
||||
pub mod models;
|
||||
|
||||
@@ -1562,6 +1562,41 @@ fn extract_tool_result_content(result: &api::request::input::ToolCallResult) ->
|
||||
None => ("Agent completed.".to_string(), false),
|
||||
}
|
||||
}
|
||||
api::request::input::tool_call_result::Result::AskUserQuestion(ask_result) => {
|
||||
match &ask_result.result {
|
||||
Some(api::ask_user_question_result::Result::Success(success)) => {
|
||||
let answers_text: Vec<String> = success
|
||||
.answers
|
||||
.iter()
|
||||
.map(|item| {
|
||||
let answer_str = match &item.answer {
|
||||
Some(api::ask_user_question_result::answer_item::Answer::MultipleChoice(mc)) => {
|
||||
let mut parts = mc.selected_options.clone();
|
||||
if !mc.other_text.is_empty() {
|
||||
parts.push(mc.other_text.clone());
|
||||
}
|
||||
parts.join(", ")
|
||||
}
|
||||
Some(api::ask_user_question_result::answer_item::Answer::Skipped(_)) => {
|
||||
"Skipped".to_string()
|
||||
}
|
||||
None => "No answer provided".to_string(),
|
||||
};
|
||||
if item.question_id.is_empty() {
|
||||
answer_str
|
||||
} else {
|
||||
format!("{}: {}", item.question_id, answer_str)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(format!("User's answers:\n{}", answers_text.join("\n")), false)
|
||||
}
|
||||
Some(api::ask_user_question_result::Result::Error(error)) => {
|
||||
(format!("User question error: {}", error.message), true)
|
||||
}
|
||||
None => ("User did not answer.".to_string(), true),
|
||||
}
|
||||
}
|
||||
_ => ("Tool completed successfully.".to_string(), false),
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -397,6 +397,13 @@ pub fn bedrock_stream_to_response_events(
|
||||
);
|
||||
}
|
||||
}
|
||||
super::crash_log::log_crash(
|
||||
"BedrockStreamError",
|
||||
&format!("{e}"),
|
||||
&model_id,
|
||||
event_count as usize,
|
||||
Some(input_tokens as u32),
|
||||
);
|
||||
if !buffered_text.is_empty() {
|
||||
let msg_id = current_text_message_id
|
||||
.clone()
|
||||
|
||||
Reference in New Issue
Block a user