g3 console initial cut + error doesnt kill auto
This commit is contained in:
@@ -11,12 +11,6 @@ use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
/// Maximum number of retry attempts for recoverable errors (default mode)
|
||||
const DEFAULT_MAX_RETRY_ATTEMPTS: u32 = 3;
|
||||
|
||||
/// Maximum number of retry attempts for autonomous mode
|
||||
const AUTONOMOUS_MAX_RETRY_ATTEMPTS: u32 = 6;
|
||||
|
||||
/// Base delay for exponential backoff (in milliseconds)
|
||||
const BASE_RETRY_DELAY_MS: u64 = 1000;
|
||||
|
||||
@@ -188,6 +182,8 @@ pub enum RecoverableError {
|
||||
Timeout,
|
||||
/// Token limit exceeded (might be recoverable with summarization)
|
||||
TokenLimit,
|
||||
/// Context length exceeded (prompt too long) - should end current turn in autonomous mode
|
||||
ContextLengthExceeded,
|
||||
}
|
||||
|
||||
/// Classify an error as recoverable or non-recoverable
|
||||
@@ -224,6 +220,13 @@ pub fn classify_error(error: &anyhow::Error) -> ErrorType {
|
||||
return ErrorType::Recoverable(RecoverableError::Timeout);
|
||||
}
|
||||
|
||||
// Check for context length exceeded errors (HTTP 400 with specific messages)
|
||||
if (error_str.contains("400") || error_str.contains("bad request")) &&
|
||||
(error_str.contains("context length") || error_str.contains("prompt is too long") ||
|
||||
error_str.contains("maximum context length") || error_str.contains("context_length_exceeded")) {
|
||||
return ErrorType::Recoverable(RecoverableError::ContextLengthExceeded);
|
||||
}
|
||||
|
||||
if error_str.contains("token") && (error_str.contains("limit") || error_str.contains("exceeded")) {
|
||||
return ErrorType::Recoverable(RecoverableError::TokenLimit);
|
||||
}
|
||||
@@ -284,6 +287,7 @@ pub async fn retry_with_backoff<F, Fut, T>(
|
||||
mut operation: F,
|
||||
context: &ErrorContext,
|
||||
is_autonomous: bool,
|
||||
max_attempts: u32,
|
||||
) -> Result<T>
|
||||
where
|
||||
F: FnMut() -> Fut,
|
||||
@@ -307,8 +311,6 @@ where
|
||||
}
|
||||
Err(error) => {
|
||||
let error_type = classify_error(&error);
|
||||
let max_attempts = if is_autonomous { AUTONOMOUS_MAX_RETRY_ATTEMPTS } else { DEFAULT_MAX_RETRY_ATTEMPTS };
|
||||
|
||||
match error_type {
|
||||
ErrorType::Recoverable(recoverable_type) => {
|
||||
if attempt >= max_attempts {
|
||||
@@ -421,6 +423,13 @@ mod tests {
|
||||
let error = anyhow!("Token limit exceeded");
|
||||
assert_eq!(classify_error(&error), ErrorType::Recoverable(RecoverableError::TokenLimit));
|
||||
|
||||
// Context length exceeded
|
||||
let error = anyhow!("HTTP 400 Bad Request: context length exceeded");
|
||||
assert_eq!(classify_error(&error), ErrorType::Recoverable(RecoverableError::ContextLengthExceeded));
|
||||
|
||||
let error = anyhow!("Error 400: prompt is too long");
|
||||
assert_eq!(classify_error(&error), ErrorType::Recoverable(RecoverableError::ContextLengthExceeded));
|
||||
|
||||
// Non-recoverable
|
||||
let error = anyhow!("Invalid API key");
|
||||
assert_eq!(classify_error(&error), ErrorType::NonRecoverable);
|
||||
|
||||
@@ -37,6 +37,7 @@ mod tests {
|
||||
},
|
||||
&context,
|
||||
false, // not autonomous mode
|
||||
3, // max_attempts
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -71,6 +72,7 @@ mod tests {
|
||||
},
|
||||
&context,
|
||||
false, // not autonomous mode
|
||||
3, // max_attempts
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -104,6 +106,7 @@ mod tests {
|
||||
},
|
||||
&context,
|
||||
false, // not autonomous mode
|
||||
3, // max_attempts
|
||||
)
|
||||
.await;
|
||||
|
||||
|
||||
@@ -1620,6 +1620,72 @@ If you can complete it with 1-2 tool calls, skip TODO.
|
||||
&self.context_window
|
||||
}
|
||||
|
||||
/// Log an error message to the session JSON file as the last message
|
||||
/// This is used in autonomous mode to record context length exceeded errors
|
||||
pub fn log_error_to_session(&self, error: &anyhow::Error, role: &str, forensic_context: Option<String>) {
|
||||
// Skip if quiet mode is enabled
|
||||
if self.quiet {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only log if we have a session ID
|
||||
let session_id = match &self.session_id {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
error!("Cannot log error to session: no session ID");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs();
|
||||
|
||||
let filename = format!("logs/g3_session_{}.json", session_id);
|
||||
|
||||
// Read existing session log
|
||||
let mut session_data: serde_json::Value = if std::path::Path::new(&filename).exists() {
|
||||
match std::fs::read_to_string(&filename) {
|
||||
Ok(content) => serde_json::from_str(&content).unwrap_or_else(|_| serde_json::json!({})),
|
||||
Err(_) => serde_json::json!({}),
|
||||
}
|
||||
} else {
|
||||
serde_json::json!({})
|
||||
};
|
||||
|
||||
// Build error message with forensic context
|
||||
let error_message = if let Some(context) = forensic_context {
|
||||
format!(
|
||||
"ERROR: {}\n\nForensic Context:\n{}",
|
||||
error,
|
||||
context
|
||||
)
|
||||
} else {
|
||||
format!("ERROR: {}", error)
|
||||
};
|
||||
|
||||
// Create error message entry
|
||||
let error_entry = serde_json::json!({
|
||||
"role": role,
|
||||
"content": error_message,
|
||||
"timestamp": timestamp,
|
||||
"error_type": "context_length_exceeded"
|
||||
});
|
||||
|
||||
// Append to conversation history
|
||||
if let Some(history) = session_data.get_mut("context_window").and_then(|cw| cw.get_mut("conversation_history")) {
|
||||
if let Some(history_array) = history.as_array_mut() {
|
||||
history_array.push(error_entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Write back to file
|
||||
if let Ok(json_content) = serde_json::to_string_pretty(&session_data) {
|
||||
let _ = std::fs::write(&filename, json_content);
|
||||
}
|
||||
}
|
||||
|
||||
/// Manually trigger context summarization regardless of context window size
|
||||
/// Returns Ok(true) if summarization was successful, Ok(false) if it failed
|
||||
pub async fn force_summarize(&mut self) -> Result<bool> {
|
||||
@@ -2485,7 +2551,11 @@ If you can complete it with 1-2 tool calls, skip TODO.
|
||||
use crate::error_handling::{calculate_retry_delay, classify_error, ErrorType};
|
||||
|
||||
let mut attempt = 0;
|
||||
let max_attempts = if self.is_autonomous { 6 } else { 3 };
|
||||
let max_attempts = if self.is_autonomous {
|
||||
self.config.agent.autonomous_max_retry_attempts
|
||||
} else {
|
||||
self.config.agent.max_retry_attempts
|
||||
};
|
||||
|
||||
loop {
|
||||
attempt += 1;
|
||||
|
||||
Reference in New Issue
Block a user