record trajectory at all steps

This commit is contained in:
AI Christianson 2025-03-11 11:01:05 -04:00
parent 5d899d3d13
commit ae9cf5021b
19 changed files with 1522 additions and 21 deletions

View File

@ -616,6 +616,24 @@ def main():
)
if args.research_only:
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
error_message = "Chat mode cannot be used with --research-only"
trajectory_repo.create(
step_data={
"display_title": "Error",
"error_message": error_message,
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message,
)
except Exception as traj_error:
# Swallow exception to avoid recursion
logger.debug(f"Error recording trajectory: {traj_error}")
pass
print_error("Chat mode cannot be used with --research-only")
sys.exit(1)
@ -719,6 +737,24 @@ def main():
# Validate message is provided
if not args.message:
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
error_message = "--message is required"
trajectory_repo.create(
step_data={
"display_title": "Error",
"error_message": error_message,
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message,
)
except Exception as traj_error:
# Swallow exception to avoid recursion
logger.debug(f"Error recording trajectory: {traj_error}")
pass
print_error("--message is required")
sys.exit(1)

View File

@ -462,6 +462,38 @@ class CiaynAgent:
error_msg = f"Error: {str(e)} \n Could not execute code: {code}"
tool_name = self.extract_tool_name(code)
logger.info(f"Tool execution failed for `{tool_name}`: {str(e)}")
# Record error in trajectory
try:
# Import here to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db())
human_input_repo = HumanInputRepository(get_db())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": f"Tool execution failed for `{tool_name}`:\nError: {str(e)}",
"display_title": "Tool Error",
"code": code,
"tool_name": tool_name
},
record_type="tool_execution",
human_input_id=human_input_id,
is_error=True,
error_message=str(e),
error_type="ToolExecutionError",
tool_name=tool_name,
tool_parameters={"code": code}
)
except Exception as trajectory_error:
# Just log and continue if there's an error in trajectory recording
logger.error(f"Error recording trajectory for tool error display: {trajectory_error}")
print_warning(f"Tool execution failed for `{tool_name}`:\nError: {str(e)}\n\nCode:\n\n````\n{code}\n````", title="Tool Error")
raise ToolExecutionError(
error_msg, base_message=msg, tool_name=tool_name
@ -495,6 +527,36 @@ class CiaynAgent:
if not fallback_response:
self.chat_history.append(err_msg)
logger.info(f"Tool fallback was attempted but did not succeed. Original error: {str(e)}")
# Record error in trajectory
try:
# Import here to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db())
human_input_repo = HumanInputRepository(get_db())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": f"Tool fallback was attempted but did not succeed. Original error: {str(e)}",
"display_title": "Fallback Failed",
"tool_name": e.tool_name if hasattr(e, "tool_name") else "unknown_tool"
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=str(e),
error_type="FallbackFailedError",
tool_name=e.tool_name if hasattr(e, "tool_name") else "unknown_tool"
)
except Exception as trajectory_error:
# Just log and continue if there's an error in trajectory recording
logger.error(f"Error recording trajectory for fallback failed warning: {trajectory_error}")
print_warning(f"Tool fallback was attempted but did not succeed. Original error: {str(e)}", title="Fallback Failed")
return ""
@ -595,6 +657,35 @@ class CiaynAgent:
matches = re.findall(pattern, response, re.DOTALL)
if len(matches) == 0:
logger.info("Failed to extract a valid tool call from the model's response.")
# Record error in trajectory
try:
# Import here to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db())
human_input_repo = HumanInputRepository(get_db())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": "Failed to extract a valid tool call from the model's response.",
"display_title": "Extraction Failed",
"code": code
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message="Failed to extract a valid tool call from the model's response.",
error_type="ExtractionError"
)
except Exception as trajectory_error:
# Just log and continue if there's an error in trajectory recording
logger.error(f"Error recording trajectory for extraction error display: {trajectory_error}")
print_warning("Failed to extract a valid tool call from the model's response.", title="Extraction Failed")
raise ToolExecutionError("Failed to extract tool call")
ma = matches[0][0].strip()
@ -647,6 +738,36 @@ class CiaynAgent:
warning_message = f"The model returned an empty response (attempt {empty_response_count} of {max_empty_responses}). Requesting the model to make a valid tool call."
logger.info(warning_message)
# Record warning in trajectory
try:
# Import here to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db_connection
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db_connection())
human_input_repo = HumanInputRepository(get_db_connection())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"warning_message": warning_message,
"display_title": "Empty Response",
"attempt": empty_response_count,
"max_attempts": max_empty_responses
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=warning_message,
error_type="EmptyResponseWarning"
)
except Exception as trajectory_error:
# Just log and continue if there's an error in trajectory recording
logger.error(f"Error recording trajectory for empty response warning: {trajectory_error}")
print_warning(warning_message, title="Empty Response")
if empty_response_count >= max_empty_responses:
@ -658,6 +779,36 @@ class CiaynAgent:
error_message = "The agent has crashed after multiple failed attempts to generate a valid tool call."
logger.error(error_message)
# Record error in trajectory
try:
# Import here to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db_connection
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db_connection())
human_input_repo = HumanInputRepository(get_db_connection())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_message,
"display_title": "Agent Crashed",
"crash_reason": crash_message,
"attempts": empty_response_count
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message,
error_type="AgentCrashError"
)
except Exception as trajectory_error:
# Just log and continue if there's an error in trajectory recording
logger.error(f"Error recording trajectory for agent crash: {trajectory_error}")
print_error(error_message)
yield self._create_error_chunk(crash_message)

View File

@ -106,6 +106,7 @@ from ra_aid.database.repositories.human_input_repository import (
from ra_aid.database.repositories.research_note_repository import (
get_research_note_repository,
)
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.work_log_repository import get_work_log_repository
from ra_aid.model_formatters import format_key_facts_dict
from ra_aid.model_formatters.key_snippets_formatter import format_key_snippets_dict
@ -460,9 +461,23 @@ def _handle_api_error(e, attempt, max_retries, base_delay):
logger.warning("API error (attempt %d/%d): %s", attempt + 1, max_retries, str(e))
delay = base_delay * (2**attempt)
print_error(
f"Encountered {e.__class__.__name__}: {e}. Retrying in {delay}s... (Attempt {attempt+1}/{max_retries})"
error_message = f"Encountered {e.__class__.__name__}: {e}. Retrying in {delay}s... (Attempt {attempt+1}/{max_retries})"
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_message,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message
)
print_error(error_message)
start = time.monotonic()
while time.monotonic() - start < delay:
check_interrupt()

View File

@ -22,6 +22,7 @@ from ra_aid import agent_utils
from ra_aid.database.repositories.key_fact_repository import get_key_fact_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
from ra_aid.database.repositories.config_repository import get_config_repository
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.llm import initialize_llm
from ra_aid.prompts.key_facts_gc_prompts import KEY_FACTS_GC_PROMPT
from ra_aid.tools.memory import log_work_event
@ -82,6 +83,22 @@ def delete_key_facts(fact_ids: List[int]) -> str:
if deleted_facts:
deleted_msg = "Successfully deleted facts:\n" + "\n".join([f"- #{fact_id}: {content}" for fact_id, content in deleted_facts])
result_parts.append(deleted_msg)
# Record GC operation in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"deleted_facts": deleted_facts,
"display_title": "Facts Deleted",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(Markdown(deleted_msg), title="Facts Deleted", border_style="green")
)
@ -89,6 +106,22 @@ def delete_key_facts(fact_ids: List[int]) -> str:
if protected_facts:
protected_msg = "Protected facts (associated with current request):\n" + "\n".join([f"- #{fact_id}: {content}" for fact_id, content in protected_facts])
result_parts.append(protected_msg)
# Record GC operation in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"protected_facts": protected_facts,
"display_title": "Facts Protected",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(Markdown(protected_msg), title="Facts Protected", border_style="blue")
)
@ -120,10 +153,44 @@ def run_key_facts_gc_agent() -> None:
fact_count = len(facts)
except RuntimeError as e:
logger.error(f"Failed to access key fact repository: {str(e)}")
# Record GC error in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error": str(e),
"display_title": "GC Error",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent",
is_error=True,
error_message=str(e),
error_type="Repository Error"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"Error: {str(e)}", title="🗑 GC Error", border_style="red"))
return # Exit the function if we can't access the repository
# Display status panel with fact count included
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"fact_count": fact_count,
"display_title": "Garbage Collection",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"Gathering my thoughts...\nCurrent number of key facts: {fact_count}", title="🗑 Garbage Collection"))
# Only run the agent if we actually have facts to clean
@ -185,6 +252,24 @@ def run_key_facts_gc_agent() -> None:
# Show info panel with updated count and protected facts count
protected_count = len(protected_facts)
if protected_count > 0:
# Record GC completion in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"original_count": fact_count,
"updated_count": updated_count,
"protected_count": protected_count,
"display_title": "GC Complete",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(
f"Cleaned key facts: {fact_count}{updated_count}\nProtected facts (associated with current request): {protected_count}",
@ -192,6 +277,24 @@ def run_key_facts_gc_agent() -> None:
)
)
else:
# Record GC completion in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"original_count": fact_count,
"updated_count": updated_count,
"protected_count": 0,
"display_title": "GC Complete",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(
f"Cleaned key facts: {fact_count}{updated_count}",
@ -199,6 +302,40 @@ def run_key_facts_gc_agent() -> None:
)
)
else:
# Record GC info in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"protected_count": len(protected_facts),
"message": "All facts are protected",
"display_title": "GC Info",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"All {len(protected_facts)} facts are associated with the current request and protected from deletion.", title="🗑 GC Info"))
else:
# Record GC info in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"fact_count": 0,
"message": "No key facts to clean",
"display_title": "GC Info",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_facts_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel("No key facts to clean.", title="🗑 GC Info"))

View File

@ -18,6 +18,7 @@ from ra_aid import agent_utils
from ra_aid.database.repositories.key_snippet_repository import get_key_snippet_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
from ra_aid.database.repositories.config_repository import get_config_repository
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.llm import initialize_llm
from ra_aid.prompts.key_snippets_gc_prompts import KEY_SNIPPETS_GC_PROMPT
from ra_aid.tools.memory import log_work_event
@ -65,6 +66,23 @@ def delete_key_snippets(snippet_ids: List[int]) -> str:
success = get_key_snippet_repository().delete(snippet_id)
if success:
success_msg = f"Successfully deleted snippet #{snippet_id} from {filepath}"
# Record GC operation in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"deleted_snippet_id": snippet_id,
"filepath": filepath,
"display_title": "Snippet Deleted",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_snippets_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(
Markdown(success_msg), title="Snippet Deleted", border_style="green"
@ -86,6 +104,22 @@ def delete_key_snippets(snippet_ids: List[int]) -> str:
if protected_snippets:
protected_msg = "Protected snippets (associated with current request):\n" + "\n".join([f"- #{snippet_id}: {filepath}" for snippet_id, filepath in protected_snippets])
result_parts.append(protected_msg)
# Record GC operation in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"protected_snippets": protected_snippets,
"display_title": "Snippets Protected",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_snippets_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(Markdown(protected_msg), title="Snippets Protected", border_style="blue")
)
@ -116,6 +150,21 @@ def run_key_snippets_gc_agent() -> None:
snippet_count = len(snippets)
# Display status panel with snippet count included
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"snippet_count": snippet_count,
"display_title": "Garbage Collection",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_snippets_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"Gathering my thoughts...\nCurrent number of key snippets: {snippet_count}", title="🗑 Garbage Collection"))
# Only run the agent if we actually have snippets to clean
@ -185,6 +234,24 @@ def run_key_snippets_gc_agent() -> None:
# Show info panel with updated count and protected snippets count
protected_count = len(protected_snippets)
if protected_count > 0:
# Record GC completion in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"original_count": snippet_count,
"updated_count": updated_count,
"protected_count": protected_count,
"display_title": "GC Complete",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_snippets_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(
f"Cleaned key snippets: {snippet_count}{updated_count}\nProtected snippets (associated with current request): {protected_count}",
@ -192,6 +259,24 @@ def run_key_snippets_gc_agent() -> None:
)
)
else:
# Record GC completion in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"original_count": snippet_count,
"updated_count": updated_count,
"protected_count": 0,
"display_title": "GC Complete",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_snippets_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(
f"Cleaned key snippets: {snippet_count}{updated_count}",
@ -199,6 +284,40 @@ def run_key_snippets_gc_agent() -> None:
)
)
else:
# Record GC info in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"protected_count": len(protected_snippets),
"message": "All snippets are protected",
"display_title": "GC Info",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_snippets_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"All {len(protected_snippets)} snippets are associated with the current request and protected from deletion.", title="🗑 GC Info"))
else:
# Record GC info in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"snippet_count": 0,
"message": "No key snippets to clean",
"display_title": "GC Info",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="key_snippets_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel("No key snippets to clean.", title="🗑 GC Info"))

View File

@ -22,6 +22,7 @@ from ra_aid.agent_utils import create_agent, run_agent_with_retry
from ra_aid.database.repositories.research_note_repository import get_research_note_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
from ra_aid.database.repositories.config_repository import get_config_repository
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.llm import initialize_llm
from ra_aid.model_formatters.research_notes_formatter import format_research_note
from ra_aid.tools.memory import log_work_event
@ -84,6 +85,22 @@ def delete_research_notes(note_ids: List[int]) -> str:
if deleted_notes:
deleted_msg = "Successfully deleted research notes:\n" + "\n".join([f"- #{note_id}: {content[:100]}..." if len(content) > 100 else f"- #{note_id}: {content}" for note_id, content in deleted_notes])
result_parts.append(deleted_msg)
# Record GC operation in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"deleted_notes": deleted_notes,
"display_title": "Research Notes Deleted",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(Markdown(deleted_msg), title="Research Notes Deleted", border_style="green")
)
@ -91,6 +108,22 @@ def delete_research_notes(note_ids: List[int]) -> str:
if protected_notes:
protected_msg = "Protected research notes (associated with current request):\n" + "\n".join([f"- #{note_id}: {content[:100]}..." if len(content) > 100 else f"- #{note_id}: {content}" for note_id, content in protected_notes])
result_parts.append(protected_msg)
# Record GC operation in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"protected_notes": protected_notes,
"display_title": "Research Notes Protected",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(Markdown(protected_msg), title="Research Notes Protected", border_style="blue")
)
@ -125,10 +158,44 @@ def run_research_notes_gc_agent(threshold: int = 30) -> None:
note_count = len(notes)
except RuntimeError as e:
logger.error(f"Failed to access research note repository: {str(e)}")
# Record GC error in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error": str(e),
"display_title": "GC Error",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent",
is_error=True,
error_message=str(e),
error_type="Repository Error"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"Error: {str(e)}", title="🗑 GC Error", border_style="red"))
return # Exit the function if we can't access the repository
# Display status panel with note count included
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"note_count": note_count,
"display_title": "Garbage Collection",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"Gathering my thoughts...\nCurrent number of research notes: {note_count}", title="🗑 Garbage Collection"))
# Only run the agent if we actually have notes to clean and we're over the threshold
@ -235,6 +302,24 @@ Remember: Your goal is to maintain a concise, high-value collection of research
# Show info panel with updated count and protected notes count
protected_count = len(protected_notes)
if protected_count > 0:
# Record GC completion in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"original_count": note_count,
"updated_count": updated_count,
"protected_count": protected_count,
"display_title": "GC Complete",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(
f"Cleaned research notes: {note_count}{updated_count}\nProtected notes (associated with current request): {protected_count}",
@ -242,6 +327,24 @@ Remember: Your goal is to maintain a concise, high-value collection of research
)
)
else:
# Record GC completion in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"original_count": note_count,
"updated_count": updated_count,
"protected_count": 0,
"display_title": "GC Complete",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(
Panel(
f"Cleaned research notes: {note_count}{updated_count}",
@ -249,6 +352,41 @@ Remember: Your goal is to maintain a concise, high-value collection of research
)
)
else:
# Record GC info in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"protected_count": len(protected_notes),
"message": "All research notes are protected",
"display_title": "GC Info",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"All {len(protected_notes)} research notes are associated with the current request and protected from deletion.", title="🗑 GC Info"))
else:
# Record GC info in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"note_count": note_count,
"threshold": threshold,
"message": "Below threshold - no cleanup needed",
"display_title": "GC Info",
},
record_type="gc_operation",
human_input_id=human_input_id,
tool_name="research_notes_gc_agent"
)
except Exception:
pass # Continue if trajectory recording fails
console.print(Panel(f"Research notes count ({note_count}) is below threshold ({threshold}). No cleanup needed.", title="🗑 GC Info"))

View File

