Write a partial report instead of erroring out when research hits max turns #8
2 changed files with 102 additions and 5 deletions
|
|
@ -17,3 +17,6 @@ serde = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
serde_json = "1"
|
||||||
|
|
|
||||||
|
|
@ -121,11 +121,10 @@ fn partial_findings_from_history(chat_history: &[Message]) -> String {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if sections.is_empty() {
|
match sections.is_empty() {
|
||||||
"The researcher exhausted its turn budget before gathering any usable evidence."
|
true => "The researcher exhausted its turn budget before gathering any usable evidence."
|
||||||
.to_string()
|
.to_string(),
|
||||||
} else {
|
false => sections.join("\n"),
|
||||||
sections.join("\n\n")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,3 +312,98 @@ async fn write_report(
|
||||||
None => report,
|
None => report,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn truncate_leaves_short_text_untouched() {
|
||||||
|
assert_eq!(truncate("hello", 10), "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn truncate_leaves_exact_length_text_untouched() {
|
||||||
|
assert_eq!(truncate("hello", 5), "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn truncate_cuts_long_text_and_marks_it() {
|
||||||
|
assert_eq!(truncate("hello world", 5), "hello [...truncated]");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assistant_text_extracts_text_blocks() {
|
||||||
|
let message = Message::assistant("found it");
|
||||||
|
assert_eq!(assistant_text(&message), vec!["found it".to_string()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assistant_text_ignores_tool_calls() {
|
||||||
|
let message = Message::Assistant {
|
||||||
|
id: None,
|
||||||
|
content: rig::OneOrMany::one(AssistantContent::tool_call(
|
||||||
|
"call-1",
|
||||||
|
"search_web",
|
||||||
|
serde_json::json!({ "query": "test" }),
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
assert!(assistant_text(&message).is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assistant_text_ignores_non_assistant_messages() {
|
||||||
|
assert!(assistant_text(&Message::user("hi")).is_empty());
|
||||||
|
assert!(assistant_text(&Message::system("be careful")).is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tool_result_text_extracts_and_truncates() {
|
||||||
|
let short = Message::tool_result("call-1", "short result");
|
||||||
|
assert_eq!(tool_result_text(&short, 100), vec!["short result".to_string()]);
|
||||||
|
|
||||||
|
let long = Message::tool_result("call-2", "0123456789");
|
||||||
|
assert_eq!(tool_result_text(&long, 5), vec!["01234 [...truncated]".to_string()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tool_result_text_ignores_non_tool_result_content() {
|
||||||
|
assert!(tool_result_text(&Message::user("plain text, no tool result"), 100).is_empty());
|
||||||
|
assert!(tool_result_text(&Message::assistant("also ignored"), 100).is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partial_findings_from_history_falls_back_when_nothing_usable() {
|
||||||
|
let history = vec![
|
||||||
|
Message::system("preamble"),
|
||||||
|
Message::Assistant {
|
||||||
|
id: None,
|
||||||
|
content: rig::OneOrMany::one(AssistantContent::tool_call(
|
||||||
|
"call-1",
|
||||||
|
"search_web",
|
||||||
|
serde_json::json!({ "query": "test" }),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
partial_findings_from_history(&history),
|
||||||
|
"The researcher exhausted its turn budget before gathering any usable evidence."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partial_findings_from_history_collects_assistant_text_and_tool_results_in_order() {
|
||||||
|
let history = vec![
|
||||||
|
Message::assistant("Checking sources..."),
|
||||||
|
Message::tool_result("call-1", "Result A [1]"),
|
||||||
|
Message::assistant("Cross-checking..."),
|
||||||
|
Message::tool_result("call-2", "Result B [2]"),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
partial_findings_from_history(&history),
|
||||||
|
"Checking sources...\nResult A [1]\nCross-checking...\nResult B [2]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue