Improve work log format.
This commit is contained in:
parent
87ec7c0268
commit
0399c5e346
|
|
@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
- Do not put file ID in file paths when reading for expert context.
|
- Do not put file ID in file paths when reading for expert context.
|
||||||
|
- Agents log work internally, improving context information.
|
||||||
|
- Clear task list when plan is completed.
|
||||||
|
|
||||||
## [0.8.2] - 2024-12-23
|
## [0.8.2] - 2024-12-23
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Dict, List, Any, Union, TypedDict, Optional, Sequence, Set, TypeVar, Literal
|
from typing import Dict, List, Any, Union, TypedDict, Optional, Set
|
||||||
|
|
||||||
class WorkLogEntry(TypedDict):
|
class WorkLogEntry(TypedDict):
|
||||||
timestamp: str
|
timestamp: str
|
||||||
|
|
@ -6,7 +6,6 @@ class WorkLogEntry(TypedDict):
|
||||||
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
|
||||||
from rich.rule import Rule
|
|
||||||
from langchain_core.tools import tool
|
from langchain_core.tools import tool
|
||||||
|
|
||||||
class SnippetInfo(TypedDict):
|
class SnippetInfo(TypedDict):
|
||||||
|
|
@ -340,9 +339,10 @@ 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
|
||||||
|
_global_memory['tasks'].clear() # Clear task list when plan is completed
|
||||||
console.print(Panel(Markdown(message), title="✅ Plan Executed"))
|
console.print(Panel(Markdown(message), title="✅ Plan Executed"))
|
||||||
log_work_event(f"Plan execution completed: {message}")
|
log_work_event(f"Plan execution completed: {message}")
|
||||||
return "Plan completion noted."
|
return "Plan completion noted and task list cleared."
|
||||||
|
|
||||||
def get_related_files() -> List[str]:
|
def get_related_files() -> List[str]:
|
||||||
"""Get the current list of related files.
|
"""Get the current list of related files.
|
||||||
|
|
@ -399,15 +399,20 @@ 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:
|
def log_work_event(event: str) -> str:
|
||||||
"""Add timestamped entry to work log.
|
"""Add timestamped entry to work log.
|
||||||
|
|
||||||
|
Internal function used to track major events during agent execution.
|
||||||
|
Each entry is stored with an ISO format timestamp.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
event: Description of the event
|
event: Description of the event to log
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Confirmation message
|
Confirmation message
|
||||||
|
|
||||||
|
Note:
|
||||||
|
Entries can be retrieved with get_work_log() as markdown formatted text.
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
entry = WorkLogEntry(
|
entry = WorkLogEntry(
|
||||||
|
|
@ -419,18 +424,28 @@ def log_work_event(event: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def get_work_log() -> str:
|
def get_work_log() -> str:
|
||||||
"""Return formatted markdown table of work log entries.
|
"""Return formatted markdown of work log entries.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Formatted markdown table with timestamps and events
|
Markdown formatted text with timestamps as headings and events as content,
|
||||||
|
or 'No work log entries' if the log is empty.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
## 2024-12-23T11:39:10
|
||||||
|
Task #1 added: Create login form
|
||||||
"""
|
"""
|
||||||
if not _global_memory['work_log']:
|
if not _global_memory['work_log']:
|
||||||
return "No work log entries"
|
return "No work log entries"
|
||||||
|
|
||||||
header = "| Timestamp | Event |\n|-----------|--------|"
|
entries = []
|
||||||
rows = [f"| {entry['timestamp']} | {entry['event']} |"
|
for entry in _global_memory['work_log']:
|
||||||
for entry in _global_memory['work_log']]
|
entries.extend([
|
||||||
return header + "\n".join(rows)
|
f"## {entry['timestamp']}",
|
||||||
|
entry['event'],
|
||||||
|
"" # Blank line between entries
|
||||||
|
])
|
||||||
|
|
||||||
|
return "\n".join(entries).rstrip() # Remove trailing newline
|
||||||
|
|
||||||
|
|
||||||
def reset_work_log() -> str:
|
def reset_work_log() -> str:
|
||||||
|
|
@ -438,6 +453,9 @@ def reset_work_log() -> str:
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Confirmation message
|
Confirmation message
|
||||||
|
|
||||||
|
Note:
|
||||||
|
This permanently removes all work log entries. The operation cannot be undone.
|
||||||
"""
|
"""
|
||||||
_global_memory['work_log'].clear()
|
_global_memory['work_log'].clear()
|
||||||
return "Work log cleared"
|
return "Work log cleared"
|
||||||
|
|
@ -525,5 +543,12 @@ def get_memory_value(key: str) -> str:
|
||||||
snippets.append("\n".join(snippet_text))
|
snippets.append("\n".join(snippet_text))
|
||||||
return "\n\n".join(snippets)
|
return "\n\n".join(snippets)
|
||||||
|
|
||||||
|
if key == 'work_log':
|
||||||
|
if not values:
|
||||||
|
return ""
|
||||||
|
entries = [f"## {entry['timestamp']}\n{entry['event']}"
|
||||||
|
for entry in values]
|
||||||
|
return "\n\n".join(entries)
|
||||||
|
|
||||||
# For other types (lists), join with newlines
|
# For other types (lists), join with newlines
|
||||||
return "\n".join(str(v) for v in values)
|
return "\n".join(str(v) for v in values)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ from ra_aid.tools.memory import (
|
||||||
delete_tasks,
|
delete_tasks,
|
||||||
swap_task_order,
|
swap_task_order,
|
||||||
log_work_event,
|
log_work_event,
|
||||||
reset_work_log
|
reset_work_log,
|
||||||
|
get_work_log
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
@ -121,22 +122,37 @@ def test_log_work_event(reset_memory):
|
||||||
assert _global_memory['work_log'][2]['event'] == "Completed task"
|
assert _global_memory['work_log'][2]['event'] == "Completed task"
|
||||||
|
|
||||||
def test_get_work_log(reset_memory):
|
def test_get_work_log(reset_memory):
|
||||||
"""Test work log formatting in markdown"""
|
"""Test work log formatting with heading-based markdown"""
|
||||||
|
# Test empty log
|
||||||
|
assert get_work_log() == "No work log entries"
|
||||||
|
|
||||||
# Add some events
|
# Add some events
|
||||||
log_work_event("First event")
|
log_work_event("First event")
|
||||||
log_work_event("Second event")
|
log_work_event("Second event")
|
||||||
|
|
||||||
# Get formatted log
|
# Get formatted log
|
||||||
log = get_memory_value('work_log')
|
log = get_work_log()
|
||||||
|
|
||||||
# Verify events and timestamps exist
|
# Split into entries for detailed verification
|
||||||
for event in _global_memory['work_log']:
|
entries = log.split('\n\n') # Double newline separates entries
|
||||||
assert isinstance(event['timestamp'], str)
|
assert len(entries) == 2 # Should have two entries
|
||||||
assert isinstance(event['event'], str)
|
|
||||||
|
|
||||||
# Verify events in chronological order
|
# Verify first entry format
|
||||||
assert _global_memory['work_log'][0]['event'] == "First event"
|
first_entry_lines = entries[0].split('\n')
|
||||||
assert _global_memory['work_log'][1]['event'] == "Second event"
|
assert first_entry_lines[0].startswith('## ') # Level 2 heading
|
||||||
|
assert first_entry_lines[0][3:].strip() != '' # Timestamp present
|
||||||
|
assert first_entry_lines[1] == "First event" # Event text
|
||||||
|
|
||||||
|
# Verify second entry format
|
||||||
|
second_entry_lines = entries[1].split('\n')
|
||||||
|
assert second_entry_lines[0].startswith('## ') # Level 2 heading
|
||||||
|
assert second_entry_lines[0][3:].strip() != '' # Timestamp present
|
||||||
|
assert second_entry_lines[1] == "Second event" # Event text
|
||||||
|
|
||||||
|
# Verify chronological order by comparing timestamps
|
||||||
|
first_timestamp = first_entry_lines[0][3:] # Skip '## '
|
||||||
|
second_timestamp = second_entry_lines[0][3:] # Skip '## '
|
||||||
|
assert first_timestamp < second_timestamp
|
||||||
|
|
||||||
def test_reset_work_log(reset_memory):
|
def test_reset_work_log(reset_memory):
|
||||||
"""Test resetting the work log"""
|
"""Test resetting the work log"""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue