From 8c07fc103510b8ecf822b280fecc3ec83e8569e5 Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Wed, 26 Feb 2025 15:29:28 -0800 Subject: [PATCH 1/8] fix: set git config + misc daemon improvements --- src/codegen/cli/commands/start/main.py | 2 +- .../git/repo_operator/repo_operator.py | 36 +++++++---------- src/codegen/runner/servers/local_daemon.py | 5 ++- src/codegen/shared/logging/get_logger.py | 39 +++++++++---------- 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/src/codegen/cli/commands/start/main.py b/src/codegen/cli/commands/start/main.py index 9d9bc0049..4335a1bf8 100644 --- a/src/codegen/cli/commands/start/main.py +++ b/src/codegen/cli/commands/start/main.py @@ -108,7 +108,7 @@ def _run_docker_container(repo_config: RepoConfig, port: int, detached: bool) -> entry_point = f"uv run --frozen uvicorn codegen.runner.servers.local_daemon:app --host {_default_host} --port {port}" port_args = ["-p", f"{port}:{port}"] detached_args = ["-d"] if detached else [] - run_cmd = ["docker", "run", *detached_args, *port_args, *name_args, *mount_args, *envvars_args, CODEGEN_RUNNER_IMAGE, entry_point] + run_cmd = ["docker", "run", "--rm", *detached_args, *port_args, *name_args, *mount_args, *envvars_args, CODEGEN_RUNNER_IMAGE, entry_point] rich.print(f"run_cmd: {str.join(' ', run_cmd)}") subprocess.run(run_cmd, check=True) diff --git a/src/codegen/git/repo_operator/repo_operator.py b/src/codegen/git/repo_operator/repo_operator.py index 81756ca13..5f045b22f 100644 --- a/src/codegen/git/repo_operator/repo_operator.py +++ b/src/codegen/git/repo_operator/repo_operator.py @@ -145,31 +145,25 @@ def git_cli(self) -> GitCLI: for level in levels: with git_cli.config_reader(level) as reader: if reader.has_option("user", "name") and not username: - username = reader.get("user", "name") - user_level = level + username = username or reader.get("user", "name") + user_level = user_level or level if reader.has_option("user", "email") and not email: - email = reader.get("user", "email") - email_level = level - if self.bot_commit: - self._set_bot_email(git_cli) + email = email or reader.get("user", "email") + email_level = email_level or level + + # We need a username and email to commit, so if they're not set, set them to the bot's + if not username or self.bot_commit: self._set_bot_username(git_cli) - else: - # we need a username and email to commit, so if they're not set, set them to the bot's - # Case 1: username is not set: set it to the bot's - if not username: - self._set_bot_username(git_cli) - # Case 2: username is set to the bot's at the repo level, but something else is set at the user level: unset it - elif username != CODEGEN_BOT_NAME and user_level != "repository": - self._unset_bot_username(git_cli) - # 3: Caseusername is only set at the repo level: do nothing - else: - pass # no-op to make the logic clearer - # Repeat for email - if not email: - self._set_bot_email(git_cli) + if not email or self.bot_commit: + self._set_bot_email(git_cli) - elif email != CODEGEN_BOT_EMAIL and email_level != "repository": + # If user config is set at a level above the repo level: unset it + if not self.bot_commit: + if username and username != CODEGEN_BOT_NAME and user_level != "repository": + self._unset_bot_username(git_cli) + if email and email != CODEGEN_BOT_EMAIL and email_level != "repository": self._unset_bot_email(git_cli) + return git_cli @property diff --git a/src/codegen/runner/servers/local_daemon.py b/src/codegen/runner/servers/local_daemon.py index 1c2e5206c..02e4e55d5 100644 --- a/src/codegen/runner/servers/local_daemon.py +++ b/src/codegen/runner/servers/local_daemon.py @@ -14,6 +14,7 @@ ) from codegen.runner.models.codemod import Codemod, CodemodRunResult from codegen.runner.sandbox.runner import SandboxRunner +from codegen.shared.logging.get_logger import get_colored_logger # Configure logging at module level logging.basicConfig( @@ -21,7 +22,7 @@ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", force=True, ) -logger = logging.getLogger(__name__) +logger = get_colored_logger(__name__) server_info: ServerInfo runner: SandboxRunner @@ -43,7 +44,7 @@ async def lifespan(server: FastAPI): runner.op.git_cli.git.config("user.name", CODEGEN_BOT_NAME) # Parse the codebase - logger.info(f"Starting up sandbox fastapi server for repo_name={repo_config.name}") + logger.info(f"Starting up fastapi server for repo_name={repo_config.name}") server_info.warmup_state = WarmupState.PENDING await runner.warmup() server_info.synced_commit = runner.commit.hexsha diff --git a/src/codegen/shared/logging/get_logger.py b/src/codegen/shared/logging/get_logger.py index 0e287412a..9fe4b1a64 100644 --- a/src/codegen/shared/logging/get_logger.py +++ b/src/codegen/shared/logging/get_logger.py @@ -2,32 +2,31 @@ import colorlog -handler = colorlog.StreamHandler() -handler.setFormatter( - colorlog.ColoredFormatter( - "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s", - log_colors={ - "DEBUG": "cyan", - "INFO": "green", - "WARNING": "yellow", - "ERROR": "red", - "CRITICAL": "red,bg_white", - }, - secondary_log_colors={ - "message": { + +def get_colored_logger(name: str) -> logging.Logger: + handler = colorlog.StreamHandler() + handler.setFormatter( + colorlog.ColoredFormatter( + "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s", + log_colors={ "DEBUG": "cyan", - "INFO": "blue", + "INFO": "green", "WARNING": "yellow", "ERROR": "red", "CRITICAL": "red,bg_white", - } - }, + }, + secondary_log_colors={ + "message": { + "DEBUG": "cyan", + "INFO": "blue", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red,bg_white", + } + }, + ) ) -) - -def get_logger(name: str, level: int = logging.INFO) -> logging.Logger: logger = logging.getLogger(name) logger.addHandler(handler) - logger.setLevel(level) return logger From d04efa0892d0f4501ac1ac9fb70ddde4ac8939d2 Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Wed, 26 Feb 2025 15:30:41 -0800 Subject: [PATCH 2/8] fix: set git config + misc daemon improvements --- src/codegen/cli/commands/run/run_daemon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/cli/commands/run/run_daemon.py b/src/codegen/cli/commands/run/run_daemon.py index f54362c49..2f108c0fb 100644 --- a/src/codegen/cli/commands/run/run_daemon.py +++ b/src/codegen/cli/commands/run/run_daemon.py @@ -46,7 +46,7 @@ def run_daemon(session: CodegenSession, function, diff_preview: int | None = Non limited_diff = "\n".join(diff_lines[:diff_preview]) if truncated: - limited_diff += "\n\n...\n\n[yellow]diff truncated to {diff_preview} lines, view the full change set on your local file system after using run with `--apply-local`[/yellow]" + limited_diff += f"\n\n...\n\n[yellow]diff truncated to {diff_preview} lines[/yellow]" panel = Panel(limited_diff, title="[bold]Diff Preview[/bold]", border_style="blue", padding=(1, 2), expand=False) rich.print(panel) From 7f77051d98e02faef9fd77598d25e3dbf43196f8 Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Wed, 26 Feb 2025 15:34:17 -0800 Subject: [PATCH 3/8] skip repo clean + clone --- src/codegen/runner/sandbox/runner.py | 4 ++-- src/codegen/runner/servers/local_daemon.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/codegen/runner/sandbox/runner.py b/src/codegen/runner/sandbox/runner.py index eace1af97..c05075b49 100644 --- a/src/codegen/runner/sandbox/runner.py +++ b/src/codegen/runner/sandbox/runner.py @@ -28,9 +28,9 @@ class SandboxRunner: codebase: CodebaseType executor: SandboxExecutor - def __init__(self, repo_config: RepoConfig) -> None: + def __init__(self, repo_config: RepoConfig, op: RepoOperator | None = None) -> None: self.repo = repo_config - self.op = RepoOperator(repo_config=self.repo, setup_option=SetupOption.PULL_OR_CLONE, bot_commit=True) + self.op = op or RepoOperator(repo_config=self.repo, setup_option=SetupOption.PULL_OR_CLONE, bot_commit=True) self.commit = self.op.git_cli.head.commit async def warmup(self) -> None: diff --git a/src/codegen/runner/servers/local_daemon.py b/src/codegen/runner/servers/local_daemon.py index 02e4e55d5..15df525eb 100644 --- a/src/codegen/runner/servers/local_daemon.py +++ b/src/codegen/runner/servers/local_daemon.py @@ -4,6 +4,8 @@ from fastapi import FastAPI from codegen.git.configs.constants import CODEGEN_BOT_EMAIL, CODEGEN_BOT_NAME +from codegen.git.repo_operator.repo_operator import RepoOperator +from codegen.git.schemas.enums import SetupOption from codegen.git.schemas.repo_config import RepoConfig from codegen.runner.enums.warmup_state import WarmupState from codegen.runner.models.apis import ( @@ -38,8 +40,9 @@ async def lifespan(server: FastAPI): server_info = ServerInfo(repo_name=repo_config.full_name or repo_config.name) # Set the bot email and username + op = RepoOperator(repo_config=repo_config, setup_option=SetupOption.SKIP, bot_commit=True) + runner = SandboxRunner(repo_config=repo_config, op=op) logger.info(f"Configuring git user config to {CODEGEN_BOT_EMAIL} and {CODEGEN_BOT_NAME}") - runner = SandboxRunner(repo_config=repo_config) runner.op.git_cli.git.config("user.email", CODEGEN_BOT_EMAIL) runner.op.git_cli.git.config("user.name", CODEGEN_BOT_NAME) From efe3d17b832c6e07f24a38d20f1b48f59c6b5361 Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Wed, 26 Feb 2025 15:42:20 -0800 Subject: [PATCH 4/8] dedupe logger handlers --- src/codegen/shared/logging/get_logger.py | 40 +++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/codegen/shared/logging/get_logger.py b/src/codegen/shared/logging/get_logger.py index 9fe4b1a64..02b5dd377 100644 --- a/src/codegen/shared/logging/get_logger.py +++ b/src/codegen/shared/logging/get_logger.py @@ -4,29 +4,31 @@ def get_colored_logger(name: str) -> logging.Logger: - handler = colorlog.StreamHandler() - handler.setFormatter( - colorlog.ColoredFormatter( - "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s", - log_colors={ + formatter = colorlog.ColoredFormatter( + "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s", + log_colors={ + "DEBUG": "cyan", + "INFO": "green", + "WARNING": "yellow", + "ERROR": "red", + "CRITICAL": "red,bg_white", + }, + secondary_log_colors={ + "message": { "DEBUG": "cyan", - "INFO": "green", + "INFO": "blue", "WARNING": "yellow", "ERROR": "red", "CRITICAL": "red,bg_white", - }, - secondary_log_colors={ - "message": { - "DEBUG": "cyan", - "INFO": "blue", - "WARNING": "yellow", - "ERROR": "red", - "CRITICAL": "red,bg_white", - } - }, - ) + } + }, ) - logger = logging.getLogger(name) - logger.addHandler(handler) + if logger.hasHandlers(): + for h in logger.handlers: + h.setFormatter(formatter) + else: + handler = colorlog.StreamHandler() + handler.setFormatter(formatter) + logger.addHandler(handler) return logger From b828c60d8c42d65eca64befb9942f1a3b7393f9b Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Wed, 26 Feb 2025 15:50:53 -0800 Subject: [PATCH 5/8] nit logger fix --- src/codegen/runner/servers/local_daemon.py | 4 ++-- src/codegen/sdk/codebase/codebase_context.py | 5 ++--- src/codegen/shared/logging/get_logger.py | 12 ++++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/codegen/runner/servers/local_daemon.py b/src/codegen/runner/servers/local_daemon.py index 15df525eb..4cedc29c9 100644 --- a/src/codegen/runner/servers/local_daemon.py +++ b/src/codegen/runner/servers/local_daemon.py @@ -16,7 +16,7 @@ ) from codegen.runner.models.codemod import Codemod, CodemodRunResult from codegen.runner.sandbox.runner import SandboxRunner -from codegen.shared.logging.get_logger import get_colored_logger +from codegen.shared.logging.get_logger import get_logger # Configure logging at module level logging.basicConfig( @@ -24,7 +24,7 @@ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", force=True, ) -logger = get_colored_logger(__name__) +logger = get_logger(__name__) server_info: ServerInfo runner: SandboxRunner diff --git a/src/codegen/sdk/codebase/codebase_context.py b/src/codegen/sdk/codebase/codebase_context.py index 1d4eb2fda..9a015ba10 100644 --- a/src/codegen/sdk/codebase/codebase_context.py +++ b/src/codegen/sdk/codebase/codebase_context.py @@ -31,6 +31,7 @@ from codegen.sdk.typescript.external.ts_declassify.ts_declassify import TSDeclassify from codegen.shared.enums.programming_language import ProgrammingLanguage from codegen.shared.exceptions.control_flow import StopCodemodException +from codegen.shared.logging.get_logger import get_logger from codegen.shared.performance.stopwatch_utils import stopwatch, stopwatch_with_sentry if TYPE_CHECKING: @@ -51,9 +52,7 @@ from codegen.sdk.core.node_id_factory import NodeId from codegen.sdk.core.parser import Parser -import logging - -logger = logging.getLogger(__name__) +logger = get_logger(__name__) # src/vs/platform/contextview/browser/contextMenuService.ts is ignored as there is a parsing error with tree-sitter diff --git a/src/codegen/shared/logging/get_logger.py b/src/codegen/shared/logging/get_logger.py index 02b5dd377..50de4d006 100644 --- a/src/codegen/shared/logging/get_logger.py +++ b/src/codegen/shared/logging/get_logger.py @@ -3,7 +3,7 @@ import colorlog -def get_colored_logger(name: str) -> logging.Logger: +def get_logger(name: str) -> logging.Logger: formatter = colorlog.ColoredFormatter( "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s", log_colors={ @@ -26,9 +26,9 @@ def get_colored_logger(name: str) -> logging.Logger: logger = logging.getLogger(name) if logger.hasHandlers(): for h in logger.handlers: - h.setFormatter(formatter) - else: - handler = colorlog.StreamHandler() - handler.setFormatter(formatter) - logger.addHandler(handler) + logger.removeHandler(h) + + handler = colorlog.StreamHandler() + handler.setFormatter(formatter) + logger.addHandler(handler) return logger From 4b87a5ef32531cd4d11f014807c25c2207fa2b91 Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Wed, 26 Feb 2025 16:05:23 -0800 Subject: [PATCH 6/8] auto resolve platform --- src/codegen/cli/commands/start/main.py | 46 ++++++++++++++++++++------ 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/src/codegen/cli/commands/start/main.py b/src/codegen/cli/commands/start/main.py index 4335a1bf8..da6787128 100644 --- a/src/codegen/cli/commands/start/main.py +++ b/src/codegen/cli/commands/start/main.py @@ -1,3 +1,4 @@ +import platform as py_platform import subprocess from importlib.metadata import version from pathlib import Path @@ -18,12 +19,11 @@ @click.command(name="start") -@click.option("--platform", "-t", type=click.Choice(["linux/amd64", "linux/arm64", "linux/amd64,linux/arm64"]), default="linux/amd64,linux/arm64", help="Target platform(s) for the Docker image") @click.option("--port", "-p", type=int, default=None, help="Port to run the server on") @click.option("--detached", "-d", is_flag=True, help="Run the server in detached mode") @click.option("--skip-build", is_flag=True, help="Skip building the Docker image") @click.option("--force", "-f", is_flag=True, help="Force start the server even if it is already running") -def start_command(port: int | None, platform: str, detached: bool = False, skip_build: bool = False, force: bool = False) -> None: +def start_command(port: int | None, detached: bool = False, skip_build: bool = False, force: bool = False) -> None: """Starts a local codegen server""" repo_path = Path.cwd().resolve() repo_config = RepoConfig.from_repo_path(str(repo_path)) @@ -42,15 +42,10 @@ def start_command(port: int | None, platform: str, detached: bool = False, skip_ try: if not skip_build: - rich.print("[bold blue]Building Docker image...[/bold blue]") - _build_docker_image(codegen_root, platform) - rich.print("[bold blue]Starting Docker container...[/bold blue]") + _build_docker_image(codegen_root) _run_docker_container(repo_config, port, detached) rich.print(Panel(f"[green]Server started successfully![/green]\nAccess the server at: [bold]http://{_default_host}:{port}[/bold]", box=ROUNDED, title="Codegen Server")) # TODO: memory snapshot here - except subprocess.CalledProcessError as e: - rich.print(f"[bold red]Error:[/bold red] Failed to {e.cmd[0]} Docker container") - raise click.Abort() except Exception as e: rich.print(f"[bold red]Error:[/bold red] {e!s}") raise click.Abort() @@ -75,7 +70,8 @@ def _handle_existing_container(repo_config: RepoConfig, container: DockerContain click.Abort() -def _build_docker_image(codegen_root: Path, platform: str) -> None: +def _build_docker_image(codegen_root: Path) -> None: + platform = _get_platform() build_cmd = [ "docker", "buildx", @@ -89,11 +85,31 @@ def _build_docker_image(codegen_root: Path, platform: str) -> None: "--load", str(codegen_root), ] - rich.print(f"build_cmd: {str.join(' ', build_cmd)}") + rich.print( + Panel( + f"{str.join(' ', build_cmd)}", + box=ROUNDED, + title="Running Build Command", + style="blue", + padding=(1, 1), + ) + ) subprocess.run(build_cmd, check=True) +def _get_platform() -> str: + machine = py_platform.machine().lower() + if machine in ("x86_64", "amd64"): + return "linux/amd64" + elif machine in ("arm64", "aarch64"): + return "linux/arm64" + else: + rich.print(f"[yellow]Warning: Unknown architecture {machine}, defaulting to linux/amd64[/yellow]") + return "linux/amd64" + + def _run_docker_container(repo_config: RepoConfig, port: int, detached: bool) -> None: + rich.print("[bold blue]Starting Docker container...[/bold blue]") container_repo_path = f"/app/git/{repo_config.name}" name_args = ["--name", f"{repo_config.name}"] envvars = { @@ -110,7 +126,15 @@ def _run_docker_container(repo_config: RepoConfig, port: int, detached: bool) -> detached_args = ["-d"] if detached else [] run_cmd = ["docker", "run", "--rm", *detached_args, *port_args, *name_args, *mount_args, *envvars_args, CODEGEN_RUNNER_IMAGE, entry_point] - rich.print(f"run_cmd: {str.join(' ', run_cmd)}") + rich.print( + Panel( + f"{str.join(' ', run_cmd)}", + box=ROUNDED, + title="Running Run Command", + style="blue", + padding=(1, 1), + ) + ) subprocess.run(run_cmd, check=True) if detached: From defb5acaa7b5323108da4fd39a8f734c8047342a Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Wed, 26 Feb 2025 16:09:28 -0800 Subject: [PATCH 7/8] dedupe logs --- src/codegen/shared/logging/get_logger.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/codegen/shared/logging/get_logger.py b/src/codegen/shared/logging/get_logger.py index 50de4d006..2c6c883d9 100644 --- a/src/codegen/shared/logging/get_logger.py +++ b/src/codegen/shared/logging/get_logger.py @@ -3,7 +3,10 @@ import colorlog -def get_logger(name: str) -> logging.Logger: +def get_logger(name: str, level: int = logging.INFO) -> logging.Logger: + # Force configure the root logger with a NullHandler to prevent duplicate logs + logging.basicConfig(handlers=[logging.NullHandler()], force=True) + formatter = colorlog.ColoredFormatter( "%(white)s%(asctime)s - %(name)s - %(log_color)s%(levelname)s%(reset)s%(white)s - %(message_log_color)s%(message)s", log_colors={ @@ -31,4 +34,8 @@ def get_logger(name: str) -> logging.Logger: handler = colorlog.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) + # Ensure the logger propagates to the root logger + logger.propagate = False + # Set the level on the logger itself + logger.setLevel(level) return logger From c67551d24802c4f3a1ab1fa96818bacc5c908a3a Mon Sep 17 00:00:00 2001 From: Carol Jung Date: Thu, 27 Feb 2025 09:30:52 -0800 Subject: [PATCH 8/8] uv.lock changes --- uv.lock | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/uv.lock b/uv.lock index cc423626f..3e9c35563 100644 --- a/uv.lock +++ b/uv.lock @@ -561,6 +561,7 @@ dependencies = [ { name = "langchain-core" }, { name = "langchain-openai" }, { name = "langgraph" }, + { name = "langgraph-prebuilt" }, { name = "lazy-object-proxy" }, { name = "lox" }, { name = "mcp", extra = ["cli"] }, @@ -687,6 +688,7 @@ requires-dist = [ { name = "langchain-core" }, { name = "langchain-openai" }, { name = "langgraph" }, + { name = "langgraph-prebuilt" }, { name = "lazy-object-proxy", specifier = ">=0.0.0" }, { name = "lox", specifier = ">=0.12.0" }, { name = "lsprotocol", marker = "extra == 'lsp'", specifier = "==2024.0.0b1" }, @@ -2117,6 +2119,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/63/03bc3dd304ead45b53313cab8727329e1d139a2d220f2d030c72242c860e/langgraph_checkpoint-2.0.16-py3-none-any.whl", hash = "sha256:dfab51076a6eddb5f9e146cfe1b977e3dd6419168b2afa23ff3f4e47973bf06f", size = 38291 }, ] +[[package]] +name = "langgraph-prebuilt" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "langgraph-checkpoint" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/15/848593ccace12e4f8b80cc0b159b0ba1da17605e1eecbda5f37d891748a3/langgraph_prebuilt-0.1.1.tar.gz", hash = "sha256:420a748ff93842f2b1a345a0c1ca3939d2bc7a2d46c20e9a9a0d8f148152cc47", size = 23257 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/62/a424fdb892f578fa88b2ff4df0bfdebdc8b89501dacb8ca3b480305cbfef/langgraph_prebuilt-0.1.1-py3-none-any.whl", hash = "sha256:148a9558a36ec7e83cc6512f3521425c862b0463251ae0242ade52a448c54e78", size = 24622 }, +] + [[package]] name = "langgraph-sdk" version = "0.1.53"