mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
fix(core/compact): prune orphan tool outputs before compacting
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::AgentTask;
|
||||
@@ -233,6 +234,8 @@ pub(super) async fn perform_compaction(
|
||||
// background maintenance task and should not affect rollout reconstruction.
|
||||
|
||||
loop {
|
||||
prune_orphan_tool_outputs(&mut turn_input);
|
||||
|
||||
let mut prompt = Prompt::default();
|
||||
prompt.input = turn_input.clone();
|
||||
prompt.store = !sess.disable_response_storage;
|
||||
@@ -663,6 +666,51 @@ pub fn sanitize_items_for_compact(items: Vec<ResponseItem>) -> Vec<ResponseItem>
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Remove tool outputs that no longer have a matching tool call in the
|
||||
/// conversation slice. This can happen if earlier items are trimmed for
|
||||
/// context overflow, leaving orphaned outputs that will be rejected by the
|
||||
/// compact endpoint.
|
||||
pub fn prune_orphan_tool_outputs(items: &mut Vec<ResponseItem>) -> usize {
|
||||
let mut seen_calls: HashSet<String> = HashSet::new();
|
||||
|
||||
for item in items.iter() {
|
||||
match item {
|
||||
ResponseItem::FunctionCall { call_id, .. }
|
||||
| ResponseItem::CustomToolCall { call_id, .. } => {
|
||||
seen_calls.insert(call_id.clone());
|
||||
}
|
||||
ResponseItem::LocalShellCall {
|
||||
id,
|
||||
call_id,
|
||||
..
|
||||
} => {
|
||||
if let Some(call_id) = call_id {
|
||||
seen_calls.insert(call_id.clone());
|
||||
}
|
||||
if let Some(id) = id {
|
||||
// Chat Completions flow sets only `id`; outputs use that value as call_id.
|
||||
seen_calls.insert(id.clone());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let before = items.len();
|
||||
items.retain(|item| match item {
|
||||
ResponseItem::FunctionCallOutput { call_id, .. }
|
||||
| ResponseItem::CustomToolCallOutput { call_id, .. } => seen_calls.contains(call_id),
|
||||
_ => true,
|
||||
});
|
||||
|
||||
let removed = before.saturating_sub(items.len());
|
||||
if removed > 0 {
|
||||
tracing::warn!("Dropping {removed} orphaned tool output(s) during compaction");
|
||||
}
|
||||
|
||||
removed
|
||||
}
|
||||
|
||||
fn compaction_checkpoint_warning_event() -> EventMsg {
|
||||
EventMsg::CompactionCheckpointWarning(CompactionCheckpointWarningEvent {
|
||||
message: COMPACTION_CHECKPOINT_MESSAGE.to_string(),
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use super::compact::{
|
||||
is_context_overflow_error,
|
||||
prune_orphan_tool_outputs,
|
||||
response_input_from_core_items,
|
||||
sanitize_items_for_compact,
|
||||
send_compaction_checkpoint_warning,
|
||||
@@ -87,6 +88,8 @@ async fn run_remote_compact_task_inner(
|
||||
let max_retries = turn_context.client.get_provider().stream_max_retries();
|
||||
let mut retries = 0;
|
||||
let new_history = loop {
|
||||
prune_orphan_tool_outputs(&mut turn_items);
|
||||
|
||||
let mut prompt = Prompt::default();
|
||||
prompt.input = turn_items.clone();
|
||||
prompt.base_instructions_override = turn_context.base_instructions.clone();
|
||||
|
||||
Reference in New Issue
Block a user