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 }
|
||||
tracing = { 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();
|
||||
|
||||
if sections.is_empty() {
|
||||
"The researcher exhausted its turn budget before gathering any usable evidence."
|
||||
.to_string()
|
||||
} else {
|
||||
sections.join("\n\n")
|
||||
match sections.is_empty() {
|
||||
true => "The researcher exhausted its turn budget before gathering any usable evidence."
|
||||
.to_string(),
|
||||
false => sections.join("\n"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -313,3 +312,98 @@ async fn write_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