|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +from typing import Any |
| 4 | + |
| 5 | +from argus.data.content import Content |
| 6 | +from argus.tools.base import Tool |
| 7 | + |
| 8 | + |
| 9 | +class ChecklistTool(Tool): |
| 10 | + """Stateful checklist tool for tracking task progress across a run.""" |
| 11 | + |
| 12 | + name = "checklist" |
| 13 | + description = ( |
| 14 | + "Manage a task checklist to plan and track your progress. " |
| 15 | + "Use action='init' at the start to list all planned steps, " |
| 16 | + "'complete' to mark a step done by its index, " |
| 17 | + "'add' to append a new step discovered mid-task, " |
| 18 | + "and 'view' to see current state." |
| 19 | + ) |
| 20 | + parameters: dict[str, Any] = { |
| 21 | + "type": "object", |
| 22 | + "properties": { |
| 23 | + "action": { |
| 24 | + "type": "string", |
| 25 | + "enum": ["init", "complete", "add", "view"], |
| 26 | + "description": ( |
| 27 | + "'init': create a new checklist (replaces any existing one); " |
| 28 | + "'complete': mark the item at the given index as done; " |
| 29 | + "'add': append a new item; " |
| 30 | + "'view': display current checklist state." |
| 31 | + ), |
| 32 | + }, |
| 33 | + "items": { |
| 34 | + "type": "array", |
| 35 | + "items": {"type": "string"}, |
| 36 | + "description": "Ordered list of step descriptions. Required for action='init'.", |
| 37 | + }, |
| 38 | + "index": { |
| 39 | + "type": "integer", |
| 40 | + "description": "0-based index of the item to complete. Required for action='complete'.", |
| 41 | + }, |
| 42 | + "item": { |
| 43 | + "type": "string", |
| 44 | + "description": "Description of the new step to append. Required for action='add'.", |
| 45 | + }, |
| 46 | + }, |
| 47 | + "required": ["action"], |
| 48 | + } |
| 49 | + |
| 50 | + def __init__(self) -> None: |
| 51 | + self._items: list[dict[str, Any]] = [] # {"text": str, "done": bool} |
| 52 | + |
| 53 | + def reset(self) -> None: |
| 54 | + """Clear checklist state at the start of a new run.""" |
| 55 | + self._items = [] |
| 56 | + |
| 57 | + def render(self) -> str: |
| 58 | + if not self._items: |
| 59 | + return "Checklist is empty." |
| 60 | + lines = ["=== Task Checklist ==="] |
| 61 | + for i, it in enumerate(self._items): |
| 62 | + mark = "x" if it["done"] else " " |
| 63 | + lines.append(f" [{mark}] {i}. {it['text']}") |
| 64 | + done = sum(1 for it in self._items if it["done"]) |
| 65 | + lines.append(f"\nProgress: {done}/{len(self._items)} completed") |
| 66 | + return "\n".join(lines) |
| 67 | + |
| 68 | + def execute( |
| 69 | + self, |
| 70 | + action: str, |
| 71 | + items: list[str] | None = None, |
| 72 | + index: int | None = None, |
| 73 | + item: str | None = None, |
| 74 | + ) -> Content: |
| 75 | + if action == "init": |
| 76 | + if not items: |
| 77 | + return Content(text="[error: 'items' is required for action='init']") |
| 78 | + self._items = [{"text": t, "done": False} for t in items] |
| 79 | + return Content( |
| 80 | + text=f"Checklist initialized with {len(self._items)} items.\n\n{self.render()}" |
| 81 | + ) |
| 82 | + |
| 83 | + if action == "complete": |
| 84 | + if index is None: |
| 85 | + return Content(text="[error: 'index' is required for action='complete']") |
| 86 | + if index < 0 or index >= len(self._items): |
| 87 | + return Content( |
| 88 | + text=f"[error: index {index} out of range (valid: 0–{len(self._items) - 1})]" |
| 89 | + ) |
| 90 | + self._items[index]["done"] = True |
| 91 | + return Content(text=f"Item {index} marked as done.\n\n{self.render()}") |
| 92 | + |
| 93 | + if action == "add": |
| 94 | + if not item: |
| 95 | + return Content(text="[error: 'item' is required for action='add']") |
| 96 | + self._items.append({"text": item, "done": False}) |
| 97 | + return Content(text=f"Item added at index {len(self._items) - 1}.\n\n{self.render()}") |
| 98 | + |
| 99 | + if action == "view": |
| 100 | + return Content(text=self.render()) |
| 101 | + |
| 102 | + return Content(text=f"[error: unknown action '{action}']") |
0 commit comments