From 1084faaf0be7852522163f1d8e9d55e0adc47fcc Mon Sep 17 00:00:00 2001 From: AI Christianson Date: Thu, 13 Feb 2025 09:45:32 -0500 Subject: [PATCH] normalize/dedupe files for programmer tool --- ra_aid/tools/programmer.py | 18 +++++++-------- tests/ra_aid/test_programmer.py | 40 ++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/ra_aid/tools/programmer.py b/ra_aid/tools/programmer.py index 4d9df17..1902b28 100644 --- a/ra_aid/tools/programmer.py +++ b/ra_aid/tools/programmer.py @@ -1,4 +1,5 @@ import os +import os import sys from pathlib import Path from typing import Dict, List, Union @@ -77,13 +78,6 @@ def run_programming_task( Returns: { "output": stdout+stderr, "return_code": 0 if success, "success": True/False } """ - # Get related files if no specific files provided - file_paths = ( - list(_global_memory["related_files"].values()) - if "related_files" in _global_memory - else [] - ) - # Build command aider_exe = get_aider_executable() command = [ @@ -96,6 +90,13 @@ def run_programming_task( "--no-check-update", ] + # Get combined list of files (explicit + related) with normalized paths + # and deduplicated using set operations + files_to_use = list({os.path.abspath(f) for f in (files or [])} | { + os.path.abspath(f) for f in _global_memory["related_files"].values() + if "related_files" in _global_memory + }) + # Add config file if specified if "config" in _global_memory and _global_memory["config"].get("aider_config"): command.extend(["--config", _global_memory["config"]["aider_config"]]) @@ -112,9 +113,6 @@ def run_programming_task( command.append("-m") command.append(instructions) - - # Add files to command - files_to_use = file_paths + (files or []) if files_to_use: command.extend(files_to_use) diff --git a/tests/ra_aid/test_programmer.py b/tests/ra_aid/test_programmer.py index 4540cbc..bef87e1 100644 --- a/tests/ra_aid/test_programmer.py +++ b/tests/ra_aid/test_programmer.py @@ -1,5 +1,6 @@ import pytest from pathlib import Path +from langchain_core.tools import Tool from ra_aid.tools.programmer import parse_aider_flags, run_programming_task, get_aider_executable @@ -88,7 +89,7 @@ def test_aider_config_flag(mocker): "ra_aid.tools.programmer.run_interactive_command", return_value=(b"", 0) ) - run_programming_task("test instruction") + run_programming_task.invoke({"instructions": "test instruction"}) args = mock_run.call_args[0][0] # Get the first positional arg (command list) assert "--config" in args @@ -96,6 +97,43 @@ def test_aider_config_flag(mocker): assert args[config_index + 1] == "/path/to/config.yml" +def test_path_normalization_and_deduplication(mocker, tmp_path): + """Test path normalization and deduplication in run_programming_task.""" + # Create a temporary test file + test_file = tmp_path / "test.py" + test_file.write_text("") + new_file = tmp_path / "new.py" + + # Mock dependencies + mocker.patch("ra_aid.tools.programmer._global_memory", {"related_files": {}}) + mocker.patch("ra_aid.tools.programmer.get_aider_executable", return_value="/path/to/aider") + mock_run = mocker.patch("ra_aid.tools.programmer.run_interactive_command", return_value=(b"", 0)) + + # Test duplicate paths + run_programming_task.invoke({ + "instructions": "test instruction", + "files": [str(test_file), str(test_file)] # Same path twice + }) + + # Get the command list passed to run_interactive_command + cmd_args = mock_run.call_args[0][0] + # Count occurrences of test_file path in command + test_file_count = sum(1 for arg in cmd_args if arg == str(test_file)) + assert test_file_count == 1, "Expected exactly one instance of test_file path" + + # Test mixed paths + run_programming_task.invoke({ + "instructions": "test instruction", + "files": [str(test_file), str(new_file)] # Two different paths + }) + + # Get the command list from the second call + cmd_args = mock_run.call_args[0][0] + # Verify both paths are present exactly once + assert sum(1 for arg in cmd_args if arg == str(test_file)) == 1, "Expected one instance of test_file" + assert sum(1 for arg in cmd_args if arg == str(new_file)) == 1, "Expected one instance of new_file" + + def test_get_aider_executable(mocker): """Test the get_aider_executable function.""" mock_sys = mocker.patch("ra_aid.tools.programmer.sys")