Skip to content

Commit 2d29e5b

Browse files
remove global container for todo list
1 parent 2f7146a commit 2d29e5b

File tree

4 files changed

+86
-61
lines changed

4 files changed

+86
-61
lines changed

examples/agents/todo_tools_example.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
"""Example demonstrating the new single tool-based todo functionality."""
1+
"""Example demonstrating the new instance-based todo functionality."""
22

33
import asyncio
44

5-
from ragbits.agents import Agent, AgentOptions, ToolCallResult, get_todo_instruction_tpl, todo_manager
5+
from ragbits.agents import Agent, AgentOptions
6+
from ragbits.agents.tools.todo import TodoList, create_todo_manager, get_todo_instruction_tpl
67
from ragbits.core.llms import LiteLLM, ToolCall
78

89

910
async def main():
10-
"""Demonstrate the new single tool-based todo approach with streaming and logging."""
11+
"""Demonstrate the new instance-based todo approach with streaming and logging."""
12+
13+
# Create a dedicated TodoList instance for this agent
14+
my_todo_list = TodoList()
15+
my_todo_manager = create_todo_manager(my_todo_list)
1116

1217
# Create an agent with higher turn limit and todo capabilities
1318
my_agent = Agent(
@@ -28,7 +33,7 @@ async def main():
2833
- Weather considerations and backup plans
2934
- Safety information and emergency contacts
3035
""" + get_todo_instruction_tpl(task_range=(3, 5)),
31-
tools=[todo_manager],
36+
tools=[my_todo_manager], # Use the instance-specific todo manager
3237
default_options=AgentOptions(max_turns=30)
3338
)
3439

packages/ragbits-agents/src/ragbits/agents/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
AgentRunContext,
88
ToolCallResult,
99
)
10-
from ragbits.agents.tools import get_todo_instruction_tpl, todo_manager
10+
from ragbits.agents.tools import get_todo_instruction_tpl, create_todo_manager
1111
from ragbits.agents.types import QuestionAnswerAgent, QuestionAnswerPromptInput, QuestionAnswerPromptOutput
1212

1313
__all__ = [
@@ -22,5 +22,5 @@
2222
"QuestionAnswerPromptOutput",
2323
"ToolCallResult",
2424
"get_todo_instruction_tpl",
25-
"todo_manager",
25+
"create_todo_manager",
2626
]
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Agent tools for extending functionality."""
22

3-
from .todo import get_todo_instruction_tpl, todo_manager
3+
from .todo import get_todo_instruction_tpl, create_todo_manager
44

5-
__all__ = ["todo_manager", "get_todo_instruction_tpl"]
5+
__all__ = ["create_todo_manager", "get_todo_instruction_tpl"]

packages/ragbits-agents/src/ragbits/agents/tools/todo.py

Lines changed: 73 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
import uuid
44
from dataclasses import dataclass, field
5-
from datetime import datetime
65
from enum import Enum
7-
from typing import Any, Literal
6+
from typing import Any, Literal, Callable
87

98

109
class TaskStatus(str, Enum):
@@ -40,52 +39,33 @@ def advance_to_next(self):
4039
"""Move to next task."""
4140
self.current_index += 1
4241

43-
44-
# Storage - just one todo list per agent run
45-
_current_todo: TodoList | None = None
46-
47-
def todo_manager(
48-
action: Literal["create", "get_current", "start_task", "complete_task", "get_final_summary"],
49-
tasks: list[str] | None = None,
50-
summary: str | None = None,
51-
) -> dict[str, Any]:
52-
"""
53-
Simplified todo manager for agent runs.
54-
55-
Actions:
56-
- create: Create todo list with tasks
57-
- get_current: Get current task to work on
58-
- start_task: Mark current task as in progress
59-
- complete_task: Complete current task with summary
60-
- get_final_summary: Get all completed work
61-
"""
62-
global _current_todo
63-
64-
if action == "create":
65-
if not tasks:
42+
def create_tasks(self, task_descriptions: list[str]) -> dict[str, Any]:
43+
"""Create tasks from descriptions."""
44+
if not task_descriptions:
6645
raise ValueError("Tasks required for create action")
6746

68-
_current_todo = TodoList()
69-
for i, desc in enumerate(tasks):
47+
# Clear existing tasks
48+
self.tasks.clear()
49+
self.current_index = 0
50+
51+
for i, desc in enumerate(task_descriptions):
7052
task = Task(
7153
id=str(uuid.uuid4()),
7254
description=desc.strip(),
7355
order=i
7456
)
75-
_current_todo.tasks.append(task)
57+
self.tasks.append(task)
7658

7759
return {
7860
"action": "create",
79-
"tasks": [{"id": t.id, "description": t.description, "order": t.order} for t in _current_todo.tasks],
80-
"total_count": len(_current_todo.tasks),
81-
"message": f"Created {len(tasks)} tasks"
61+
"tasks": [{"id": t.id, "description": t.description, "order": t.order} for t in self.tasks],
62+
"total_count": len(self.tasks),
63+
"message": f"Created {len(task_descriptions)} tasks"
8264
}
8365

84-
if not _current_todo:
85-
raise ValueError("No todo list exists. Create one first.")
86-
87-
if action == "get_current":
88-
current = _current_todo.get_current_task()
66+
def get_current(self) -> dict[str, Any]:
67+
"""Get current task information."""
68+
current = self.get_current_task()
8969
if not current:
9070
return {
9171
"action": "get_current",
@@ -97,12 +77,13 @@ def todo_manager(
9777
return {
9878
"action": "get_current",
9979
"current_task": {"id": current.id, "description": current.description, "status": current.status.value},
100-
"progress": f"{_current_todo.current_index + 1}/{len(_current_todo.tasks)}",
80+
"progress": f"{self.current_index + 1}/{len(self.tasks)}",
10181
"message": f"Current task: {current.description}"
10282
}
10383

104-
elif action == "start_task":
105-
current = _current_todo.get_current_task()
84+
def start_current_task(self) -> dict[str, Any]:
85+
"""Start the current task."""
86+
current = self.get_current_task()
10687
if not current:
10788
raise ValueError("No current task to start")
10889

@@ -113,11 +94,12 @@ def todo_manager(
11394
"message": f"Started task: {current.description}"
11495
}
11596

116-
elif action == "complete_task":
97+
def complete_current_task(self, summary: str) -> dict[str, Any]:
98+
"""Complete the current task with summary."""
11799
if not summary:
118100
raise ValueError("Summary required for complete_task")
119101

120-
current = _current_todo.get_current_task()
102+
current = self.get_current_task()
121103
if not current:
122104
raise ValueError("No current task to complete")
123105

@@ -126,22 +108,23 @@ def todo_manager(
126108

127109
current.status = TaskStatus.COMPLETED
128110
current.summary = summary.strip()
129-
_current_todo.advance_to_next()
111+
self.advance_to_next()
130112

131-
next_task = _current_todo.get_current_task()
132-
completed_count = sum(1 for t in _current_todo.tasks if t.status == TaskStatus.COMPLETED)
113+
next_task = self.get_current_task()
114+
completed_count = sum(1 for t in self.tasks if t.status == TaskStatus.COMPLETED)
133115

134116
return {
135117
"action": "complete_task",
136118
"completed_task": {"id": current.id, "description": current.description, "summary": current.summary},
137119
"next_task": {"id": next_task.id, "description": next_task.description} if next_task else None,
138-
"progress": f"{completed_count}/{len(_current_todo.tasks)}",
120+
"progress": f"{completed_count}/{len(self.tasks)}",
139121
"all_completed": next_task is None,
140122
"message": f"Completed: {current.description}"
141123
}
142124

143-
elif action == "get_final_summary":
144-
completed_tasks = [t for t in _current_todo.tasks if t.status == TaskStatus.COMPLETED]
125+
def get_final_summary(self) -> dict[str, Any]:
126+
"""Get comprehensive final summary of all completed work."""
127+
completed_tasks = [t for t in self.tasks if t.status == TaskStatus.COMPLETED]
145128

146129
if not completed_tasks:
147130
return {
@@ -160,18 +143,55 @@ def todo_manager(
160143

161144
final_summary = "\n\n".join(final_content)
162145

163-
# Clean up after getting final summary
164-
_current_todo = None
165-
166146
return {
167147
"action": "get_final_summary",
168148
"final_summary": final_summary,
169149
"total_completed": len(completed_tasks),
170150
"message": f"Final summary with {len(completed_tasks)} completed tasks."
171151
}
172152

173-
else:
174-
raise ValueError(f"Unknown action: {action}")
153+
154+
def create_todo_manager(todo_list: TodoList) -> Callable[..., dict[str, Any]]:
155+
"""
156+
Create a todo_manager function bound to a specific TodoList instance.
157+
158+
This allows each agent to have its own isolated todo list.
159+
160+
Args:
161+
todo_list: The TodoList instance to bind to
162+
163+
Returns:
164+
A todo_manager function that operates on the provided TodoList
165+
"""
166+
def todo_manager(
167+
action: Literal["create", "get_current", "start_task", "complete_task", "get_final_summary"],
168+
tasks: list[str] | None = None,
169+
summary: str | None = None,
170+
) -> dict[str, Any]:
171+
"""
172+
Todo manager bound to a specific TodoList instance.
173+
174+
Actions:
175+
- create: Create todo list with tasks
176+
- get_current: Get current task to work on
177+
- start_task: Mark current task as in progress
178+
- complete_task: Complete current task with summary
179+
- get_final_summary: Get all completed work
180+
"""
181+
if action == "create":
182+
return todo_list.create_tasks(tasks or [])
183+
elif action == "get_current":
184+
return todo_list.get_current()
185+
elif action == "start_task":
186+
return todo_list.start_current_task()
187+
elif action == "complete_task":
188+
return todo_list.complete_current_task(summary or "")
189+
elif action == "get_final_summary":
190+
return todo_list.get_final_summary()
191+
else:
192+
raise ValueError(f"Unknown action: {action}")
193+
194+
return todo_manager
175195

176196

177197
def get_todo_instruction_tpl(task_range: tuple[int, int] = (3, 5)) -> str:
@@ -196,4 +216,4 @@ def get_todo_instruction_tpl(task_range: tuple[int, int] = (3, 5)) -> str:
196216
197217
IMPORTANT: Task summaries should be DETAILED and COMPREHENSIVE (3-5 sentences).
198218
Include specific information, recommendations, and actionable details.
199-
"""
219+
"""

0 commit comments

Comments
 (0)