From db5ac38a5a2ad72bc0343da254f220ff9af1f71d Mon Sep 17 00:00:00 2001 From: AI Christianson Date: Tue, 17 Dec 2024 10:18:22 -0500 Subject: [PATCH] allow tasks to be deleted --- ra_aid/__main__.py | 4 +-- ra_aid/tools/memory.py | 41 +++++++++++++++++++--- tests/ra_aid/tools/test_memory.py | 56 +++++++++++++++++++++++++++++-- 3 files changed, 92 insertions(+), 9 deletions(-) diff --git a/ra_aid/__main__.py b/ra_aid/__main__.py index e243384..8ae64b9 100644 --- a/ra_aid/__main__.py +++ b/ra_aid/__main__.py @@ -225,8 +225,8 @@ def run_implementation_stage(base_task, tasks, plan, related_files, model, exper print_stage_header("Implementation Stage") - # Get tasks directly from memory - task_list = _global_memory['tasks'] + # Get tasks directly from memory, maintaining order by ID + task_list = [task for _, task in sorted(_global_memory['tasks'].items())] print_task_header(f"Found {len(task_list)} tasks to implement") diff --git a/ra_aid/tools/memory.py b/ra_aid/tools/memory.py index 8ef0d34..a3c0d58 100644 --- a/ra_aid/tools/memory.py +++ b/ra_aid/tools/memory.py @@ -18,7 +18,8 @@ console = Console() _global_memory: Dict[str, Union[List[Any], Dict[int, str], Dict[int, SnippetInfo], int, Set[str]]] = { 'research_notes': [], 'plans': [], - 'tasks': [], + 'tasks': {}, # Dict[int, str] - ID to task mapping + 'task_id_counter': 0, # Counter for generating unique task IDs 'research_subtasks': [], 'key_facts': {}, # Dict[int, str] - ID to fact mapping 'key_fact_id_counter': 0, # Counter for generating unique fact IDs @@ -65,11 +66,17 @@ def emit_task(task: str) -> str: task: The task to store Returns: - The stored task + String confirming task storage with ID number """ - _global_memory['tasks'].append(task) - console.print(Panel(Markdown(task), title="✅ Task")) - return task + # Get and increment task ID + task_id = _global_memory['task_id_counter'] + _global_memory['task_id_counter'] += 1 + + # Store task with ID + _global_memory['tasks'][task_id] = task + + console.print(Panel(Markdown(task), title=f"✅ Task #{task_id}")) + return f"Task #{task_id} stored." @tool("emit_research_subtask") def emit_research_subtask(subtask: str) -> str: @@ -139,6 +146,30 @@ def delete_key_facts(fact_ids: List[int]) -> str: return "Facts deleted." +@tool("delete_tasks") +def delete_tasks(task_ids: List[int]) -> str: + """Delete multiple tasks from global memory by their IDs. + Silently skips any IDs that don't exist. + + Args: + task_ids: List of task IDs to delete + + Returns: + Confirmation message + """ + results = [] + for task_id in task_ids: + if task_id in _global_memory['tasks']: + # Delete the task + deleted_task = _global_memory['tasks'].pop(task_id) + success_msg = f"Successfully deleted task #{task_id}: {deleted_task}" + console.print(Panel(Markdown(success_msg), + title="🗑️ Task Deleted", + border_style="green")) + results.append(success_msg) + + return "Tasks deleted." + @tool("request_implementation") def request_implementation(reason: str) -> str: """Request that implementation proceed after research/planning. diff --git a/tests/ra_aid/tools/test_memory.py b/tests/ra_aid/tools/test_memory.py index db5a6b6..3e25ca1 100644 --- a/tests/ra_aid/tools/test_memory.py +++ b/tests/ra_aid/tools/test_memory.py @@ -8,7 +8,9 @@ from ra_aid.tools.memory import ( emit_key_snippets, delete_key_snippets, emit_related_files, - get_related_files + get_related_files, + emit_task, + delete_tasks ) @pytest.fixture @@ -23,6 +25,8 @@ def reset_memory(): _global_memory['tasks'] = [] _global_memory['research_subtasks'] = [] _global_memory['related_files'] = set() + _global_memory['tasks'] = {} + _global_memory['task_id_counter'] = 0 yield # Clean up after test _global_memory['key_facts'] = {} @@ -31,7 +35,8 @@ def reset_memory(): _global_memory['key_snippet_id_counter'] = 0 _global_memory['research_notes'] = [] _global_memory['plans'] = [] - _global_memory['tasks'] = [] + _global_memory['tasks'] = {} + _global_memory['task_id_counter'] = 0 _global_memory['research_subtasks'] = [] def test_emit_key_facts_single_fact(reset_memory): @@ -311,6 +316,53 @@ def test_key_snippets_integration(reset_memory): # Counter should still maintain its value assert _global_memory['key_snippet_id_counter'] == 4 +def test_emit_task_with_id(reset_memory): + """Test emitting tasks with ID tracking""" + # Test adding a single task + task = "Implement new feature" + result = emit_task.invoke({"task": task}) + + # Verify return message includes task ID + assert result == "Task #0 stored." + + # Verify task stored correctly with ID + assert _global_memory['tasks'][0] == task + + # Verify counter incremented + assert _global_memory['task_id_counter'] == 1 + + # Add another task to verify counter continues correctly + task2 = "Fix bug" + result = emit_task.invoke({"task": task2}) + assert result == "Task #1 stored." + assert _global_memory['tasks'][1] == task2 + assert _global_memory['task_id_counter'] == 2 + +def test_delete_tasks(reset_memory): + """Test deleting tasks""" + # Add some test tasks + tasks = ["Task 1", "Task 2", "Task 3"] + for task in tasks: + emit_task.invoke({"task": task}) + + # Test deleting single task + result = delete_tasks.invoke({"task_ids": [1]}) + assert result == "Tasks deleted." + assert 1 not in _global_memory['tasks'] + assert len(_global_memory['tasks']) == 2 + + # Test deleting multiple tasks including non-existent ID + result = delete_tasks.invoke({"task_ids": [0, 2, 999]}) + assert result == "Tasks deleted." + assert len(_global_memory['tasks']) == 0 + + # Test deleting from empty tasks dict + result = delete_tasks.invoke({"task_ids": [0]}) + assert result == "Tasks deleted." + + # Counter should remain unchanged after deletions + assert _global_memory['task_id_counter'] == 3 + def test_emit_research_subtask(reset_memory): """Test emitting research subtasks""" # Test adding a research subtask