Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,5 @@ logs/
repos/
config.yml
hydra_outputs/
.commit0*
.commit0*
.agent*
File renamed without changes.
10 changes: 10 additions & 0 deletions agent/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from agent.cli import agent_app


def main() -> None:
"""Main function to run the CLI"""
agent_app()


if __name__ == "__main__":
main()
File renamed without changes.
File renamed without changes.
176 changes: 176 additions & 0 deletions agent/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import typer
import subprocess
import yaml
from agent.run_agent import run_agent

agent_app = typer.Typer(
no_args_is_help=True,
add_completion=False,
context_settings={"help_option_names": ["-h", "--help"]},
help="""
This is the command for running agent on Commit-0.

See the website at https://commit-0.github.io/ for documentation and more information about Commit-0.
""",
)


class Colors:
RESET = "\033[0m"
RED = "\033[91m"
YELLOW = "\033[93m"
CYAN = "\033[96m"
ORANGE = "\033[95m"


def write_agent_config(agent_config_file: str, agent_config: dict) -> None:
"""Write the agent config to the file."""
with open(agent_config_file, "w") as f:
yaml.dump(agent_config, f)


def check_aider_path() -> None:
"""Code adapted from https://github.com/modal-labs/modal-client/blob/a8ddd418f8c65b7e168a9125451eeb70da2b6203/modal/cli/entry_point.py#L55

Checks whether the `aider` executable is on the path and usable.
"""
url = "https://aider.chat/docs/install.html"
try:
subprocess.run(["aider", "--help"], capture_output=True)
# TODO(erikbern): check returncode?
return
except FileNotFoundError:
typer.echo(
typer.style(
"The `aider` command was not found on your path!", fg=typer.colors.RED
)
+ "\n"
+ typer.style(
"You may need to add it to your path or use `python -m run_agent` as a workaround.",
fg=typer.colors.RED,
)
)
except PermissionError:
typer.echo(
typer.style("The `aider` command is not executable!", fg=typer.colors.RED)
+ "\n"
+ typer.style(
"You may need to give it permissions or use `python -m run_agent` as a workaround.",
fg=typer.colors.RED,
)
)
typer.echo(f"See more information here:\n\n{url}")
typer.echo("─" * 80) # Simple rule to separate content


def highlight(text: str, color: str) -> str:
"""Highlight text with a color."""
return f"{color}{text}{Colors.RESET}"


@agent_app.command()
def config(
agent_name: str = typer.Argument(
...,
help=f"Agent to use, we only support {highlight('aider', Colors.ORANGE)} for now",
),
model_name: str = typer.Option(
"claude-3-5-sonnet-20240620",
help="Model to use, check https://aider.chat/docs/llms.html for more information",
),
use_user_prompt: bool = typer.Option(
False,
help="Use the user prompt instead of the default prompt",
),
user_prompt: str = typer.Option(
"Here is your task:\nYou need to implement all functions with ' pass' and pass the unit tests.\nDo not change the names of existing functions or classes, as they may be referenced from other code like unit tests, etc.\nWhen you generate code, you must maintain the original formatting of the function stubs (such as whitespaces), otherwise we will not able to search/replace blocks for code modifications, and therefore you will receive a score of 0 for your generated code.",
help="User prompt to use",
),
run_tests: bool = typer.Option(
False,
help="Run the tests after the agent is done",
),
max_iteration: int = typer.Option(
3,
help="Maximum number of iterations to run",
),
use_repo_info: bool = typer.Option(
False,
help="Use the repository information",
),
max_repo_info_length: int = typer.Option(
10000,
help="Maximum length of the repository information to use",
),
use_unit_tests_info: bool = typer.Option(
False,
help="Use the unit tests information",
),
max_unit_tests_info_length: int = typer.Option(
10000,
help="Maximum length of the unit tests information to use",
),
use_spec_info: bool = typer.Option(
False,
help="Use the spec information",
),
max_spec_info_length: int = typer.Option(
10000,
help="Maximum length of the spec information to use",
),
use_lint_info: bool = typer.Option(
False,
help="Use the lint information",
),
max_lint_info_length: int = typer.Option(
10000,
help="Maximum length of the lint information to use",
),
pre_commit_config_path: str = typer.Option(
".pre-commit-config.yaml",
help="Path to the pre-commit config file",
),
agent_config_file: str = typer.Option(
".agent.yaml",
help="Path to the agent config file",
),
) -> None:
"""Configure the agent."""
if agent_name == "aider":
check_aider_path()
else:
raise typer.BadParameter(
f"Invalid {highlight('AGENT', Colors.RED)}. We only support aider for now",
param_hint="AGENT",
)

agent_config = {
"agent_name": agent_name,
"model_name": model_name,
"use_user_prompt": use_user_prompt,
"user_prompt": user_prompt,
"run_tests": run_tests,
"max_iteration": max_iteration,
"use_repo_info": use_repo_info,
"max_repo_info_length": max_repo_info_length,
"use_unit_tests_info": use_unit_tests_info,
"max_unit_tests_info_length": max_unit_tests_info_length,
"use_spec_info": use_spec_info,
"max_spec_info_length": max_spec_info_length,
"use_lint_info": use_lint_info,
"max_lint_info_length": max_lint_info_length,
"pre_commit_config_path": pre_commit_config_path,
}

write_agent_config(agent_config_file, agent_config)


@agent_app.command()
def run(
agent_config_file: str = typer.Argument(
".agent.yaml",
help="Path to the agent config file",
),
) -> None:
"""Run the agent on the repository."""
run_agent(agent_config_file)
4 changes: 2 additions & 2 deletions baselines/commit0_utils.py → agent/commit0_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import List
import fitz

from baselines.class_types import AgentConfig
from agent.class_types import AgentConfig

PROMPT_HEADER = ">>> Here is the Task:\n"
REFERENCE_HEADER = "\n\n>>> Here is the Reference for you to finish the task:\n"
Expand Down Expand Up @@ -128,7 +128,7 @@ def get_target_edit_files(target_dir: str) -> list[str]:
if filename.endswith(".py"):
file_path = os.path.join(root, filename)
with open(file_path, "r") as file:
if "NotImplementedError('IMPLEMENT ME HERE')" in file.read():
if " pass" in file.read():
files.append(file_path)

# Remove the base_dir prefix
Expand Down
File renamed without changes.
File renamed without changes.
39 changes: 22 additions & 17 deletions baselines/run_agent.py → agent/run_agent.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import os
import sys
import hydra
import yaml
import multiprocessing
from tqdm import tqdm
from datasets import load_dataset
from git import Repo
from baselines.commit0_utils import (
from agent.commit0_utils import (
args2string,
create_branch,
get_message,
get_target_edit_files,
get_lint_cmd,
)
from baselines.agents import AiderAgents
from agent.agents import AiderAgents
from typing import Optional, Type
from types import TracebackType
from hydra.core.config_store import ConfigStore
from baselines.class_types import AgentConfig
from agent.class_types import AgentConfig
from commit0.harness.constants import SPLIT
from commit0.harness.get_pytest_ids import main as get_tests
from commit0.harness.constants import RUN_AIDER_LOG_DIR, RepoInstance
from tqdm import tqdm
from commit0.cli import read_commit0_dot_file


Expand All @@ -40,6 +39,14 @@ def __exit__(
os.chdir(self.cwd)


def read_yaml_config(config_file: str) -> dict:
"""Read the yaml config from the file."""
if not os.path.exists(config_file):
raise FileNotFoundError(f"The config file '{config_file}' does not exist.")
with open(config_file, "r") as f:
return yaml.load(f, Loader=yaml.FullLoader)


def run_agent_for_repo(
repo_base_dir: str,
agent_config: AgentConfig,
Expand Down Expand Up @@ -111,21 +118,23 @@ def run_agent_for_repo(
for f in target_edit_files:
file_name = f.replace(".py", "").replace("/", "__")
log_dir = RUN_AIDER_LOG_DIR / "no_tests" / file_name
# write agent_config to .agent.yaml
with open(
RUN_AIDER_LOG_DIR / "no_tests" / file_name / ".agent.yaml", "w"
) as agent_config_file:
yaml.dump(agent_config, agent_config_file)
lint_cmd = get_lint_cmd(local_repo, agent_config.use_lint_info)

agent.run(message, "", lint_cmd, [f], log_dir)


def main() -> None:
def run_agent(agent_config_file: str) -> None:
"""Main function to run Aider for a given repository.

Will run in parallel for each repo.
"""
cs = ConfigStore.instance()
cs.store(name="user", node=AgentConfig)
hydra.initialize(version_base=None, config_path="configs")
config = hydra.compose(config_name="agent")
agent_config = AgentConfig(**config.agent_config)
config = read_yaml_config(agent_config_file)

agent_config = AgentConfig(**config)

commit0_config = read_commit0_dot_file(".commit0.yaml")

Expand Down Expand Up @@ -168,7 +177,3 @@ def main() -> None:

for result in results:
result.wait()


if __name__ == "__main__":
main()
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ requires-python = ">=3.10"
dependencies = [
"ruff>=0.6.4",
"pre-commit>=3.8.0",
"hydra-core>=1.3.2",
"PyMuPDF>=1.24.5",
"aider-chat>=0.56.0",
"modal>=0.64.95",
Expand All @@ -22,7 +21,7 @@ dependencies = [
"gitpython>=3.1.43",
"pytest>=8.3.3",
]
scripts = { commit0 = "commit0.__main__:main" }
scripts = { commit0 = "commit0.__main__:main", agent = "agent.__main__:main" }

[tool.pyright]
include = ["**/commit0"]
Expand Down
35 changes: 0 additions & 35 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.