Spaces:
Running
Running
File size: 5,134 Bytes
0913c52 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | """
This is the new version of the coder tool, which uses aider as a command line tool.
"""
import re
import shutil
import subprocess
import tempfile
from loguru import logger
from .registry import register_tool, register_toolset_desc
register_toolset_desc(
"coder",
"Coder toolset. This toolset allows you to call an AI coding agent using natural language instructions. "
"The agent can modify, create, or edit code files based on your instructions. "
"But you have to provide the details, like the file structure, the file names, the file paths, the file content, etc."
"Note: The coding agent has no memory of previous interactions and operates independently on each call. And it will not chat and exit the session after it's done.",
)
@register_tool(
"coder",
{
"type": "function",
"function": {
"name": "run_coder",
"description": (
"Execute a coding task using an AI coding agent (aider). "
"This tool calls another agent with natural language instructions to modify, create, or edit code files. "
"But you have to provide the details, like the file structure, the file names, the file paths, the file content, etc."
"IMPORTANT: The coding agent has no memory of previous interactions - each call is independent. "
"Provide clear, complete instructions for each task."
"And the coding agent will not chat and exit the session after it's done."
),
"parameters": {
"type": "object",
"properties": {
"fnames": {
"type": "array",
"items": {"type": "string"},
"description": (
"Optional list of file paths or directory paths to add to the coding session. "
"The agent will have access to read and modify these files. "
"If not provided, the agent will work without pre-loaded files."
),
},
"instruction": {
"type": "string",
"description": (
"Natural language instruction for the coding agent. "
"Be specific and clear about what you want the agent to do. "
"Example: 'Create a new function called calculate_sum that adds two numbers'"
),
},
},
"required": ["instruction"],
},
},
},
)
def run_coder(fnames: list[str] | None = None, instruction: str = "") -> str:
"""
Execute a coding task using aider.
Args:
fnames: Optional list of file paths to include in the coding session
instruction: Natural language instruction for the coding agent
Returns:
Result message from the coding agent
"""
# Use OS command line to call `aider` and pass the instruction via stdin
logger.debug("Running aider with fnames: \n{}\n\ninstruction: \n{}", fnames, instruction)
try:
if not instruction.strip():
return "Error: instruction must be a non-empty string."
aider_path = shutil.which("aider")
if not aider_path:
err_text = "Error: 'aider' executable not found in PATH. Please install aider and ensure it is available."
logger.error(err_text)
return err_text
# create temp file to store the instruction
with tempfile.NamedTemporaryFile(mode="w") as temp_file:
temp_file.write(instruction)
temp_file.flush()
temp_file_path = temp_file.name
cmd = [aider_path, "--message-file", temp_file_path, "--yes", "--exit"]
if fnames:
cmd.extend(fnames)
result = subprocess.run(
cmd,
text=True,
capture_output=True,
)
if result.returncode != 0:
return "Error executing coding task: " + (
result.stderr.strip() or f"Non-zero exit status {result.returncode}"
)
output_text = result.stdout.strip()
output_text = _parse_aider_output(output_text)
return output_text or "Coding task completed."
except Exception as e:
return f"Error executing coding task: {str(e)}"
def _parse_aider_output(output_text: str) -> str:
"""Parse aider output to extract the result message."""
lines = output_text.splitlines()
result = ""
for i, line in enumerate(lines):
line = line.strip()
if line.startswith("Repo-map:"):
result = "\n".join(lines[i + 1 :])
break
else:
result = "\n".join(lines)
result = result.strip()
# remove all the color codes in the results
ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
result = ansi_escape.sub("", result)
return result
|