Add initial work log.
This commit is contained in:
parent
243cd55615
commit
87ec7c0268
|
|
@ -1,4 +1,8 @@
|
||||||
from typing import Dict, List, Any, Union, TypedDict, Optional, Sequence, Set
|
from typing import Dict, List, Any, Union, TypedDict, Optional, Sequence, Set, TypeVar, Literal
|
||||||
|
|
||||||
|
class WorkLogEntry(TypedDict):
|
||||||
|
timestamp: str
|
||||||
|
event: str
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.markdown import Markdown
|
from rich.markdown import Markdown
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
|
|
@ -15,7 +19,7 @@ class SnippetInfo(TypedDict):
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
# Global memory store
|
# Global memory store
|
||||||
_global_memory: Dict[str, Union[List[Any], Dict[int, str], Dict[int, SnippetInfo], int, Set[str], bool, str, int]] = {
|
_global_memory: Dict[str, Union[List[Any], Dict[int, str], Dict[int, SnippetInfo], int, Set[str], bool, str, int, List[WorkLogEntry]]] = {
|
||||||
'research_notes': [],
|
'research_notes': [],
|
||||||
'plans': [],
|
'plans': [],
|
||||||
'tasks': {}, # Dict[int, str] - ID to task mapping
|
'tasks': {}, # Dict[int, str] - ID to task mapping
|
||||||
|
|
@ -30,7 +34,8 @@ _global_memory: Dict[str, Union[List[Any], Dict[int, str], Dict[int, SnippetInfo
|
||||||
'related_files': {}, # Dict[int, str] - ID to filepath mapping
|
'related_files': {}, # Dict[int, str] - ID to filepath mapping
|
||||||
'related_file_id_counter': 1, # Counter for generating unique file IDs
|
'related_file_id_counter': 1, # Counter for generating unique file IDs
|
||||||
'plan_completed': False,
|
'plan_completed': False,
|
||||||
'research_depth': 0
|
'research_depth': 0,
|
||||||
|
'work_log': [] # List[WorkLogEntry] - Timestamped work events
|
||||||
}
|
}
|
||||||
|
|
||||||
@tool("emit_research_notes")
|
@tool("emit_research_notes")
|
||||||
|
|
@ -59,6 +64,7 @@ def emit_plan(plan: str) -> str:
|
||||||
"""
|
"""
|
||||||
_global_memory['plans'].append(plan)
|
_global_memory['plans'].append(plan)
|
||||||
console.print(Panel(Markdown(plan), title="📋 Plan"))
|
console.print(Panel(Markdown(plan), title="📋 Plan"))
|
||||||
|
log_work_event(f"Added plan step: {plan}")
|
||||||
return plan
|
return plan
|
||||||
|
|
||||||
@tool("emit_task")
|
@tool("emit_task")
|
||||||
|
|
@ -79,6 +85,7 @@ def emit_task(task: str) -> str:
|
||||||
_global_memory['tasks'][task_id] = task
|
_global_memory['tasks'][task_id] = task
|
||||||
|
|
||||||
console.print(Panel(Markdown(task), title=f"✅ Task #{task_id}"))
|
console.print(Panel(Markdown(task), title=f"✅ Task #{task_id}"))
|
||||||
|
log_work_event(f"Task #{task_id} added: {task}")
|
||||||
return f"Task #{task_id} stored."
|
return f"Task #{task_id} stored."
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -108,6 +115,7 @@ def emit_key_facts(facts: List[str]) -> str:
|
||||||
# Add result message
|
# Add result message
|
||||||
results.append(f"Stored fact #{fact_id}: {fact}")
|
results.append(f"Stored fact #{fact_id}: {fact}")
|
||||||
|
|
||||||
|
log_work_event(f"Stored {len(facts)} key facts")
|
||||||
return "Facts stored."
|
return "Facts stored."
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -131,6 +139,7 @@ def delete_key_facts(fact_ids: List[int]) -> str:
|
||||||
console.print(Panel(Markdown(success_msg), title="Fact Deleted", border_style="green"))
|
console.print(Panel(Markdown(success_msg), title="Fact Deleted", border_style="green"))
|
||||||
results.append(success_msg)
|
results.append(success_msg)
|
||||||
|
|
||||||
|
log_work_event(f"Deleted facts {fact_ids}")
|
||||||
return "Facts deleted."
|
return "Facts deleted."
|
||||||
|
|
||||||
@tool("delete_tasks")
|
@tool("delete_tasks")
|
||||||
|
|
@ -155,6 +164,7 @@ def delete_tasks(task_ids: List[int]) -> str:
|
||||||
border_style="green"))
|
border_style="green"))
|
||||||
results.append(success_msg)
|
results.append(success_msg)
|
||||||
|
|
||||||
|
log_work_event(f"Deleted tasks {task_ids}")
|
||||||
return "Tasks deleted."
|
return "Tasks deleted."
|
||||||
|
|
||||||
@tool("request_implementation")
|
@tool("request_implementation")
|
||||||
|
|
@ -172,6 +182,7 @@ def request_implementation() -> str:
|
||||||
"""
|
"""
|
||||||
_global_memory['implementation_requested'] = True
|
_global_memory['implementation_requested'] = True
|
||||||
console.print(Panel("🚀 Implementation Requested", style="yellow", padding=0))
|
console.print(Panel("🚀 Implementation Requested", style="yellow", padding=0))
|
||||||
|
log_work_event("Implementation requested")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -224,6 +235,7 @@ def emit_key_snippets(snippets: List[SnippetInfo]) -> str:
|
||||||
|
|
||||||
results.append(f"Stored snippet #{snippet_id}")
|
results.append(f"Stored snippet #{snippet_id}")
|
||||||
|
|
||||||
|
log_work_event(f"Stored {len(snippets)} code snippets")
|
||||||
return "Snippets stored."
|
return "Snippets stored."
|
||||||
|
|
||||||
@tool("delete_key_snippets")
|
@tool("delete_key_snippets")
|
||||||
|
|
@ -248,6 +260,7 @@ def delete_key_snippets(snippet_ids: List[int]) -> str:
|
||||||
border_style="green"))
|
border_style="green"))
|
||||||
results.append(success_msg)
|
results.append(success_msg)
|
||||||
|
|
||||||
|
log_work_event(f"Deleted snippets {snippet_ids}")
|
||||||
return "Snippets deleted."
|
return "Snippets deleted."
|
||||||
|
|
||||||
@tool("swap_task_order")
|
@tool("swap_task_order")
|
||||||
|
|
@ -297,6 +310,7 @@ def one_shot_completed(message: str) -> str:
|
||||||
_global_memory['task_completed'] = True
|
_global_memory['task_completed'] = True
|
||||||
_global_memory['completion_message'] = message
|
_global_memory['completion_message'] = message
|
||||||
console.print(Panel(Markdown(message), title="✅ Task Completed"))
|
console.print(Panel(Markdown(message), title="✅ Task Completed"))
|
||||||
|
log_work_event(f"Task completed: {message}")
|
||||||
return "Completion noted."
|
return "Completion noted."
|
||||||
|
|
||||||
@tool("task_completed")
|
@tool("task_completed")
|
||||||
|
|
@ -327,6 +341,7 @@ def plan_implementation_completed(message: str) -> str:
|
||||||
_global_memory['plan_completed'] = True
|
_global_memory['plan_completed'] = True
|
||||||
_global_memory['completion_message'] = message
|
_global_memory['completion_message'] = message
|
||||||
console.print(Panel(Markdown(message), title="✅ Plan Executed"))
|
console.print(Panel(Markdown(message), title="✅ Plan Executed"))
|
||||||
|
log_work_event(f"Plan execution completed: {message}")
|
||||||
return "Plan completion noted."
|
return "Plan completion noted."
|
||||||
|
|
||||||
def get_related_files() -> List[str]:
|
def get_related_files() -> List[str]:
|
||||||
|
|
@ -384,6 +399,50 @@ def emit_related_files(files: List[str]) -> str:
|
||||||
return '\n'.join(results)
|
return '\n'.join(results)
|
||||||
|
|
||||||
|
|
||||||
|
@tool("log_work_event")
|
||||||
|
def log_work_event(event: str) -> str:
|
||||||
|
"""Add timestamped entry to work log.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event: Description of the event
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Confirmation message
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
entry = WorkLogEntry(
|
||||||
|
timestamp=datetime.now().isoformat(),
|
||||||
|
event=event
|
||||||
|
)
|
||||||
|
_global_memory['work_log'].append(entry)
|
||||||
|
return f"Event logged: {event}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_work_log() -> str:
|
||||||
|
"""Return formatted markdown table of work log entries.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Formatted markdown table with timestamps and events
|
||||||
|
"""
|
||||||
|
if not _global_memory['work_log']:
|
||||||
|
return "No work log entries"
|
||||||
|
|
||||||
|
header = "| Timestamp | Event |\n|-----------|--------|"
|
||||||
|
rows = [f"| {entry['timestamp']} | {entry['event']} |"
|
||||||
|
for entry in _global_memory['work_log']]
|
||||||
|
return header + "\n".join(rows)
|
||||||
|
|
||||||
|
|
||||||
|
def reset_work_log() -> str:
|
||||||
|
"""Clear the work log.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Confirmation message
|
||||||
|
"""
|
||||||
|
_global_memory['work_log'].clear()
|
||||||
|
return "Work log cleared"
|
||||||
|
|
||||||
|
|
||||||
@tool("deregister_related_files")
|
@tool("deregister_related_files")
|
||||||
def deregister_related_files(file_ids: List[int]) -> str:
|
def deregister_related_files(file_ids: List[int]) -> str:
|
||||||
"""Delete multiple related files from global memory by their IDs.
|
"""Delete multiple related files from global memory by their IDs.
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ from ra_aid.tools.memory import (
|
||||||
deregister_related_files,
|
deregister_related_files,
|
||||||
emit_task,
|
emit_task,
|
||||||
delete_tasks,
|
delete_tasks,
|
||||||
swap_task_order
|
swap_task_order,
|
||||||
|
log_work_event,
|
||||||
|
reset_work_log
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -28,6 +30,7 @@ def reset_memory():
|
||||||
_global_memory['related_file_id_counter'] = 0
|
_global_memory['related_file_id_counter'] = 0
|
||||||
_global_memory['tasks'] = {}
|
_global_memory['tasks'] = {}
|
||||||
_global_memory['task_id_counter'] = 0
|
_global_memory['task_id_counter'] = 0
|
||||||
|
_global_memory['work_log'] = []
|
||||||
yield
|
yield
|
||||||
# Clean up after test
|
# Clean up after test
|
||||||
_global_memory['key_facts'] = {}
|
_global_memory['key_facts'] = {}
|
||||||
|
|
@ -40,6 +43,7 @@ def reset_memory():
|
||||||
_global_memory['related_file_id_counter'] = 0
|
_global_memory['related_file_id_counter'] = 0
|
||||||
_global_memory['tasks'] = {}
|
_global_memory['tasks'] = {}
|
||||||
_global_memory['task_id_counter'] = 0
|
_global_memory['task_id_counter'] = 0
|
||||||
|
_global_memory['work_log'] = []
|
||||||
|
|
||||||
def test_emit_key_facts_single_fact(reset_memory):
|
def test_emit_key_facts_single_fact(reset_memory):
|
||||||
"""Test emitting a single key fact using emit_key_facts"""
|
"""Test emitting a single key fact using emit_key_facts"""
|
||||||
|
|
@ -97,6 +101,61 @@ def test_get_memory_value_other_types(reset_memory):
|
||||||
# Test with non-existent key
|
# Test with non-existent key
|
||||||
assert get_memory_value('nonexistent') == ""
|
assert get_memory_value('nonexistent') == ""
|
||||||
|
|
||||||
|
def test_log_work_event(reset_memory):
|
||||||
|
"""Test logging work events with timestamps"""
|
||||||
|
# Log some events
|
||||||
|
log_work_event("Started task")
|
||||||
|
log_work_event("Made progress")
|
||||||
|
log_work_event("Completed task")
|
||||||
|
|
||||||
|
# Verify events are stored
|
||||||
|
assert len(_global_memory['work_log']) == 3
|
||||||
|
|
||||||
|
# Check event structure
|
||||||
|
event = _global_memory['work_log'][0]
|
||||||
|
assert isinstance(event['timestamp'], str)
|
||||||
|
assert event['event'] == "Started task"
|
||||||
|
|
||||||
|
# Verify order
|
||||||
|
assert _global_memory['work_log'][1]['event'] == "Made progress"
|
||||||
|
assert _global_memory['work_log'][2]['event'] == "Completed task"
|
||||||
|
|
||||||
|
def test_get_work_log(reset_memory):
|
||||||
|
"""Test work log formatting in markdown"""
|
||||||
|
# Add some events
|
||||||
|
log_work_event("First event")
|
||||||
|
log_work_event("Second event")
|
||||||
|
|
||||||
|
# Get formatted log
|
||||||
|
log = get_memory_value('work_log')
|
||||||
|
|
||||||
|
# Verify events and timestamps exist
|
||||||
|
for event in _global_memory['work_log']:
|
||||||
|
assert isinstance(event['timestamp'], str)
|
||||||
|
assert isinstance(event['event'], str)
|
||||||
|
|
||||||
|
# Verify events in chronological order
|
||||||
|
assert _global_memory['work_log'][0]['event'] == "First event"
|
||||||
|
assert _global_memory['work_log'][1]['event'] == "Second event"
|
||||||
|
|
||||||
|
def test_reset_work_log(reset_memory):
|
||||||
|
"""Test resetting the work log"""
|
||||||
|
# Add some events
|
||||||
|
log_work_event("Test event")
|
||||||
|
assert len(_global_memory['work_log']) == 1
|
||||||
|
|
||||||
|
# Reset log
|
||||||
|
reset_work_log()
|
||||||
|
|
||||||
|
# Verify log is empty
|
||||||
|
assert len(_global_memory['work_log']) == 0
|
||||||
|
assert get_memory_value('work_log') == ""
|
||||||
|
|
||||||
|
def test_empty_work_log(reset_memory):
|
||||||
|
"""Test empty work log behavior"""
|
||||||
|
# Fresh work log should return empty string
|
||||||
|
assert get_memory_value('work_log') == ""
|
||||||
|
|
||||||
def test_emit_key_facts(reset_memory):
|
def test_emit_key_facts(reset_memory):
|
||||||
"""Test emitting multiple key facts at once"""
|
"""Test emitting multiple key facts at once"""
|
||||||
# Test emitting multiple facts
|
# Test emitting multiple facts
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue