mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-07 03:00:29 +00:00
Replace ContextWindowWarning with generic Warning event
Introduce a reusable Warning { kind, message, details } variant in
AgentEvent so non-fatal warnings (context window usage, deprecation,
etc.) share a single event shape. The context_window warning preserves
all original fields inside the JSON details object.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dd75880cdd
commit
2a7aea5950
8 changed files with 36 additions and 35 deletions
|
|
@ -253,7 +253,7 @@ All agent events include `node_id` and `node_label`.
|
|||
| `Agent.ToolCallOutputDelta` | `delta` |
|
||||
| `Agent.ToolCallCompleted` | `tool_name`, `tool_call_id`, `output`, `is_error` |
|
||||
| `Agent.Error` | `error` |
|
||||
| `Agent.ContextWindowWarning` | `estimated_tokens`, `context_window_size`, `usage_percent` |
|
||||
| `Agent.Warning` | `kind`, `message`, `details` |
|
||||
| `Agent.LoopDetected` | _(none)_ |
|
||||
| `Agent.TurnLimitReached` | `max_turns` |
|
||||
| `Agent.SkillExpanded` | `skill_name` |
|
||||
|
|
|
|||
|
|
@ -240,6 +240,6 @@ Compaction emits three events to the [event stream](/execution/observability#eve
|
|||
|
||||
| Event | When | Key fields |
|
||||
|---|---|---|
|
||||
| `Agent.ContextWindowWarning` | Token estimate exceeds 80% of context window | `estimated_tokens`, `context_window_size`, `usage_percent` |
|
||||
| `Agent.Warning` (kind: `context_window`) | Token estimate exceeds 80% of context window | `kind`, `message`, `details` (contains `estimated_tokens`, `context_window_size`, `usage_percent`) |
|
||||
| `Agent.CompactionStarted` | Compaction begins | `estimated_tokens`, `context_window_size` |
|
||||
| `Agent.CompactionCompleted` | Summary generated and history replaced | `original_turn_count`, `preserved_turn_count`, `summary_token_estimate`, `tracked_file_count` |
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ Events fall into several categories:
|
|||
| `Agent.Error` | `stage`, `error` | Agent-level error |
|
||||
| `Agent.LoopDetected` | `stage` | Repeated tool call pattern detected |
|
||||
| `Agent.SteeringInjected` | `stage`, `text` | Human steering message injected |
|
||||
| `Agent.ContextWindowWarning` | `stage`, `estimated_tokens`, `context_window_size`, `usage_percent` | Token usage exceeds context window threshold |
|
||||
| `Agent.Warning` | `stage`, `kind`, `message`, `details` | Non-fatal warning (e.g. context window usage) |
|
||||
| `Agent.CompactionStarted` | `stage`, `estimated_tokens`, `context_window_size` | Context compaction triggered |
|
||||
| `Agent.CompactionCompleted` | `stage`, `original_turn_count`, `preserved_turn_count`, `summary_token_estimate`, `tracked_file_count` | Context compaction finished |
|
||||
| `Agent.LlmRetry` | `stage`, `provider`, `model`, `attempt`, `delay_secs` | LLM API call retried |
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ profile.register_subagent_tools(manager, factory, 0);
|
|||
## Safety Features
|
||||
|
||||
- **Loop detection** -- Detects repeating tool call patterns (period 1, 2, or 3) and injects a steering warning
|
||||
- **Context window monitoring** -- Emits `ContextWindowWarning` events when estimated usage exceeds 80%
|
||||
- **Context window monitoring** -- Emits `Warning` events (kind `"context_window"`) when estimated usage exceeds 80%
|
||||
- **Tool argument validation** -- Validates arguments against JSON Schema before execution
|
||||
- **Tool output truncation** -- Per-tool character and line limits with head/tail or tail-only truncation modes
|
||||
- **Environment variable filtering** -- `LocalSandbox` strips secrets (`*_API_KEY`, `*_SECRET`, `*_TOKEN`, `*_PASSWORD`, `*_CREDENTIAL`) from subprocess environments
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use fabro_llm::types::{Message, Request};
|
|||
use tracing::debug;
|
||||
|
||||
/// Check whether the context window usage exceeds the configured threshold.
|
||||
/// Emits a `ContextWindowWarning` event when over the threshold.
|
||||
/// Emits a `Warning` event with kind `"context_window"` when over the threshold.
|
||||
/// Returns `true` if the threshold is exceeded.
|
||||
pub fn check_context_usage(
|
||||
system_prompt: &str,
|
||||
|
|
@ -26,10 +26,17 @@ pub fn check_context_usage(
|
|||
if estimated_tokens > threshold {
|
||||
emitter.emit(
|
||||
session_id.to_owned(),
|
||||
AgentEvent::ContextWindowWarning {
|
||||
estimated_tokens,
|
||||
context_window_size: context_window,
|
||||
usage_percent: estimated_tokens * 100 / context_window,
|
||||
AgentEvent::Warning {
|
||||
kind: "context_window".into(),
|
||||
message: format!(
|
||||
"Context window usage: {}%",
|
||||
estimated_tokens * 100 / context_window
|
||||
),
|
||||
details: serde_json::json!({
|
||||
"estimated_tokens": estimated_tokens,
|
||||
"context_window_size": context_window,
|
||||
"usage_percent": estimated_tokens * 100 / context_window,
|
||||
}),
|
||||
},
|
||||
);
|
||||
true
|
||||
|
|
@ -345,11 +352,8 @@ mod tests {
|
|||
let over = check_context_usage("prompt", &history, &profile, 80, &emitter, "sess");
|
||||
assert!(over);
|
||||
|
||||
// Should have emitted a ContextWindowWarning
|
||||
// Should have emitted a Warning
|
||||
let event = rx.try_recv().unwrap();
|
||||
assert!(matches!(
|
||||
event.event,
|
||||
AgentEvent::ContextWindowWarning { .. }
|
||||
));
|
||||
assert!(matches!(event.event, AgentEvent::Warning { .. }));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1608,13 +1608,9 @@ mod tests {
|
|||
|
||||
let mut found_warning = false;
|
||||
while let Ok(event) = rx.try_recv() {
|
||||
if let AgentEvent::ContextWindowWarning {
|
||||
context_window_size,
|
||||
..
|
||||
} = &event.event
|
||||
{
|
||||
if let AgentEvent::Warning { details, .. } = &event.event {
|
||||
found_warning = true;
|
||||
assert_eq!(*context_window_size, 100);
|
||||
assert_eq!(details["context_window_size"], 100);
|
||||
}
|
||||
}
|
||||
assert!(found_warning);
|
||||
|
|
@ -1660,7 +1656,7 @@ mod tests {
|
|||
|
||||
let mut found_warning = false;
|
||||
while let Ok(event) = rx.try_recv() {
|
||||
if matches!(event.event, AgentEvent::ContextWindowWarning { .. }) {
|
||||
if matches!(event.event, AgentEvent::Warning { .. }) {
|
||||
found_warning = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,10 +130,10 @@ pub enum AgentEvent {
|
|||
Error {
|
||||
error: crate::error::AgentError,
|
||||
},
|
||||
ContextWindowWarning {
|
||||
estimated_tokens: usize,
|
||||
context_window_size: usize,
|
||||
usage_percent: usize,
|
||||
Warning {
|
||||
kind: String,
|
||||
message: String,
|
||||
details: serde_json::Value,
|
||||
},
|
||||
LoopDetected,
|
||||
TurnLimitReached {
|
||||
|
|
@ -264,17 +264,12 @@ impl AgentEvent {
|
|||
Self::Error { error } => {
|
||||
error!(session_id, error = %error, "Agent error");
|
||||
}
|
||||
Self::ContextWindowWarning {
|
||||
estimated_tokens,
|
||||
context_window_size,
|
||||
usage_percent,
|
||||
} => {
|
||||
Self::Warning { kind, message, .. } => {
|
||||
warn!(
|
||||
session_id,
|
||||
estimated_tokens,
|
||||
context_window_size,
|
||||
usage_percent,
|
||||
"Context window usage high"
|
||||
kind = kind.as_str(),
|
||||
message = message.as_str(),
|
||||
"Warning"
|
||||
);
|
||||
}
|
||||
Self::LoopDetected => {
|
||||
|
|
|
|||
|
|
@ -1380,7 +1380,13 @@ impl ProgressUI {
|
|||
}
|
||||
self.on_tool_call_completed(stage_node_id, tool_call_id, *is_error);
|
||||
}
|
||||
AgentEvent::ContextWindowWarning { usage_percent, .. } if self.verbose => {
|
||||
AgentEvent::Warning { kind, details, .. }
|
||||
if kind == "context_window" && self.verbose =>
|
||||
{
|
||||
let usage_percent = details
|
||||
.get("usage_percent")
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(0);
|
||||
let yellow = Style::new().yellow();
|
||||
self.insert_info_line_for_stage(
|
||||
stage_node_id,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue