use latest aider; update tool calling prompts and minimize return values to improve tool calling performance
This commit is contained in:
parent
cc93961bf3
commit
52722f6600
|
|
@ -35,7 +35,7 @@ dependencies = [
|
||||||
"rapidfuzz>=3.11.0",
|
"rapidfuzz>=3.11.0",
|
||||||
"pathspec>=0.11.0",
|
"pathspec>=0.11.0",
|
||||||
"pyte>=0.8.2",
|
"pyte>=0.8.2",
|
||||||
"aider-chat>=0.74.2",
|
"aider-chat>=0.75",
|
||||||
"tavily-python>=0.5.0",
|
"tavily-python>=0.5.0",
|
||||||
"litellm>=1.60.6",
|
"litellm>=1.60.6",
|
||||||
"fastapi>=0.104.0",
|
"fastapi>=0.104.0",
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,9 @@ def build_agent_kwargs(
|
||||||
Returns:
|
Returns:
|
||||||
Dictionary of kwargs for agent creation
|
Dictionary of kwargs for agent creation
|
||||||
"""
|
"""
|
||||||
agent_kwargs = {}
|
agent_kwargs = {
|
||||||
|
"version": "v2",
|
||||||
|
}
|
||||||
|
|
||||||
if checkpointer is not None:
|
if checkpointer is not None:
|
||||||
agent_kwargs["checkpointer"] = checkpointer
|
agent_kwargs["checkpointer"] = checkpointer
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ Project State Handling:
|
||||||
Be aware there may be additional relevant files
|
Be aware there may be additional relevant files
|
||||||
Use tools like ripgrep_search and fuzzy_find_project_files to locate specific files
|
Use tools like ripgrep_search and fuzzy_find_project_files to locate specific files
|
||||||
|
|
||||||
Be very thorough in your research and emit lots of snippets, key facts. If you take more than a few steps, be eager to emit research subtasks.{research_only_note}
|
When necessary, emit research subtasks.{research_only_note}
|
||||||
|
|
||||||
Objective
|
Objective
|
||||||
Investigate and understand the codebase as it relates to the query.
|
Investigate and understand the codebase as it relates to the query.
|
||||||
|
|
@ -259,11 +259,12 @@ Thoroughness and Completeness:
|
||||||
- For tasks requiring UI changes, not researching existing UI libraries and conventions.
|
- For tasks requiring UI changes, not researching existing UI libraries and conventions.
|
||||||
- Not requesting enough research subtasks on changes on large projects, e.g. to discover testing or UI conventions, etc.
|
- Not requesting enough research subtasks on changes on large projects, e.g. to discover testing or UI conventions, etc.
|
||||||
- Doing one-shot tasks, which is good, but not compiling or testing your work when appropriate.
|
- Doing one-shot tasks, which is good, but not compiling or testing your work when appropriate.
|
||||||
- Not finding *examples* of how to do similar things in the current codebase and emitting them with emit_key_snippets.
|
- Not finding *examples* of how to do similar things in the current codebase and calling emit_key_snippet to report them.
|
||||||
- Not finding unit tests because they are in slightly different locations than expected.
|
- Not finding unit tests because they are in slightly different locations than expected.
|
||||||
- Not handling real-world projects that often have inconsistencies and require more thorough research and pragmatism.
|
- Not handling real-world projects that often have inconsistencies and require more thorough research and pragmatism.
|
||||||
- Not finding *ALL* related files and snippets. You'll often be on the right path and give up/start implementing too quickly.
|
- Not finding *ALL* related files and snippets. You'll often be on the right path and give up/start implementing too quickly.
|
||||||
- You sometimes use emit_key_snippets to *write* code rather than to record key snippets of existing code, which it is meant for.
|
- Not calling tools/functions properly, e.g. leaving off required arguments, calling a tool in a loop, calling tools inappropriately.
|
||||||
|
- Doing redundant research and taking way more steps than necessary.
|
||||||
|
|
||||||
If there are existing relevant unit tests/test suites, you must run them *during the research stage*, before editing anything, using run_shell_command to get a baseline about passing/failing tests and call emit_key_facts with key facts about the tests and whether they were passing when you started. This ensures a proper baseline is established before any changes.
|
If there are existing relevant unit tests/test suites, you must run them *during the research stage*, before editing anything, using run_shell_command to get a baseline about passing/failing tests and call emit_key_facts with key facts about the tests and whether they were passing when you started. This ensures a proper baseline is established before any changes.
|
||||||
|
|
||||||
|
|
@ -535,18 +536,6 @@ Work done so far:
|
||||||
{work_log}
|
{work_log}
|
||||||
</work log>
|
</work log>
|
||||||
|
|
||||||
Fact Management:
|
|
||||||
Each fact is identified with [Fact ID: X].
|
|
||||||
Facts may be deleted if they become outdated, irrelevant, or duplicates.
|
|
||||||
Use delete_key_facts([id1, id2, ...]) with a list of numeric Fact IDs to remove unnecessary facts.
|
|
||||||
|
|
||||||
Snippet Management:
|
|
||||||
Each snippet is identified with [Snippet ID: X].
|
|
||||||
Snippets include file path, line number, and source code.
|
|
||||||
Snippets may have optional descriptions explaining their significance.
|
|
||||||
Delete snippets with delete_key_snippets([id1, id2, ...]) to remove outdated or irrelevant ones.
|
|
||||||
Use emit_key_snippets to store important code sections needed for reference in batches.
|
|
||||||
|
|
||||||
Guidelines:
|
Guidelines:
|
||||||
|
|
||||||
If you need additional input or assistance from the expert (if expert is available), especially for debugging, deeper logic analysis, or correctness checks, use emit_expert_context to provide all relevant context and wait for the expert’s response.
|
If you need additional input or assistance from the expert (if expert is available), especially for debugging, deeper logic analysis, or correctness checks, use emit_expert_context to provide all relevant context and wait for the expert’s response.
|
||||||
|
|
@ -615,7 +604,6 @@ Working Directory: {working_directory}
|
||||||
Important Notes:
|
Important Notes:
|
||||||
- Focus solely on the given task and implement it as described.
|
- Focus solely on the given task and implement it as described.
|
||||||
- Scale the complexity of your solution to the complexity of the request. For simple requests, keep it straightforward and minimal. For complex requests, maintain the previously planned depth.
|
- Scale the complexity of your solution to the complexity of the request. For simple requests, keep it straightforward and minimal. For complex requests, maintain the previously planned depth.
|
||||||
- Use emit_key_snippets to manage code sections before and after modifications in batches.
|
|
||||||
|
|
||||||
- Work incrementally, validating as you go. If at any point the implementation logic is unclear or you need debugging assistance, consult the expert (if expert is available) for deeper analysis.
|
- Work incrementally, validating as you go. If at any point the implementation logic is unclear or you need debugging assistance, consult the expert (if expert is available) for deeper analysis.
|
||||||
- Do not add features not explicitly required.
|
- Do not add features not explicitly required.
|
||||||
|
|
@ -713,11 +701,6 @@ Exit Criteria:
|
||||||
- Until such confirmation, continue to engage and ask_human if additional clarification is required.
|
- Until such confirmation, continue to engage and ask_human if additional clarification is required.
|
||||||
- If there are any doubts about final correctness or thoroughness, consult the expert (if expert is available) before concluding.
|
- If there are any doubts about final correctness or thoroughness, consult the expert (if expert is available) before concluding.
|
||||||
|
|
||||||
Context Cleanup:
|
|
||||||
- Use delete_key_facts to remove any key facts that no longer apply.
|
|
||||||
- Use delete_key_snippets to remove any key snippets that no longer apply.
|
|
||||||
- Use deregister_related_files to remove any related files that no longer apply.
|
|
||||||
|
|
||||||
When processing request_* tool responses:
|
When processing request_* tool responses:
|
||||||
- Always check completion_message and work_log for implementation status
|
- Always check completion_message and work_log for implementation status
|
||||||
- If the work_log includes 'Implementation completed' or 'Plan execution completed', the changes have already been made
|
- If the work_log includes 'Implementation completed' or 'Plan execution completed', the changes have already been made
|
||||||
|
|
@ -942,11 +925,6 @@ Exit Criteria:
|
||||||
- Until such confirmation, continue to engage and ask_human if additional clarification is required.
|
- Until such confirmation, continue to engage and ask_human if additional clarification is required.
|
||||||
- If there are any doubts about final correctness or thoroughness, consult the expert (if expert is available) before concluding.
|
- If there are any doubts about final correctness or thoroughness, consult the expert (if expert is available) before concluding.
|
||||||
|
|
||||||
Context Cleanup:
|
|
||||||
- Use delete_key_facts to remove any key facts that no longer apply.
|
|
||||||
- Use delete_key_snippets to remove any key snippets that no longer apply.
|
|
||||||
- Use deregister_related_files to remove any related files that no longer apply.
|
|
||||||
|
|
||||||
When processing request_* tool responses:
|
When processing request_* tool responses:
|
||||||
- Always check completion_message and work_log for implementation status
|
- Always check completion_message and work_log for implementation status
|
||||||
- If the work_log includes 'Implementation completed' or 'Plan execution completed', the changes have already been made
|
- If the work_log includes 'Implementation completed' or 'Plan execution completed', the changes have already been made
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from ra_aid.tools import (
|
||||||
ask_human,
|
ask_human,
|
||||||
emit_expert_context,
|
emit_expert_context,
|
||||||
emit_key_facts,
|
emit_key_facts,
|
||||||
emit_key_snippets,
|
emit_key_snippet,
|
||||||
emit_related_files,
|
emit_related_files,
|
||||||
emit_research_notes,
|
emit_research_notes,
|
||||||
fuzzy_find_project_files,
|
fuzzy_find_project_files,
|
||||||
|
|
@ -41,9 +41,9 @@ def get_read_only_tools(
|
||||||
List of tool functions
|
List of tool functions
|
||||||
"""
|
"""
|
||||||
tools = [
|
tools = [
|
||||||
|
emit_key_snippet,
|
||||||
emit_related_files,
|
emit_related_files,
|
||||||
emit_key_facts,
|
emit_key_facts,
|
||||||
emit_key_snippets,
|
|
||||||
# *TEMPORARILY* disabled to improve tool calling perf.
|
# *TEMPORARILY* disabled to improve tool calling perf.
|
||||||
# delete_key_facts,
|
# delete_key_facts,
|
||||||
# delete_key_snippets,
|
# delete_key_snippets,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from .memory import (
|
||||||
delete_tasks,
|
delete_tasks,
|
||||||
deregister_related_files,
|
deregister_related_files,
|
||||||
emit_key_facts,
|
emit_key_facts,
|
||||||
emit_key_snippets,
|
emit_key_snippet,
|
||||||
emit_plan,
|
emit_plan,
|
||||||
emit_related_files,
|
emit_related_files,
|
||||||
emit_research_notes,
|
emit_research_notes,
|
||||||
|
|
@ -36,7 +36,7 @@ __all__ = [
|
||||||
"deregister_related_files",
|
"deregister_related_files",
|
||||||
"emit_expert_context",
|
"emit_expert_context",
|
||||||
"emit_key_facts",
|
"emit_key_facts",
|
||||||
"emit_key_snippets",
|
"emit_key_snippet",
|
||||||
"emit_plan",
|
"emit_plan",
|
||||||
"emit_related_files",
|
"emit_related_files",
|
||||||
"emit_research_notes",
|
"emit_research_notes",
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ class WorkLogEntry(TypedDict):
|
||||||
|
|
||||||
|
|
||||||
class SnippetInfo(TypedDict):
|
class SnippetInfo(TypedDict):
|
||||||
"""Type definition for source code snippet information"""
|
|
||||||
|
|
||||||
filepath: str
|
filepath: str
|
||||||
line_number: int
|
line_number: int
|
||||||
snippet: str
|
snippet: str
|
||||||
|
|
@ -30,12 +28,13 @@ _global_memory: Dict[
|
||||||
Union[
|
Union[
|
||||||
Dict[int, str],
|
Dict[int, str],
|
||||||
Dict[int, SnippetInfo],
|
Dict[int, SnippetInfo],
|
||||||
|
Dict[int, WorkLogEntry],
|
||||||
int,
|
int,
|
||||||
Set[str],
|
Set[str],
|
||||||
bool,
|
bool,
|
||||||
str,
|
str,
|
||||||
List[str],
|
List[str],
|
||||||
List[WorkLogEntry],
|
List[WorkLogEntry]
|
||||||
],
|
],
|
||||||
] = {
|
] = {
|
||||||
"research_notes": [],
|
"research_notes": [],
|
||||||
|
|
@ -59,17 +58,14 @@ _global_memory: Dict[
|
||||||
|
|
||||||
@tool("emit_research_notes")
|
@tool("emit_research_notes")
|
||||||
def emit_research_notes(notes: str) -> str:
|
def emit_research_notes(notes: str) -> str:
|
||||||
"""Store research notes in global memory.
|
"""Use this when you have completed your research to share your notes in markdown format, no more than 500 words.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
notes: REQUIRED The research notes to store
|
notes: REQUIRED The research notes to store
|
||||||
|
|
||||||
Returns:
|
|
||||||
The stored notes
|
|
||||||
"""
|
"""
|
||||||
_global_memory["research_notes"].append(notes)
|
_global_memory["research_notes"].append(notes)
|
||||||
console.print(Panel(Markdown(notes), title="🔍 Research Notes"))
|
console.print(Panel(Markdown(notes), title="🔍 Research Notes"))
|
||||||
return notes
|
return "Research notes stored."
|
||||||
|
|
||||||
|
|
||||||
@tool("emit_plan")
|
@tool("emit_plan")
|
||||||
|
|
@ -78,14 +74,11 @@ def emit_plan(plan: str) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
plan: The plan step to store (markdown format; be clear, complete, use newlines, and use as many tokens as you need)
|
plan: The plan step to store (markdown format; be clear, complete, use newlines, and use as many tokens as you need)
|
||||||
|
|
||||||
Returns:
|
|
||||||
The stored plan
|
|
||||||
"""
|
"""
|
||||||
_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:\n\n{plan}")
|
log_work_event(f"Added plan step:\n\n{plan}")
|
||||||
return plan
|
return "Plan stored."
|
||||||
|
|
||||||
|
|
||||||
@tool("emit_task")
|
@tool("emit_task")
|
||||||
|
|
@ -94,9 +87,6 @@ def emit_task(task: str) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task: The task to store
|
task: The task to store
|
||||||
|
|
||||||
Returns:
|
|
||||||
String confirming task storage with ID number
|
|
||||||
"""
|
"""
|
||||||
# Get and increment task ID
|
# Get and increment task ID
|
||||||
task_id = _global_memory["task_id_counter"]
|
task_id = _global_memory["task_id_counter"]
|
||||||
|
|
@ -116,9 +106,6 @@ def emit_key_facts(facts: List[str]) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
facts: List of key facts to store
|
facts: List of key facts to store
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of stored fact confirmation messages
|
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
for fact in facts:
|
for fact in facts:
|
||||||
|
|
@ -152,9 +139,6 @@ def delete_key_facts(fact_ids: List[int]) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
fact_ids: List of fact IDs to delete
|
fact_ids: List of fact IDs to delete
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of success messages for deleted facts
|
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
for fact_id in fact_ids:
|
for fact_id in fact_ids:
|
||||||
|
|
@ -178,9 +162,6 @@ def delete_tasks(task_ids: List[int]) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_ids: List of task IDs to delete
|
task_ids: List of task IDs to delete
|
||||||
|
|
||||||
Returns:
|
|
||||||
Confirmation message
|
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
for task_id in task_ids:
|
for task_id in task_ids:
|
||||||
|
|
@ -206,74 +187,62 @@ def request_implementation() -> str:
|
||||||
Do you need to request research subtasks first?
|
Do you need to request research subtasks first?
|
||||||
Have you run relevant unit tests, if they exist, to get a baseline (this can be a subtask)?
|
Have you run relevant unit tests, if they exist, to get a baseline (this can be a subtask)?
|
||||||
Do you need to crawl deeper to find all related files and symbols?
|
Do you need to crawl deeper to find all related files and symbols?
|
||||||
|
|
||||||
Returns:
|
|
||||||
Empty string
|
|
||||||
"""
|
"""
|
||||||
_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.")
|
log_work_event("Implementation requested.")
|
||||||
return ""
|
return "Implementation requested."
|
||||||
|
|
||||||
|
|
||||||
@tool("emit_key_snippets")
|
@tool("emit_key_snippet")
|
||||||
def emit_key_snippets(snippets: List[SnippetInfo]) -> str:
|
def emit_key_snippet(snippet_info: SnippetInfo) -> str:
|
||||||
"""Store multiple key source code snippets in global memory.
|
"""Store a single source code snippet in global memory which represents key information.
|
||||||
Automatically adds the filepaths of the snippets to related files.
|
Automatically adds the filepath of the snippet to related files.
|
||||||
|
|
||||||
This is for **existing**, or **just-written** files, not for things to be created in the future.
|
This is for **existing**, or **just-written** files, not for things to be created in the future.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
snippets: REQUIRED List of snippet information dictionaries containing:
|
snippet_info: Dict with keys:
|
||||||
- filepath: Path to the source file
|
- filepath: Path to the source file
|
||||||
- line_number: Line number where the snippet starts
|
- line_number: Line number where the snippet starts
|
||||||
- snippet: The source code snippet text
|
- snippet: The source code snippet text
|
||||||
- description: Optional description of the significance
|
- description: Optional description of the significance
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of stored snippet confirmation messages
|
|
||||||
"""
|
"""
|
||||||
# First collect unique filepaths to add as related files
|
# Add filepath to related files
|
||||||
emit_related_files.invoke(
|
emit_related_files.invoke({"files": [snippet_info["filepath"]]})
|
||||||
{"files": [snippet_info["filepath"] for snippet_info in snippets]}
|
|
||||||
|
# Get and increment snippet ID
|
||||||
|
snippet_id = _global_memory["key_snippet_id_counter"]
|
||||||
|
_global_memory["key_snippet_id_counter"] += 1
|
||||||
|
|
||||||
|
# Store snippet info
|
||||||
|
_global_memory["key_snippets"][snippet_id] = snippet_info
|
||||||
|
|
||||||
|
# Format display text as markdown
|
||||||
|
display_text = [
|
||||||
|
"**Source Location**:",
|
||||||
|
f"- File: `{snippet_info['filepath']}`",
|
||||||
|
f"- Line: `{snippet_info['line_number']}`",
|
||||||
|
"", # Empty line before code block
|
||||||
|
"**Code**:",
|
||||||
|
"```python",
|
||||||
|
snippet_info["snippet"].rstrip(), # Remove trailing whitespace
|
||||||
|
"```",
|
||||||
|
]
|
||||||
|
if snippet_info["description"]:
|
||||||
|
display_text.extend(["", "**Description**:", snippet_info["description"]])
|
||||||
|
|
||||||
|
# Display panel
|
||||||
|
console.print(
|
||||||
|
Panel(
|
||||||
|
Markdown("\n".join(display_text)),
|
||||||
|
title=f"📝 Key Snippet #{snippet_id}",
|
||||||
|
border_style="bright_cyan",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
results = []
|
log_work_event(f"Stored code snippet #{snippet_id}.")
|
||||||
for snippet_info in snippets:
|
return f"Snippet #{snippet_id} stored."
|
||||||
# Get and increment snippet ID
|
|
||||||
snippet_id = _global_memory["key_snippet_id_counter"]
|
|
||||||
_global_memory["key_snippet_id_counter"] += 1
|
|
||||||
|
|
||||||
# Store snippet info
|
|
||||||
_global_memory["key_snippets"][snippet_id] = snippet_info
|
|
||||||
|
|
||||||
# Format display text as markdown
|
|
||||||
display_text = [
|
|
||||||
"**Source Location**:",
|
|
||||||
f"- File: `{snippet_info['filepath']}`",
|
|
||||||
f"- Line: `{snippet_info['line_number']}`",
|
|
||||||
"", # Empty line before code block
|
|
||||||
"**Code**:",
|
|
||||||
"```python",
|
|
||||||
snippet_info["snippet"].rstrip(), # Remove trailing whitespace
|
|
||||||
"```",
|
|
||||||
]
|
|
||||||
if snippet_info["description"]:
|
|
||||||
display_text.extend(["", "**Description**:", snippet_info["description"]])
|
|
||||||
|
|
||||||
# Display panel
|
|
||||||
console.print(
|
|
||||||
Panel(
|
|
||||||
Markdown("\n".join(display_text)),
|
|
||||||
title=f"📝 Key Snippet #{snippet_id}",
|
|
||||||
border_style="bright_cyan",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
results.append(f"Stored snippet #{snippet_id}")
|
|
||||||
|
|
||||||
log_work_event(f"Stored {len(snippets)} code snippets.")
|
|
||||||
return "Snippets stored."
|
|
||||||
|
|
||||||
|
|
||||||
@tool("delete_key_snippets")
|
@tool("delete_key_snippets")
|
||||||
|
|
@ -283,9 +252,6 @@ def delete_key_snippets(snippet_ids: List[int]) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
snippet_ids: List of snippet IDs to delete
|
snippet_ids: List of snippet IDs to delete
|
||||||
|
|
||||||
Returns:
|
|
||||||
List of success messages for deleted snippets
|
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
for snippet_id in snippet_ids:
|
for snippet_id in snippet_ids:
|
||||||
|
|
@ -311,9 +277,6 @@ def swap_task_order(id1: int, id2: int) -> str:
|
||||||
Args:
|
Args:
|
||||||
id1: First task ID
|
id1: First task ID
|
||||||
id2: Second task ID
|
id2: Second task ID
|
||||||
|
|
||||||
Returns:
|
|
||||||
Success or error message depending on outcome
|
|
||||||
"""
|
"""
|
||||||
# Validate IDs are different
|
# Validate IDs are different
|
||||||
if id1 == id2:
|
if id1 == id2:
|
||||||
|
|
@ -338,7 +301,7 @@ def swap_task_order(id1: int, id2: int) -> str:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return "Tasks swapped."
|
return "Tasks deleted."
|
||||||
|
|
||||||
|
|
||||||
@tool("one_shot_completed")
|
@tool("one_shot_completed")
|
||||||
|
|
@ -366,9 +329,6 @@ def task_completed(message: str) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: Message explaining how/why the task is complete
|
message: Message explaining how/why the task is complete
|
||||||
|
|
||||||
Returns:
|
|
||||||
The completion message
|
|
||||||
"""
|
"""
|
||||||
_global_memory["task_completed"] = True
|
_global_memory["task_completed"] = True
|
||||||
_global_memory["completion_message"] = message
|
_global_memory["completion_message"] = message
|
||||||
|
|
@ -382,9 +342,6 @@ def plan_implementation_completed(message: str) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
message: Message explaining how the implementation plan was completed
|
message: Message explaining how the implementation plan was completed
|
||||||
|
|
||||||
Returns:
|
|
||||||
Confirmation message
|
|
||||||
"""
|
"""
|
||||||
_global_memory["task_completed"] = True
|
_global_memory["task_completed"] = True
|
||||||
_global_memory["completion_message"] = message
|
_global_memory["completion_message"] = message
|
||||||
|
|
@ -413,9 +370,6 @@ def emit_related_files(files: List[str]) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
files: List of file paths to add
|
files: List of file paths to add
|
||||||
|
|
||||||
Returns:
|
|
||||||
Formatted string containing file IDs and paths for all processed files
|
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
added_files = []
|
added_files = []
|
||||||
|
|
@ -476,7 +430,7 @@ def emit_related_files(files: List[str]) -> str:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return "\n".join(results)
|
return "Files noted."
|
||||||
|
|
||||||
|
|
||||||
def log_work_event(event: str) -> str:
|
def log_work_event(event: str) -> str:
|
||||||
|
|
@ -550,9 +504,6 @@ def deregister_related_files(file_ids: List[int]) -> str:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
file_ids: List of file IDs to delete
|
file_ids: List of file IDs to delete
|
||||||
|
|
||||||
Returns:
|
|
||||||
Success message string
|
|
||||||
"""
|
"""
|
||||||
results = []
|
results = []
|
||||||
for file_id in file_ids:
|
for file_id in file_ids:
|
||||||
|
|
@ -571,7 +522,7 @@ def deregister_related_files(file_ids: List[int]) -> str:
|
||||||
)
|
)
|
||||||
results.append(success_msg)
|
results.append(success_msg)
|
||||||
|
|
||||||
return "File references removed."
|
return "Files noted."
|
||||||
|
|
||||||
|
|
||||||
def get_memory_value(key: str) -> str:
|
def get_memory_value(key: str) -> str:
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,6 @@ def run_programming_task(
|
||||||
Args:
|
Args:
|
||||||
instructions: REQUIRED Programming task instructions (markdown format, use newlines and as many tokens as needed, no commands allowed)
|
instructions: REQUIRED Programming task instructions (markdown format, use newlines and as many tokens as needed, no commands allowed)
|
||||||
files: Optional; if not provided, uses related_files
|
files: Optional; if not provided, uses related_files
|
||||||
|
|
||||||
Returns: { "output": stdout+stderr, "return_code": 0 if success, "success": True/False }
|
|
||||||
"""
|
"""
|
||||||
# Build command
|
# Build command
|
||||||
aider_exe = get_aider_executable()
|
aider_exe = get_aider_executable()
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ def test_create_agent_anthropic(mock_model, mock_memory):
|
||||||
|
|
||||||
assert agent == "react_agent"
|
assert agent == "react_agent"
|
||||||
mock_react.assert_called_once_with(
|
mock_react.assert_called_once_with(
|
||||||
mock_model, [], state_modifier=mock_react.call_args[1]["state_modifier"]
|
mock_model, [], version='v2', state_modifier=mock_react.call_args[1]["state_modifier"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -258,7 +258,7 @@ def test_create_agent_anthropic_token_limiting_disabled(mock_model, mock_memory)
|
||||||
agent = create_agent(mock_model, [])
|
agent = create_agent(mock_model, [])
|
||||||
|
|
||||||
assert agent == "react_agent"
|
assert agent == "react_agent"
|
||||||
mock_react.assert_called_once_with(mock_model, [])
|
mock_react.assert_called_once_with(mock_model, [], version='v2')
|
||||||
|
|
||||||
|
|
||||||
def test_get_model_token_limit_research(mock_memory):
|
def test_get_model_token_limit_research(mock_memory):
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from ra_aid.tools.memory import (
|
||||||
delete_tasks,
|
delete_tasks,
|
||||||
deregister_related_files,
|
deregister_related_files,
|
||||||
emit_key_facts,
|
emit_key_facts,
|
||||||
emit_key_snippets,
|
emit_key_snippet,
|
||||||
emit_related_files,
|
emit_related_files,
|
||||||
emit_task,
|
emit_task,
|
||||||
get_memory_value,
|
get_memory_value,
|
||||||
|
|
@ -201,34 +201,45 @@ def test_delete_key_facts(reset_memory):
|
||||||
assert _global_memory["key_facts"][2] == "Third fact"
|
assert _global_memory["key_facts"][2] == "Third fact"
|
||||||
|
|
||||||
|
|
||||||
def test_emit_key_snippets(reset_memory):
|
def test_emit_key_snippet(reset_memory):
|
||||||
"""Test emitting multiple code snippets at once"""
|
"""Test emitting a single code snippet"""
|
||||||
# Test snippets with and without descriptions
|
# Test snippet with description
|
||||||
snippets = [
|
snippet = {
|
||||||
{
|
"filepath": "test.py",
|
||||||
"filepath": "test.py",
|
"line_number": 10,
|
||||||
"line_number": 10,
|
"snippet": "def test():\n pass",
|
||||||
"snippet": "def test():\n pass",
|
"description": "Test function",
|
||||||
"description": "Test function",
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"filepath": "main.py",
|
|
||||||
"line_number": 20,
|
|
||||||
"snippet": "print('hello')",
|
|
||||||
"description": None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
# Emit snippets
|
# Emit snippet
|
||||||
result = emit_key_snippets.invoke({"snippets": snippets})
|
result = emit_key_snippet.invoke({"snippet_info": snippet})
|
||||||
|
|
||||||
# Verify return message
|
# Verify return message
|
||||||
assert result == "Snippets stored."
|
assert result == "Snippet #0 stored."
|
||||||
|
|
||||||
# Verify snippets stored correctly
|
# Verify snippet stored correctly
|
||||||
assert _global_memory["key_snippets"][0] == snippets[0]
|
assert _global_memory["key_snippets"][0] == snippet
|
||||||
assert _global_memory["key_snippets"][1] == snippets[1]
|
|
||||||
|
|
||||||
|
# Verify counter incremented correctly
|
||||||
|
assert _global_memory["key_snippet_id_counter"] == 1
|
||||||
|
|
||||||
|
# Test snippet without description
|
||||||
|
snippet2 = {
|
||||||
|
"filepath": "main.py",
|
||||||
|
"line_number": 20,
|
||||||
|
"snippet": "print('hello')",
|
||||||
|
"description": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Emit second snippet
|
||||||
|
result = emit_key_snippet.invoke({"snippet_info": snippet2})
|
||||||
|
|
||||||
|
# Verify return message
|
||||||
|
assert result == "Snippet #1 stored."
|
||||||
|
|
||||||
|
# Verify snippet stored correctly
|
||||||
|
assert _global_memory["key_snippets"][1] == snippet2
|
||||||
|
|
||||||
# Verify counter incremented correctly
|
# Verify counter incremented correctly
|
||||||
assert _global_memory["key_snippet_id_counter"] == 2
|
assert _global_memory["key_snippet_id_counter"] == 2
|
||||||
|
|
||||||
|
|
@ -256,7 +267,9 @@ def test_delete_key_snippets(reset_memory):
|
||||||
"description": None,
|
"description": None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
emit_key_snippets.invoke({"snippets": snippets})
|
# Add snippets one by one
|
||||||
|
for snippet in snippets:
|
||||||
|
emit_key_snippet.invoke({"snippet_info": snippet})
|
||||||
|
|
||||||
# Test deleting mix of valid and invalid IDs
|
# Test deleting mix of valid and invalid IDs
|
||||||
result = delete_key_snippets.invoke({"snippet_ids": [0, 1, 999]})
|
result = delete_key_snippets.invoke({"snippet_ids": [0, 1, 999]})
|
||||||
|
|
@ -280,7 +293,7 @@ def test_delete_key_snippets_empty(reset_memory):
|
||||||
"snippet": "code",
|
"snippet": "code",
|
||||||
"description": None,
|
"description": None,
|
||||||
}
|
}
|
||||||
emit_key_snippets.invoke({"snippets": [snippet]})
|
emit_key_snippet.invoke({"snippet_info": snippet})
|
||||||
|
|
||||||
# Test with empty list
|
# Test with empty list
|
||||||
result = delete_key_snippets.invoke({"snippet_ids": []})
|
result = delete_key_snippets.invoke({"snippet_ids": []})
|
||||||
|
|
@ -302,12 +315,12 @@ def test_emit_related_files_basic(reset_memory, tmp_path):
|
||||||
|
|
||||||
# Test adding single file
|
# Test adding single file
|
||||||
result = emit_related_files.invoke({"files": [str(test_file)]})
|
result = emit_related_files.invoke({"files": [str(test_file)]})
|
||||||
assert result == f"File ID #0: {test_file}"
|
assert result == "Files noted."
|
||||||
assert _global_memory["related_files"][0] == str(test_file)
|
assert _global_memory["related_files"][0] == str(test_file)
|
||||||
|
|
||||||
# Test adding multiple files
|
# Test adding multiple files
|
||||||
result = emit_related_files.invoke({"files": [str(main_file), str(utils_file)]})
|
result = emit_related_files.invoke({"files": [str(main_file), str(utils_file)]})
|
||||||
assert result == f"File ID #1: {main_file}\nFile ID #2: {utils_file}"
|
assert result == "Files noted."
|
||||||
# Verify both files exist in related_files
|
# Verify both files exist in related_files
|
||||||
values = list(_global_memory["related_files"].values())
|
values = list(_global_memory["related_files"].values())
|
||||||
assert str(main_file) in values
|
assert str(main_file) in values
|
||||||
|
|
@ -331,17 +344,17 @@ def test_emit_related_files_duplicates(reset_memory, tmp_path):
|
||||||
|
|
||||||
# Add initial files
|
# Add initial files
|
||||||
result = emit_related_files.invoke({"files": [str(test_file), str(main_file)]})
|
result = emit_related_files.invoke({"files": [str(test_file), str(main_file)]})
|
||||||
assert result == f"File ID #0: {test_file}\nFile ID #1: {main_file}"
|
assert result == "Files noted."
|
||||||
_first_id = 0 # ID of test.py
|
_first_id = 0 # ID of test.py
|
||||||
|
|
||||||
# Try adding duplicates
|
# Try adding duplicates
|
||||||
result = emit_related_files.invoke({"files": [str(test_file)]})
|
result = emit_related_files.invoke({"files": [str(test_file)]})
|
||||||
assert result == f"File ID #0: {test_file}" # Should return same ID
|
assert result == "Files noted."
|
||||||
assert len(_global_memory["related_files"]) == 2 # Count should not increase
|
assert len(_global_memory["related_files"]) == 2 # Count should not increase
|
||||||
|
|
||||||
# Try mix of new and duplicate files
|
# Try mix of new and duplicate files
|
||||||
result = emit_related_files.invoke({"files": [str(test_file), str(new_file)]})
|
result = emit_related_files.invoke({"files": [str(test_file), str(new_file)]})
|
||||||
assert result == f"File ID #0: {test_file}\nFile ID #2: {new_file}"
|
assert result == "Files noted."
|
||||||
assert len(_global_memory["related_files"]) == 3
|
assert len(_global_memory["related_files"]) == 3
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -355,12 +368,12 @@ def test_related_files_id_tracking(reset_memory, tmp_path):
|
||||||
|
|
||||||
# Add first file
|
# Add first file
|
||||||
result = emit_related_files.invoke({"files": [str(file1)]})
|
result = emit_related_files.invoke({"files": [str(file1)]})
|
||||||
assert result == f"File ID #0: {file1}"
|
assert result == "Files noted."
|
||||||
assert _global_memory["related_file_id_counter"] == 1
|
assert _global_memory["related_file_id_counter"] == 1
|
||||||
|
|
||||||
# Add second file
|
# Add second file
|
||||||
result = emit_related_files.invoke({"files": [str(file2)]})
|
result = emit_related_files.invoke({"files": [str(file2)]})
|
||||||
assert result == f"File ID #1: {file2}"
|
assert result == "Files noted."
|
||||||
assert _global_memory["related_file_id_counter"] == 2
|
assert _global_memory["related_file_id_counter"] == 2
|
||||||
|
|
||||||
# Verify all files stored correctly
|
# Verify all files stored correctly
|
||||||
|
|
@ -383,13 +396,13 @@ def test_deregister_related_files(reset_memory, tmp_path):
|
||||||
|
|
||||||
# Delete middle file
|
# Delete middle file
|
||||||
result = deregister_related_files.invoke({"file_ids": [1]})
|
result = deregister_related_files.invoke({"file_ids": [1]})
|
||||||
assert result == "File references removed."
|
assert result == "Files noted."
|
||||||
assert 1 not in _global_memory["related_files"]
|
assert 1 not in _global_memory["related_files"]
|
||||||
assert len(_global_memory["related_files"]) == 2
|
assert len(_global_memory["related_files"]) == 2
|
||||||
|
|
||||||
# Delete multiple files including non-existent ID
|
# Delete multiple files including non-existent ID
|
||||||
result = deregister_related_files.invoke({"file_ids": [0, 2, 999]})
|
result = deregister_related_files.invoke({"file_ids": [0, 2, 999]})
|
||||||
assert result == "File references removed."
|
assert result == "Files noted."
|
||||||
assert len(_global_memory["related_files"]) == 0
|
assert len(_global_memory["related_files"]) == 0
|
||||||
|
|
||||||
# Counter should remain unchanged after deletions
|
# Counter should remain unchanged after deletions
|
||||||
|
|
@ -404,11 +417,11 @@ def test_related_files_duplicates(reset_memory, tmp_path):
|
||||||
|
|
||||||
# Add initial file
|
# Add initial file
|
||||||
result1 = emit_related_files.invoke({"files": [str(test_file)]})
|
result1 = emit_related_files.invoke({"files": [str(test_file)]})
|
||||||
assert result1 == f"File ID #0: {test_file}"
|
assert result1 == "Files noted."
|
||||||
|
|
||||||
# Add same file again
|
# Add same file again
|
||||||
result2 = emit_related_files.invoke({"files": [str(test_file)]})
|
result2 = emit_related_files.invoke({"files": [str(test_file)]})
|
||||||
assert result2 == f"File ID #0: {test_file}"
|
assert result2 == "Files noted."
|
||||||
|
|
||||||
# Verify only one entry exists
|
# Verify only one entry exists
|
||||||
assert len(_global_memory["related_files"]) == 1
|
assert len(_global_memory["related_files"]) == 1
|
||||||
|
|
@ -429,9 +442,8 @@ def test_emit_related_files_with_directory(reset_memory, tmp_path):
|
||||||
{"files": [str(test_dir), str(nonexistent), str(test_file)]}
|
{"files": [str(test_dir), str(nonexistent), str(test_file)]}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Verify specific error messages for directory and nonexistent path
|
# Verify result is the standard message
|
||||||
assert f"Error: Path '{test_dir}' is a directory, not a file" in result
|
assert result == "Files noted."
|
||||||
assert f"Error: Path '{nonexistent}' does not exist" in result
|
|
||||||
|
|
||||||
# Verify directory and nonexistent not added but valid file was
|
# Verify directory and nonexistent not added but valid file was
|
||||||
assert len(_global_memory["related_files"]) == 1
|
assert len(_global_memory["related_files"]) == 1
|
||||||
|
|
@ -439,7 +451,6 @@ def test_emit_related_files_with_directory(reset_memory, tmp_path):
|
||||||
assert str(test_file) in values
|
assert str(test_file) in values
|
||||||
assert str(test_dir) not in values
|
assert str(test_dir) not in values
|
||||||
assert str(nonexistent) not in values
|
assert str(nonexistent) not in values
|
||||||
assert str(nonexistent) not in values
|
|
||||||
|
|
||||||
|
|
||||||
def test_related_files_formatting(reset_memory, tmp_path):
|
def test_related_files_formatting(reset_memory, tmp_path):
|
||||||
|
|
@ -479,11 +490,11 @@ def test_emit_related_files_path_normalization(reset_memory, tmp_path):
|
||||||
try:
|
try:
|
||||||
# Add file with absolute path
|
# Add file with absolute path
|
||||||
result1 = emit_related_files.invoke({"files": ["file.txt"]})
|
result1 = emit_related_files.invoke({"files": ["file.txt"]})
|
||||||
assert "File ID #0:" in result1
|
assert result1 == "Files noted."
|
||||||
|
|
||||||
# Add same file with relative path - should get same ID due to path normalization
|
# Add same file with relative path - should get same ID due to path normalization
|
||||||
result2 = emit_related_files.invoke({"files": ["./file.txt"]})
|
result2 = emit_related_files.invoke({"files": ["./file.txt"]})
|
||||||
assert "File ID #0:" in result2 # Should reuse ID since it's the same file
|
assert result2 == "Files noted."
|
||||||
|
|
||||||
# Verify only one normalized path entry exists
|
# Verify only one normalized path entry exists
|
||||||
assert len(_global_memory["related_files"]) == 1
|
assert len(_global_memory["related_files"]) == 1
|
||||||
|
|
@ -525,9 +536,10 @@ def test_key_snippets_integration(reset_memory, tmp_path):
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add all snippets
|
# Add all snippets one by one
|
||||||
result = emit_key_snippets.invoke({"snippets": snippets})
|
for i, snippet in enumerate(snippets):
|
||||||
assert result == "Snippets stored."
|
result = emit_key_snippet.invoke({"snippet_info": snippet})
|
||||||
|
assert result == f"Snippet #{i} stored."
|
||||||
assert _global_memory["key_snippet_id_counter"] == 3
|
assert _global_memory["key_snippet_id_counter"] == 3
|
||||||
# Verify related files were tracked with IDs
|
# Verify related files were tracked with IDs
|
||||||
assert len(_global_memory["related_files"]) == 3
|
assert len(_global_memory["related_files"]) == 3
|
||||||
|
|
@ -564,8 +576,8 @@ def test_key_snippets_integration(reset_memory, tmp_path):
|
||||||
"snippet": "def func4():\n return False",
|
"snippet": "def func4():\n return False",
|
||||||
"description": "Fourth function",
|
"description": "Fourth function",
|
||||||
}
|
}
|
||||||
result = emit_key_snippets.invoke({"snippets": [new_snippet]})
|
result = emit_key_snippet.invoke({"snippet_info": new_snippet})
|
||||||
assert result == "Snippets stored."
|
assert result == "Snippet #3 stored."
|
||||||
assert _global_memory["key_snippet_id_counter"] == 4
|
assert _global_memory["key_snippet_id_counter"] == 4
|
||||||
# Verify new file was added to related files
|
# Verify new file was added to related files
|
||||||
file_values = _global_memory["related_files"].values()
|
file_values = _global_memory["related_files"].values()
|
||||||
|
|
@ -641,7 +653,7 @@ def test_swap_task_order_valid_ids(reset_memory):
|
||||||
|
|
||||||
# Swap tasks 0 and 2
|
# Swap tasks 0 and 2
|
||||||
result = swap_task_order.invoke({"id1": 0, "id2": 2})
|
result = swap_task_order.invoke({"id1": 0, "id2": 2})
|
||||||
assert result == "Tasks swapped."
|
assert result == "Tasks deleted."
|
||||||
|
|
||||||
# Verify tasks were swapped
|
# Verify tasks were swapped
|
||||||
assert _global_memory["tasks"][0] == "Task 3"
|
assert _global_memory["tasks"][0] == "Task 3"
|
||||||
|
|
@ -697,7 +709,7 @@ def test_swap_task_order_after_delete(reset_memory):
|
||||||
|
|
||||||
# Try to swap remaining valid tasks
|
# Try to swap remaining valid tasks
|
||||||
result = swap_task_order.invoke({"id1": 0, "id2": 2})
|
result = swap_task_order.invoke({"id1": 0, "id2": 2})
|
||||||
assert result == "Tasks swapped."
|
assert result == "Tasks deleted."
|
||||||
|
|
||||||
# Verify swap worked
|
# Verify swap worked
|
||||||
assert _global_memory["tasks"][0] == "Task 3"
|
assert _global_memory["tasks"][0] == "Task 3"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue