mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
Avoid data loss in SubAgentEvent flattening
Instead of flattening the inner event's fields into the top level (which causes collisions when SubAgentEvent wraps SubAgentEvent — losing inner agent_id, depth, and nested event body), keep the inner event as a `nested_event` JSON value. The dot-notation event name still reflects the inner type for easy filtering. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eb195a0be9
commit
b4f1495982
1 changed files with 50 additions and 26 deletions
|
|
@ -554,41 +554,28 @@ fn flatten_sub_agent_event(
|
|||
}
|
||||
return ("Agent.SubAgentEvent".to_string(), fields);
|
||||
};
|
||||
let agent_id = sub_fields.remove("agent_id");
|
||||
let depth = sub_fields.remove("depth");
|
||||
let nested_event = sub_fields.remove("event").unwrap_or(serde_json::Value::Null);
|
||||
|
||||
let (nested_name, nested_fields) = match &nested_event {
|
||||
serde_json::Value::Object(event_map) => {
|
||||
let (name, value) = event_map
|
||||
.iter()
|
||||
.next()
|
||||
.expect("nested event must have one key");
|
||||
let fields = match value {
|
||||
serde_json::Value::Object(m) => m.clone(),
|
||||
_ => serde_json::Map::new(),
|
||||
};
|
||||
(Some(name.clone()), fields)
|
||||
}
|
||||
serde_json::Value::String(name) => (Some(name.clone()), serde_json::Map::new()),
|
||||
_ => (None, serde_json::Map::new()),
|
||||
// Extract the inner event name for dot notation, but keep full inner
|
||||
// event as `nested_event` JSON to avoid field collisions when sub-agents
|
||||
// are themselves nested (SubAgentEvent wrapping SubAgentEvent).
|
||||
let nested_event = sub_fields.remove("event").unwrap_or(serde_json::Value::Null);
|
||||
let inner_name = match &nested_event {
|
||||
serde_json::Value::Object(map) => map.keys().next().cloned(),
|
||||
serde_json::Value::String(name) => Some(name.clone()),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let event_name = match &nested_name {
|
||||
let event_name = match &inner_name {
|
||||
Some(name) => format!("Agent.SubAgentEvent.{name}"),
|
||||
None => "Agent.SubAgentEvent".to_string(),
|
||||
};
|
||||
|
||||
let mut fields = nested_fields;
|
||||
// Start with the SubAgentEvent's own fields (agent_id, depth)
|
||||
let mut fields = sub_fields;
|
||||
if let Some(s) = stage {
|
||||
fields.insert("stage".to_string(), s);
|
||||
}
|
||||
if let Some(id) = agent_id {
|
||||
fields.insert("agent_id".to_string(), id);
|
||||
}
|
||||
if let Some(d) = depth {
|
||||
fields.insert("depth".to_string(), d);
|
||||
}
|
||||
fields.insert("nested_event".to_string(), nested_event);
|
||||
|
||||
(event_name, fields)
|
||||
}
|
||||
|
|
@ -1253,7 +1240,44 @@ mod tests {
|
|||
assert_eq!(fields["stage"], "code");
|
||||
assert_eq!(fields["agent_id"], "sub_1");
|
||||
assert_eq!(fields["depth"], 1);
|
||||
assert_eq!(fields["tool_name"], "write_file");
|
||||
// Inner event preserved as nested_event JSON (not flattened)
|
||||
let nested = fields["nested_event"].as_object().unwrap();
|
||||
let tool_call = nested["ToolCallStarted"].as_object().unwrap();
|
||||
assert_eq!(tool_call["tool_name"], "write_file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_event_doubly_nested_sub_agent_preserves_all_data() {
|
||||
let event = WorkflowRunEvent::Agent {
|
||||
stage: "code".to_string(),
|
||||
event: AgentEvent::SubAgentEvent {
|
||||
agent_id: "sub_1".to_string(),
|
||||
depth: 1,
|
||||
event: Box::new(AgentEvent::SubAgentEvent {
|
||||
agent_id: "sub_2".to_string(),
|
||||
depth: 2,
|
||||
event: Box::new(AgentEvent::ToolCallStarted {
|
||||
tool_name: "read_file".to_string(),
|
||||
tool_call_id: "call_3".to_string(),
|
||||
arguments: serde_json::json!({}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
};
|
||||
let (name, fields) = flatten_event(&event);
|
||||
assert_eq!(name, "Agent.SubAgentEvent.SubAgentEvent");
|
||||
// Outer SubAgentEvent fields at top level
|
||||
assert_eq!(fields["agent_id"], "sub_1");
|
||||
assert_eq!(fields["depth"], 1);
|
||||
assert_eq!(fields["stage"], "code");
|
||||
// Inner SubAgentEvent preserved in nested_event with all data intact
|
||||
let nested = fields["nested_event"].as_object().unwrap();
|
||||
let inner_sub = nested["SubAgentEvent"].as_object().unwrap();
|
||||
assert_eq!(inner_sub["agent_id"], "sub_2");
|
||||
assert_eq!(inner_sub["depth"], 2);
|
||||
let inner_event = inner_sub["event"].as_object().unwrap();
|
||||
let tool_call = inner_event["ToolCallStarted"].as_object().unwrap();
|
||||
assert_eq!(tool_call["tool_name"], "read_file");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue