Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions crates/tinyinference-llm/src/prompt_tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,10 @@ pub fn ensure_resolvable_user_turn(messages: &[Message]) -> Vec<Message> {
/// Keep the active request at the end of a prompt-guided tool continuation.
/// Some chat templates treat the synthetic tool-result user turn as transport
/// data and resolve an earlier user message as the query. Repeating the latest
/// real text request after a terminal result gives those templates the correct
/// query without changing the durable transcript.
/// real text request alongside a terminal result gives those templates the
/// correct query without changing the durable transcript. Both belong in one
/// user turn: a separate trailing request makes a model restart discovery and
/// ignore the result it just received.
#[must_use]
pub fn anchor_user_request_after_tool_result(messages: &[Message]) -> Vec<Message> {
if !messages.last().is_some_and(|message| {
Expand All @@ -258,9 +260,19 @@ pub fn anchor_user_request_after_tool_result(messages: &[Message]) -> Vec<Messag
return messages.to_vec();
}
let mut out = messages.to_vec();
out.push(Message::user(format!(
"Continue the latest user request using the tool result above. Latest user request:\n{request}"
)));
// `coalesce_tool_results` supplies a user-role result here. Keep every
// original block intact: direct callers can also pass JSON or media blocks
// alongside the result text.
if let Some(Message::User(last)) = out.last_mut() {
Comment thread
senamakel marked this conversation as resolved.
last.content.insert(
0,
ContentBlock::Text(format!(
"Continue the active user request using the completed tool result below. \
Do not repeat the completed tool call unless new information requires it.\n\
Active user request:\n{request}\n\n"
)),
);
}
out
}

Expand Down
28 changes: 26 additions & 2 deletions crates/tinyinference-llm/src/prompt_tools/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,11 @@ fn tool_continuation_anchors_the_latest_request_after_search_results() {
Message::tool("search-1", "GMAIL_FETCH_EMAILS schema"),
]);
let anchored = anchor_user_request_after_tool_result(&messages);
assert_eq!(anchored.len(), messages.len() + 1);
assert_eq!(anchored.len(), messages.len());
assert!(
anchored[anchored.len() - 2]
anchored
.last()
.unwrap()
.text()
.contains("GMAIL_FETCH_EMAILS")
);
Expand All @@ -191,6 +193,7 @@ fn tool_continuation_anchors_the_latest_request_after_search_results() {
.contains("fetch my latest email")
);
assert!(!anchored.last().unwrap().text().contains("Hey! What's up?"));
assert!(anchored.last().unwrap().text().contains("Do not repeat"));
assert_eq!(anchor_user_request_after_tool_result(&anchored), anchored);
}

Expand All @@ -204,6 +207,27 @@ fn tool_continuation_without_a_real_user_request_stays_unchanged() {
assert_eq!(anchor_user_request_after_tool_result(&messages), messages);
}

#[test]
fn tool_continuation_preserves_non_text_result_blocks() {
let mut result = Message::user("[Tool results]\nlookup completed");
let Message::User(user) = &mut result else {
unreachable!()
};
let json_block = ContentBlock::Json(serde_json::json!({"count": 1}));
user.content.push(json_block.clone());
let anchored =
anchor_user_request_after_tool_result(&[Message::user("find the count"), result]);
let Message::User(last) = anchored.last().unwrap() else {
panic!("tool result remains a user turn");
};
assert_eq!(last.content.last(), Some(&json_block));
assert!(
last.content[0]
.as_text()
.is_some_and(|text| text.contains("find the count"))
);
}

#[test]
fn user_turn_normalization_ignores_blank_and_accepts_non_text_turns() {
let out = ensure_resolvable_user_turn(&[Message::system("system"), Message::user(" ")]);
Expand Down
Loading