@ -154,6 +154,24 @@ class FallbackHandler:
logger.debug(
f"Tool call failed {self.tool_failure_consecutive_failures} times. Attempting fallback for tool: {self.current_failing_tool_name}"
)
# Import repository classes directly to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db())
human_input_repo = HumanInputRepository(get_db())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"message": f"**Tool fallback activated**: Attempting fallback for tool {self.current_failing_tool_name}.",
"display_title": "Fallback Notification",
},
record_type="info",
human_input_id=human_input_id
)
cpm(
f"**Tool fallback activated**: Attempting fallback for tool {self.current_failing_tool_name}.",
title="Fallback Notification",
@ -163,6 +181,24 @@ class FallbackHandler:
if result_list:
return result_list
# Import repository classes directly to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db())
human_input_repo = HumanInputRepository(get_db())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"message": "All fallback models have failed.",
"display_title": "Fallback Failed",
},
record_type="error",
human_input_id=human_input_id
)
cpm("All fallback models have failed.", title="Fallback Failed")
current_failing_tool_name = self.current_failing_tool_name

View File

@ -234,6 +234,24 @@ def create_llm_client(
elif supports_temperature:
if temperature is None:
temperature = 0.7
# Import repository classes directly to avoid circular imports
from ra_aid.database.repositories.trajectory_repository import TrajectoryRepository
from ra_aid.database.repositories.human_input_repository import HumanInputRepository
from ra_aid.database.connection import get_db
# Create repositories directly
trajectory_repo = TrajectoryRepository(get_db())
human_input_repo = HumanInputRepository(get_db())
human_input_id = human_input_repo.get_most_recent_id()
trajectory_repo.create(
step_data={
"message": "This model supports temperature argument but none was given. Setting default temperature to 0.7.",
"display_title": "Information",
},
record_type="info",
human_input_id=human_input_id
)
cpm(
"This model supports temperature argument but none was given. Setting default temperature to 0.7."
)

View File

@ -17,6 +17,8 @@ __all__ = [
from ra_aid.file_listing import FileListerError, get_file_listing
from ra_aid.project_state import ProjectStateError, is_new_project
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
@dataclass
@ -130,6 +132,24 @@ def display_project_status(info: ProjectInfo) -> None:
{status} with **{file_count} file(s)**
"""
# Record project status in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"project_status": "new" if info.is_new else "existing",
"file_count": file_count,
"total_files": info.total_files,
"display_title": "Project Status",
},
record_type="info",
human_input_id=human_input_id
)
except Exception as e:
# Silently continue if trajectory recording fails
pass
# Create and display panel
console = Console()
console.print(Panel(Markdown(status_text.strip()), title="📊 Project Status"))

View File

@ -62,7 +62,23 @@ def request_research(query: str) -> ResearchResult:
# Check recursion depth
current_depth = get_depth()
if current_depth >= RESEARCH_AGENT_RECURSION_LIMIT:
print_error("Maximum research recursion depth reached")
error_message = "Maximum research recursion depth reached"
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_message,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message
)
print_error(error_message)
try:
key_facts = format_key_facts_dict(get_key_fact_repository().get_facts_dict())
except RuntimeError as e:
@ -109,7 +125,23 @@ def request_research(query: str) -> ResearchResult:
except KeyboardInterrupt:
raise
except Exception as e:
print_error(f"Error during research: {str(e)}")
error_message = f"Error during research: {str(e)}"
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_message,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message
)
print_error(error_message)
success = False
reason = f"error: {str(e)}"
finally:
@ -194,7 +226,23 @@ def request_web_research(query: str) -> ResearchResult:
except KeyboardInterrupt:
raise
except Exception as e:
print_error(f"Error during web research: {str(e)}")
error_message = f"Error during web research: {str(e)}"
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_message,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message
)
print_error(error_message)
success = False
reason = f"error: {str(e)}"
finally:
@ -384,7 +432,23 @@ def request_task_implementation(task_spec: str) -> str:
except KeyboardInterrupt:
raise
except Exception as e:
print_error(f"Error during task implementation: {str(e)}")
error_message = f"Error during task implementation: {str(e)}"
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_message,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message
)
print_error(error_message)
success = False
reason = f"error: {str(e)}"
@ -515,7 +579,23 @@ def request_implementation(task_spec: str) -> str:
except KeyboardInterrupt:
raise
except Exception as e:
print_error(f"Error during planning: {str(e)}")
error_message = f"Error during planning: {str(e)}"
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_message,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_message
)
print_error(error_message)
success = False
reason = f"error: {str(e)}"

View File

@ -9,6 +9,9 @@ from rich.panel import Panel
logger = logging.getLogger(__name__)
from ..database.repositories.trajectory_repository import get_trajectory_repository
from ..database.repositories.human_input_repository import get_human_input_repository
from ..database.repositories.key_fact_repository import get_key_fact_repository
from ..database.repositories.key_snippet_repository import get_key_snippet_repository
from ..database.repositories.related_files_repository import get_related_files_repository
@ -72,6 +75,23 @@ def emit_expert_context(context: str) -> str:
"""
expert_context["text"].append(context)
# Record expert context in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="emit_expert_context",
tool_parameters={"context_length": len(context)},
step_data={
"display_title": "Expert Context",
"context_length": len(context),
},
record_type="tool_execution",
human_input_id=human_input_id
)
except Exception as e:
logger.error(f"Failed to record trajectory: {e}")
# Create and display status panel
panel_content = f"Added expert context ({len(context)} characters)"
console.print(Panel(panel_content, title="Expert Context", border_style="blue"))
@ -184,6 +204,23 @@ def ask_expert(question: str) -> str:
# Build display query (just question)
display_query = "# Question\n" + question
# Record expert query in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="ask_expert",
tool_parameters={"question": question},
step_data={
"display_title": "Expert Query",
"question": question,
},
record_type="tool_execution",
human_input_id=human_input_id
)
except Exception as e:
logger.error(f"Failed to record trajectory: {e}")
# Show only question in panel
console.print(
Panel(Markdown(display_query), title="🤔 Expert Query", border_style="yellow")
@ -263,6 +300,23 @@ def ask_expert(question: str) -> str:
logger.error(f"Exception during content processing: {str(e)}")
raise
# Record expert response in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="ask_expert",
tool_parameters={"question": question},
step_data={
"display_title": "Expert Response",
"response_length": len(content),
},
record_type="tool_execution",
human_input_id=human_input_id
)
except Exception as e:
logger.error(f"Failed to record trajectory: {e}")
# Format and display response
console.print(
Panel(Markdown(content), title="Expert Response", border_style="blue")

View File

@ -7,6 +7,8 @@ from rich.panel import Panel
from ra_aid.console import console
from ra_aid.console.formatting import print_error
from ra_aid.tools.memory import emit_related_files
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
def truncate_display_str(s: str, max_length: int = 30) -> str:
@ -54,6 +56,32 @@ def file_str_replace(filepath: str, old_str: str, new_str: str, *, replace_all:
path = Path(filepath)
if not path.exists():
msg = f"File not found: {filepath}"
# Record error in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": msg,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=msg,
tool_name="file_str_replace",
tool_parameters={
"filepath": filepath,
"old_str": old_str,
"new_str": new_str,
"replace_all": replace_all
}
)
except Exception:
# Silently handle trajectory recording failures (e.g., in test environments)
pass
print_error(msg)
return {"success": False, "message": msg}
@ -62,10 +90,62 @@ def file_str_replace(filepath: str, old_str: str, new_str: str, *, replace_all:
if count == 0:
msg = f"String not found: {truncate_display_str(old_str)}"
# Record error in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": msg,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=msg,
tool_name="file_str_replace",
tool_parameters={
"filepath": filepath,
"old_str": old_str,
"new_str": new_str,
"replace_all": replace_all
}
)
except Exception:
# Silently handle trajectory recording failures (e.g., in test environments)
pass
print_error(msg)
return {"success": False, "message": msg}
elif count > 1 and not replace_all:
msg = f"String appears {count} times - must be unique (use replace_all=True to replace all occurrences)"
# Record error in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": msg,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=msg,
tool_name="file_str_replace",
tool_parameters={
"filepath": filepath,
"old_str": old_str,
"new_str": new_str,
"replace_all": replace_all
}
)
except Exception:
# Silently handle trajectory recording failures (e.g., in test environments)
pass
print_error(msg)
return {"success": False, "message": msg}
@ -93,7 +173,34 @@ def file_str_replace(filepath: str, old_str: str, new_str: str, *, replace_all:
emit_related_files.invoke({"files": [filepath]})
except Exception as e:
# Don't let related files error affect main function success
print_error(f"Note: Could not add to related files: {str(e)}")
error_msg = f"Note: Could not add to related files: {str(e)}"
# Record error in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": error_msg,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=error_msg,
tool_name="file_str_replace",
tool_parameters={
"filepath": filepath,
"old_str": old_str,
"new_str": new_str,
"replace_all": replace_all
}
)
except Exception:
# Silently handle trajectory recording failures (e.g., in test environments)
pass
print_error(error_msg)
return {
"success": True,
@ -102,5 +209,31 @@ def file_str_replace(filepath: str, old_str: str, new_str: str, *, replace_all:
except Exception as e:
msg = f"Error: {str(e)}"
# Record error in trajectory
try:
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
step_data={
"error_message": msg,
"display_title": "Error",
},
record_type="error",
human_input_id=human_input_id,
is_error=True,
error_message=msg,
tool_name="file_str_replace",
tool_parameters={
"filepath": filepath,
"old_str": old_str,
"new_str": new_str,
"replace_all": replace_all
}
)
except Exception:
# Silently handle trajectory recording failures (e.g., in test environments)
pass
print_error(msg)
return {"success": False, "message": msg}

View File

@ -1,5 +1,6 @@
import fnmatch
from typing import List, Tuple
import logging
from typing import List, Tuple, Dict, Optional, Any
from fuzzywuzzy import process
from git import Repo, exc
@ -12,6 +13,49 @@ from ra_aid.file_listing import get_all_project_files, FileListerError
console = Console()
def record_trajectory(
tool_name: str,
tool_parameters: Dict,
step_data: Dict,
record_type: str = "tool_execution",
is_error: bool = False,
error_message: Optional[str] = None,
error_type: Optional[str] = None
) -> None:
"""
Helper function to record trajectory information, handling the case when repositories are not available.
Args:
tool_name: Name of the tool
tool_parameters: Parameters passed to the tool
step_data: UI rendering data
record_type: Type of trajectory record
is_error: Flag indicating if this record represents an error
error_message: The error message
error_type: The type/class of the error
"""
try:
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name=tool_name,
tool_parameters=tool_parameters,
step_data=step_data,
record_type=record_type,
human_input_id=human_input_id,
is_error=is_error,
error_message=error_message,
error_type=error_type
)
except (ImportError, RuntimeError):
# If either the repository modules can't be imported or no repository is available,
# just log and continue without recording trajectory
logging.debug("Skipping trajectory recording: repositories not available")
DEFAULT_EXCLUDE_PATTERNS = [
"*.pyc",
"__pycache__/*",
@ -57,7 +101,32 @@ def fuzzy_find_project_files(
"""
# Validate threshold
if not 0 <= threshold <= 100:
raise ValueError("Threshold must be between 0 and 100")
error_msg = "Threshold must be between 0 and 100"
# Record error in trajectory
record_trajectory(
tool_name="fuzzy_find_project_files",
tool_parameters={
"search_term": search_term,
"repo_path": repo_path,
"threshold": threshold,
"max_results": max_results,
"include_paths": include_paths,
"exclude_patterns": exclude_patterns,
"include_hidden": include_hidden
},
step_data={
"search_term": search_term,
"display_title": "Invalid Threshold Value",
"error_message": error_msg
},
record_type="tool_execution",
is_error=True,
error_message=error_msg,
error_type="ValueError"
)
raise ValueError(error_msg)
# Handle empty search term as special case
if not search_term:
@ -126,6 +195,27 @@ def fuzzy_find_project_files(
else:
info_sections.append("## Results\n*No matches found*")
# Record fuzzy find in trajectory
record_trajectory(
tool_name="fuzzy_find_project_files",
tool_parameters={
"search_term": search_term,
"repo_path": repo_path,
"threshold": threshold,
"max_results": max_results,
"include_paths": include_paths,
"exclude_patterns": exclude_patterns,
"include_hidden": include_hidden
},
step_data={
"search_term": search_term,
"display_title": "Fuzzy Find Results",
"total_files": len(all_files),
"matches_found": len(filtered_matches)
},
record_type="tool_execution"
)
# Display the panel
console.print(
Panel(
@ -138,5 +228,30 @@ def fuzzy_find_project_files(
return filtered_matches
except FileListerError as e:
console.print(f"[bold red]Error listing files: {e}[/bold red]")
error_msg = f"Error listing files: {e}"
# Record error in trajectory
record_trajectory(
tool_name="fuzzy_find_project_files",
tool_parameters={
"search_term": search_term,
"repo_path": repo_path,
"threshold": threshold,
"max_results": max_results,
"include_paths": include_paths,
"exclude_patterns": exclude_patterns,
"include_hidden": include_hidden
},
step_data={
"search_term": search_term,
"display_title": "Fuzzy Find Error",
"error_message": error_msg
},
record_type="tool_execution",
is_error=True,
error_message=error_msg,
error_type=type(e).__name__
)
console.print(f"[bold red]{error_msg}[/bold red]")
return []

View File

@ -17,6 +17,7 @@ from ra_aid.database.repositories.key_fact_repository import get_key_fact_reposi
from ra_aid.database.repositories.key_snippet_repository import get_key_snippet_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
from ra_aid.database.repositories.research_note_repository import get_research_note_repository
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.work_log_repository import get_work_log_repository
from ra_aid.model_formatters import key_snippets_formatter
from ra_aid.logging_config import get_logger
@ -69,6 +70,22 @@ def emit_research_notes(notes: str) -> str:
from ra_aid.model_formatters.research_notes_formatter import format_research_note
formatted_note = format_research_note(note_id, notes)
# Record to trajectory before displaying panel
try:
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="emit_research_notes",
tool_parameters={"notes": notes},
step_data={
"note_id": note_id,
"display_title": "Research Notes",
},
record_type="memory_operation",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
# Display formatted note
console.print(Panel(Markdown(formatted_note), title="🔍 Research Notes"))
@ -123,6 +140,23 @@ def emit_key_facts(facts: List[str]) -> str:
console.print(f"Error storing fact: {str(e)}", style="red")
continue
# Record to trajectory before displaying panel
try:
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="emit_key_facts",
tool_parameters={"facts": [fact]},
step_data={
"fact_id": fact_id,
"fact": fact,
"display_title": f"Key Fact #{fact_id}",
},
record_type="memory_operation",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
# Display panel with ID
console.print(
Panel(
@ -214,6 +248,32 @@ def emit_key_snippet(snippet_info: SnippetInfo) -> str:
if snippet_info["description"]:
display_text.extend(["", "**Description**:", snippet_info["description"]])
# Record to trajectory before displaying panel
try:
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="emit_key_snippet",
tool_parameters={
"snippet_info": {
"filepath": snippet_info["filepath"],
"line_number": snippet_info["line_number"],
"description": snippet_info["description"],
# Omit the full snippet content to avoid duplicating large text in the database
"snippet_length": len(snippet_info["snippet"])
}
},
step_data={
"snippet_id": snippet_id,
"filepath": snippet_info["filepath"],
"line_number": snippet_info["line_number"],
"display_title": f"Key Snippet #{snippet_id}",
},
record_type="memory_operation",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
# Display panel
console.print(
Panel(
@ -248,6 +308,25 @@ def one_shot_completed(message: str) -> str:
message: Completion message to display
"""
mark_task_completed(message)
# Record to trajectory before displaying panel
human_input_id = None
try:
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="one_shot_completed",
tool_parameters={"message": message},
step_data={
"completion_message": message,
"display_title": "Task Completed",
},
record_type="task_completion",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
console.print(Panel(Markdown(message), title="✅ Task Completed"))
log_work_event(f"Task completed:\n\n{message}")
return "Completion noted."
@ -261,6 +340,25 @@ def task_completed(message: str) -> str:
message: Message explaining how/why the task is complete
"""
mark_task_completed(message)
# Record to trajectory before displaying panel
human_input_id = None
try:
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="task_completed",
tool_parameters={"message": message},
step_data={
"completion_message": message,
"display_title": "Task Completed",
},
record_type="task_completion",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
console.print(Panel(Markdown(message), title="✅ Task Completed"))
log_work_event(f"Task completed:\n\n{message}")
return "Completion noted."
@ -275,6 +373,25 @@ def plan_implementation_completed(message: str) -> str:
"""
mark_should_exit(propagation_depth=1)
mark_plan_completed(message)
# Record to trajectory before displaying panel
human_input_id = None
try:
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="plan_implementation_completed",
tool_parameters={"message": message},
step_data={
"completion_message": message,
"display_title": "Plan Executed",
},
record_type="plan_completion",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
console.print(Panel(Markdown(message), title="✅ Plan Executed"))
log_work_event(f"Completed implementation:\n\n{message}")
return "Plan completion noted."
@ -361,10 +478,29 @@ def emit_related_files(files: List[str]) -> str:
results.append(f"File ID #{file_id}: {file}")
# Rich output - single consolidated panel for added files
# Record to trajectory before displaying panel for added files
if added_files:
files_added_md = "\n".join(f"- `{file}`" for id, file in added_files)
md_content = f"**Files Noted:**\n{files_added_md}"
human_input_id = None
try:
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="emit_related_files",
tool_parameters={"files": files},
step_data={
"added_files": [file for _, file in added_files],
"added_file_ids": [file_id for file_id, _ in added_files],
"display_title": "Related Files Noted",
},
record_type="memory_operation",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
console.print(
Panel(
Markdown(md_content),
@ -373,10 +509,28 @@ def emit_related_files(files: List[str]) -> str:
)
)
# Display skipped binary files
# Record to trajectory before displaying panel for binary files
if binary_files:
binary_files_md = "\n".join(f"- `{file}`" for file in binary_files)
md_content = f"**Binary Files Skipped:**\n{binary_files_md}"
human_input_id = None
try:
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo = get_trajectory_repository()
trajectory_repo.create(
tool_name="emit_related_files",
tool_parameters={"files": files},
step_data={
"binary_files": binary_files,
"display_title": "Binary Files Not Added",
},
record_type="memory_operation",
human_input_id=human_input_id
)
except RuntimeError as e:
logger.warning(f"Failed to record trajectory: {str(e)}")
console.print(
Panel(
Markdown(md_content),

View File

@ -1,7 +1,7 @@
import logging
import os.path
import time
from typing import Dict
from typing import Dict, Optional
from langchain_core.tools import tool
from rich.console import Console
@ -16,6 +16,49 @@ console = Console()
CHUNK_SIZE = 8192
def record_trajectory(
tool_name: str,
tool_parameters: Dict,
step_data: Dict,
record_type: str = "tool_execution",
is_error: bool = False,
error_message: Optional[str] = None,
error_type: Optional[str] = None
) -> None:
"""
Helper function to record trajectory information, handling the case when repositories are not available.
Args:
tool_name: Name of the tool
tool_parameters: Parameters passed to the tool
step_data: UI rendering data
record_type: Type of trajectory record
is_error: Flag indicating if this record represents an error
error_message: The error message
error_type: The type/class of the error
"""
try:
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name=tool_name,
tool_parameters=tool_parameters,
step_data=step_data,
record_type=record_type,
human_input_id=human_input_id,
is_error=is_error,
error_message=error_message,
error_type=error_type
)
except (ImportError, RuntimeError):
# If either the repository modules can't be imported or no repository is available,
# just log and continue without recording trajectory
logging.debug("Skipping trajectory recording: repositories not available")
@tool
def read_file_tool(filepath: str, encoding: str = "utf-8") -> Dict[str, str]:
"""Read and return the contents of a text file.
@ -29,10 +72,43 @@ def read_file_tool(filepath: str, encoding: str = "utf-8") -> Dict[str, str]:
start_time = time.time()
try:
if not os.path.exists(filepath):
# Record error in trajectory
record_trajectory(
tool_name="read_file_tool",
tool_parameters={
"filepath": filepath,
"encoding": encoding
},
step_data={
"filepath": filepath,
"display_title": "File Not Found",
"error_message": f"File not found: {filepath}"
},
is_error=True,
error_message=f"File not found: {filepath}",
error_type="FileNotFoundError"
)
raise FileNotFoundError(f"File not found: {filepath}")
# Check if the file is binary
if is_binary_file(filepath):
# Record binary file error in trajectory
record_trajectory(
tool_name="read_file_tool",
tool_parameters={
"filepath": filepath,
"encoding": encoding
},
step_data={
"filepath": filepath,
"display_title": "Binary File Detected",
"error_message": f"Cannot read binary file: {filepath}"
},
is_error=True,
error_message="Cannot read binary file",
error_type="BinaryFileError"
)
console.print(
Panel(
f"Cannot read binary file: {filepath}",
@ -67,6 +143,22 @@ def read_file_tool(filepath: str, encoding: str = "utf-8") -> Dict[str, str]:
logging.debug(f"File read complete: {total_bytes} bytes in {elapsed:.2f}s")
logging.debug(f"Pre-truncation stats: {total_bytes} bytes, {line_count} lines")
# Record successful file read in trajectory
record_trajectory(
tool_name="read_file_tool",
tool_parameters={
"filepath": filepath,
"encoding": encoding
},
step_data={
"filepath": filepath,
"display_title": "File Read",
"line_count": line_count,
"total_bytes": total_bytes,
"elapsed_time": elapsed
}
)
console.print(
Panel(
f"Read {line_count} lines ({total_bytes} bytes) from {filepath} in {elapsed:.2f}s",
@ -80,6 +172,25 @@ def read_file_tool(filepath: str, encoding: str = "utf-8") -> Dict[str, str]:
return {"content": truncated}
except Exception:
except Exception as e:
elapsed = time.time() - start_time
# Record exception in trajectory (if it's not already a handled FileNotFoundError)
if not isinstance(e, FileNotFoundError):
record_trajectory(
tool_name="read_file_tool",
tool_parameters={
"filepath": filepath,
"encoding": encoding
},
step_data={
"filepath": filepath,
"display_title": "File Read Error",
"error_message": str(e)
},
is_error=True,
error_message=str(e),
error_type=type(e).__name__
)
raise

View File

@ -2,6 +2,9 @@ from langchain_core.tools import tool
from rich.console import Console
from rich.panel import Panel
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
console = Console()
@ -10,6 +13,24 @@ def existing_project_detected() -> dict:
"""
When to call: Once you have confirmed that the current working directory contains project files.
"""
try:
# Record detection in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="existing_project_detected",
tool_parameters={},
step_data={
"detection_type": "existing_project",
"display_title": "Existing Project Detected",
},
record_type="tool_execution",
human_input_id=human_input_id
)
except Exception as e:
# Continue even if trajectory recording fails
console.print(f"Warning: Could not record trajectory: {str(e)}")
console.print(Panel("📁 Existing Project Detected", style="bright_blue", padding=0))
return {
"hint": (
@ -30,6 +51,24 @@ def monorepo_detected() -> dict:
"""
When to call: After identifying that multiple packages or modules exist within a single repository.
"""
try:
# Record detection in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="monorepo_detected",
tool_parameters={},
step_data={
"detection_type": "monorepo",
"display_title": "Monorepo Detected",
},
record_type="tool_execution",
human_input_id=human_input_id
)
except Exception as e:
# Continue even if trajectory recording fails
console.print(f"Warning: Could not record trajectory: {str(e)}")
console.print(Panel("📦 Monorepo Detected", style="bright_blue", padding=0))
return {
"hint": (
@ -53,6 +92,24 @@ def ui_detected() -> dict:
"""
When to call: After detecting that the project contains a user interface layer or front-end component.
"""
try:
# Record detection in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="ui_detected",
tool_parameters={},
step_data={
"detection_type": "ui",
"display_title": "UI Detected",
},
record_type="tool_execution",
human_input_id=human_input_id
)
except Exception as e:
# Continue even if trajectory recording fails
console.print(f"Warning: Could not record trajectory: {str(e)}")
console.print(Panel("🎯 UI Detected", style="bright_blue", padding=0))
return {
"hint": (

View File

@ -5,6 +5,8 @@ from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.proc.interactive import run_interactive_command
from ra_aid.text.processing import truncate_output
@ -158,6 +160,30 @@ def ripgrep_search(
info_sections.append("\n".join(params))
# Execute command
# Record ripgrep search in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="ripgrep_search",
tool_parameters={
"pattern": pattern,
"before_context_lines": before_context_lines,
"after_context_lines": after_context_lines,
"file_type": file_type,
"case_sensitive": case_sensitive,
"include_hidden": include_hidden,
"follow_links": follow_links,
"exclude_dirs": exclude_dirs,
"fixed_string": fixed_string
},
step_data={
"search_pattern": pattern,
"display_title": "Ripgrep Search",
},
record_type="tool_execution",
human_input_id=human_input_id
)
console.print(
Panel(
Markdown(f"Searching for: **{pattern}**"),
@ -179,5 +205,34 @@ def ripgrep_search(
except Exception as e:
error_msg = str(e)
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="ripgrep_search",
tool_parameters={
"pattern": pattern,
"before_context_lines": before_context_lines,
"after_context_lines": after_context_lines,
"file_type": file_type,
"case_sensitive": case_sensitive,
"include_hidden": include_hidden,
"follow_links": follow_links,
"exclude_dirs": exclude_dirs,
"fixed_string": fixed_string
},
step_data={
"search_pattern": pattern,
"display_title": "Ripgrep Search Error",
"error_message": error_msg
},
record_type="tool_execution",
human_input_id=human_input_id,
is_error=True,
error_message=error_msg,
error_type=type(e).__name__
)
console.print(Panel(error_msg, title="❌ Error", border_style="red"))
return {"output": error_msg, "return_code": 1, "success": False}

View File

@ -10,6 +10,8 @@ from ra_aid.proc.interactive import run_interactive_command
from ra_aid.text.processing import truncate_output
from ra_aid.tools.memory import log_work_event
from ra_aid.database.repositories.config_repository import get_config_repository
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
console = Console()
@ -54,6 +56,20 @@ def run_shell_command(
console.print(" " + get_cowboy_message())
console.print("")
# Record tool execution in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="run_shell_command",
tool_parameters={"command": command, "timeout": timeout},
step_data={
"command": command,
"display_title": "Shell Command",
},
record_type="tool_execution",
human_input_id=human_input_id
)
# Show just the command in a simple panel
console.print(Panel(command, title="🐚 Shell", border_style="bright_yellow"))
@ -96,5 +112,23 @@ def run_shell_command(
return result
except Exception as e:
print()
# Record error in trajectory
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="run_shell_command",
tool_parameters={"command": command, "timeout": timeout},
step_data={
"command": command,
"error": str(e),
"display_title": "Shell Error",
},
record_type="tool_execution",
is_error=True,
error_message=str(e),
error_type=type(e).__name__,
human_input_id=human_input_id
)
console.print(Panel(str(e), title="❌ Error", border_style="red"))
return {"output": str(e), "return_code": 1, "success": False}

View File

@ -7,6 +7,9 @@ from rich.markdown import Markdown
from rich.panel import Panel
from tavily import TavilyClient
from ra_aid.database.repositories.trajectory_repository import get_trajectory_repository
from ra_aid.database.repositories.human_input_repository import get_human_input_repository
console = Console()
@ -21,9 +24,44 @@ def web_search_tavily(query: str) -> Dict:
Returns:
Dict containing search results from Tavily
"""
client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
# Record trajectory before displaying panel
trajectory_repo = get_trajectory_repository()
human_input_id = get_human_input_repository().get_most_recent_id()
trajectory_repo.create(
tool_name="web_search_tavily",
tool_parameters={"query": query},
step_data={
"query": query,
"display_title": "Web Search",
},
record_type="tool_execution",
human_input_id=human_input_id
)
# Display search query panel
console.print(
Panel(Markdown(query), title="🔍 Searching Tavily", border_style="bright_blue")
)
try:
client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
search_result = client.search(query=query)
return search_result
except Exception as e:
# Record error in trajectory
trajectory_repo.create(
tool_name="web_search_tavily",
tool_parameters={"query": query},
step_data={
"query": query,
"display_title": "Web Search Error",
"error": str(e)
},
record_type="tool_execution",
human_input_id=human_input_id,
is_error=True,
error_message=str(e),
error_type=type(e).__name__
)
# Re-raise the exception to maintain original behavior
raise