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
27 changes: 27 additions & 0 deletions crates/tinyagents-graph/src/todos/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,33 @@ mod tool_tests {
.expect("successful todo result has a JSON payload")
}

/// The description is what makes a model treat the list as bookkeeping
/// rather than as the work itself: without the "same response carries the
/// next call" rule, models write a list, stop, and wait to be prompted;
/// without the "only after its work has run" rule they tick items off
/// ahead of doing them.
#[test]
fn description_states_when_an_item_may_be_completed_and_that_writing_is_not_working() {
let tool = TodoTool::new(store());
let description = Tool::description(&tool);
assert!(
description.contains("only after its work has actually run"),
"an item is completed after its result exists: {description}"
);
assert!(
description.contains("bookkeeping, not work"),
"writing the list is not the work: {description}"
);
assert!(
description.contains("immediately carry out the next step"),
"the model advances after bookkeeping: {description}"
);
assert!(
description.contains("next model turn"),
"sequential providers may advance on their next turn: {description}"
);
}

#[test]
fn todo_tools_builds_a_single_tool() {
let tools = todo_tools(store());
Expand Down
9 changes: 6 additions & 3 deletions crates/tinyagents-graph/src/todos/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ const TODO_TOOL_NAME: &str = "todo";

const TODO_DESCRIPTION: &str = "Your todo list for this thread. Pass the complete list every \
time; it replaces what was there. Use it for work with 3+ steps: write the steps up front, \
keep exactly one `in_progress`, mark each `completed` the moment it is done. Omit `todos` \
to read the current list. The list is bound automatically to the current thread — do not \
pass a thread id.";
keep exactly one `in_progress`, mark each `completed` only after its work has actually run \
and its result is in this conversation. Writing the list is bookkeeping, not work: after \
updating it, immediately carry out the next step. Providers that cannot issue parallel tool \
Comment on lines +29 to +30

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exempt the terminal todo update from the next-step mandate

When the model completes the final item and updates the list, this unconditional instruction requires it to immediately carry out a next step even though none exists. Because the description is the model-facing contract, this can discourage clean termination or cause the model to invent additional work after the checklist is complete; qualify the instruction so it applies only while a pending step remains.

Useful? React with 👍 / 👎.

calls may make that call in the next model turn, and one update per response is enough. Omit \
`todos` to read the current list. The list is bound \
automatically to the current thread — do not pass a thread id.";

/// The `todo` harness [`Tool`], backed by a [`Store`](tinyagents_harness::store::Store).
pub struct TodoTool {
Expand Down
Loading