Spaces:
Running
Running
| import json, os | |
| from smolagents import Tool | |
| PLAN_PATH = "./execution_plan.json" | |
| def load_plan(): | |
| if not os.path.exists(PLAN_PATH): | |
| return [] | |
| with open(PLAN_PATH, "r") as f: | |
| return json.load(f) | |
| def save_plan(plan): | |
| with open(PLAN_PATH, "w") as f: | |
| json.dump(plan, f, indent=2) | |
| class UpdatePlanTool(Tool): | |
| """ | |
| Tool to update the execution plan stored on disk. | |
| Maintains a persistent execution plan that can be updated during multi-step operations. | |
| The plan is stored in JSON format at './execution_plan.json'. | |
| Usage: | |
| - Set merge=False to overwrite the entire plan | |
| - Set merge=True to update specific items by ID while preserving others | |
| Example input: | |
| { | |
| "merge": true, | |
| "todos": [ | |
| {"id": "1", "content": "Research market trends", "status": "completed"}, | |
| {"id": "2", "content": "Analyze competitor data", "status": "in_progress"} | |
| ] | |
| } | |
| Returns: | |
| - {"result": "plan overwritten", "plan": updated_plan} when merge=False | |
| - {"result": "plan updated", "plan": updated_plan} when merge=True | |
| - {"error": "...", "plan": current_plan} when invariant violated | |
| output_type: object | |
| """ | |
| name = "update_plan" | |
| description = ( | |
| "Update the current execution plan on disk. " | |
| "Use for multi-step tasks only. Maintains 2–5 non-operational items. " | |
| "Requires exactly one item in_progress during execution." | |
| ) | |
| inputs = { | |
| "merge": {"type": "boolean", "description": "Merge with existing plan"}, | |
| "todos": {"type": "array", "description": "List of objects {id, content, status}"} | |
| } | |
| output_type = "string" | |
| def forward(self, merge: bool, todos: list) -> str: | |
| plan = load_plan() | |
| if not merge: | |
| save_plan(todos) | |
| return json.dumps({"result": "plan overwritten", "plan": todos}) | |
| # merge: update items by id | |
| plan_by_id = {item["id"]: item for item in plan} | |
| for new_item in todos: | |
| plan_by_id[new_item["id"]] = new_item | |
| merged_plan = list(plan_by_id.values()) | |
| # enforce invariant: at most one in_progress | |
| in_prog = [t for t in merged_plan if t["status"] == "in_progress"] | |
| if len(in_prog) > 1: | |
| return { | |
| "error": "Only one item may be in_progress at a time. Fix your update.", | |
| "plan": merged_plan | |
| } | |
| save_plan(merged_plan) | |
| return json.dumps({"result": "plan updated", "plan": merged_plan}) | |