From e915ca48ff0119b661cc70c0542fec22e2f8181f Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 7 Oct 2025 10:34:34 -0400 Subject: [PATCH 01/27] SWE-agent local model WIP commit --- .../swe_agent/swe_agent_translator.py | 106 ++++++++++++++++-- .../microXOR/cuda/repo/translation_task.md | 6 +- targets/nanoXOR/cuda/repo/translation_task.md | 7 ++ 3 files changed, 108 insertions(+), 11 deletions(-) create mode 100644 targets/nanoXOR/cuda/repo/translation_task.md diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 9cb1839..261f617 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -32,6 +32,13 @@ class SWEAgentTranslator(Translator): # Instance variables _swe_agent_model_name: str _swe_agent_per_instance_cost_limit: float + _swe_agent_total_cost_limit: float + _swe_agent_per_instance_call_limit: Optional[int] + _swe_agent_api_base: Optional[str] + _swe_agent_config: Optional[str] + _swe_agent_parser: Optional[str] + _swe_agent_max_input_token: Optional[int] + _temp_repo_path: str _translation_task_path: str _output_path: str @@ -47,7 +54,13 @@ def __init__( dry: bool = False, hide_progress: bool = False, swe_agent_model_name: Optional[str] = None, - swe_agent_per_instance_cost_limit: float = 0.06 + swe_agent_per_instance_cost_limit: float = 0.06, + swe_agent_total_cost_limit: float = 0.0, + swe_agent_per_instance_call_limit: Optional[int] = None, + swe_agent_api_base: Optional[str] = None, + swe_agent_config: Optional[str] = None, + swe_agent_parser: Optional[str] = None, + swe_agent_max_input_token: Optional[int] = None, ) -> None: super().__init__( input_repo, @@ -60,33 +73,77 @@ def __init__( hide_progress=hide_progress ) - self._swe_agent_model_name = swe_agent_model_name + self._swe_agent_model_name = swe_agent_model_name or "" self._swe_agent_per_instance_cost_limit = swe_agent_per_instance_cost_limit + self._swe_agent_total_cost_limit = swe_agent_total_cost_limit + self._swe_agent_per_instance_call_limit = swe_agent_per_instance_call_limit + self._swe_agent_api_base = swe_agent_api_base + self._swe_agent_config = swe_agent_config + self._swe_agent_parser = swe_agent_parser + self._swe_agent_max_input_token = swe_agent_max_input_token + self._temp_repo_path = self.TEMP_REPO_PATH self._translation_task_path = os.path.join( self._input_repo.path, self.TRANSLATION_TASK_FILENAME ) self._output_path = os.path.join(self._output_paths[0], "repo") + if self._is_ollama_model(self._swe_agent_model_name): + if not self._swe_agent_api_base: + self._swe_agent_api_base = os.getenv("OLLAMA_HOST", "http://127.0.0.1:11434") + if self._swe_agent_parser is None: + self._swe_agent_parser = "thought_action" + if self._swe_agent_total_cost_limit is None: + self._swe_agent_total_cost_limit = 0.0 + if self._swe_agent_per_instance_call_limit is None: + self._swe_agent_per_instance_call_limit = 100 + if self._swe_agent_max_input_token is None: + # quiets 'No max input tokens found' warnings for most local models + self._swe_agent_max_input_token = 4096 + # Local runs typically disable cost tracking + if self._swe_agent_per_instance_cost_limit is None: + self._swe_agent_per_instance_cost_limit = 0.0 + + @staticmethod + def _is_ollama_model(name: str) -> bool: + name = (name or "").lower() + return name.startswith("ollama/") or name.startswith("ollama_chat/") @staticmethod def add_args(parser: Any) -> None: """Add command line arguments for SWE-agent configuration.""" parser.add_argument("--swe-agent-model-name", type=str, - help="Name of the agent model to use (e.g. 'gpt-4o').") + help="Name of the agent model to use (e.g. 'gpt-4o', 'ollama/llama3.2:latest').") parser.add_argument("--swe-agent-per-instance-cost-limit", type=float, - help="Per-instance cost limit for the agent model.") - + help="Per-instance cost limit for the agent model; set to 0 for local models.") + # New: local-model friendly options + parser.add_argument("--swe-agent-total-cost-limit", type=float, default=0.0, + help="Total cost limit; set to 0 for local models.") + parser.add_argument("--swe-agent-per-instance-call-limit", type=int, + help="Max LLM calls per instance (e.g., 100).") + parser.add_argument("--swe-agent-api-base", type=str, + help="Override model base URL (e.g., 'http://127.0.0.1:11434' for Ollama).") + parser.add_argument("--swe-agent-config", type=str, + help="Path to a SWE-agent YAML config (overrides defaults).") + parser.add_argument("--swe-agent-parser", type=str, choices=["thought_action", "function_call"], + help="Parsing strategy. Use 'thought_action' for local/Ollama models.") + parser.add_argument("--swe-agent-max-input-token", type=int, + help="Override max input tokens to avoid local-model warnings.") @staticmethod def parse_args(args: Any) -> Dict[str, Any]: """Parse command line arguments for SWE-agent configuration.""" return { "swe_agent_model_name": args.swe_agent_model_name, - "swe_agent_per_instance_cost_limit": args.swe_agent_per_instance_cost_limit + "swe_agent_per_instance_cost_limit": args.swe_agent_per_instance_cost_limit, + "swe_agent_total_cost_limit": args.swe_agent_total_cost_limit, + "swe_agent_per_instance_call_limit": args.swe_agent_per_instance_call_limit, + "swe_agent_api_base": args.swe_agent_api_base, + "swe_agent_config": args.swe_agent_config, + "swe_agent_parser": args.swe_agent_parser, + "swe_agent_max_input_token": args.swe_agent_max_input_token, } - def translate(self) -> None: """Execute the complete translation process using SWE-agent. @@ -158,6 +215,7 @@ def initialize_temp_repo(self) -> None: self._prepare_temp_directory() self._copy_source_to_temp() self._initialize_git_repo() + self._ensure_repro_scripts() def _prepare_temp_directory(self) -> None: """Remove existing temp directory if it exists.""" @@ -175,6 +233,23 @@ def _initialize_git_repo(self) -> None: subprocess.run(self.GIT_ADD_ALL, cwd=self._temp_repo_path, check=True) subprocess.run(self.GIT_COMMIT_INITIAL, cwd=self._temp_repo_path, check=True) + def _ensure_repro_scripts(self) -> None: + """Create a minimal reproduce script at plausible paths to avoid tool-call loops.""" + # Root-level repro script + root_repro = os.path.join(self._temp_repo_path, "reproduce_error.py") + if not os.path.exists(root_repro): + with open(root_repro, "w", encoding="utf-8") as f: + f.write('print("No repro needed yet.")\n') + + # Also create under the declared relative path (if present) since some prompts try that path + rel_path = self._dst_config.get("path") + if isinstance(rel_path, str) and rel_path.strip(): + nested_dir = os.path.join(self._temp_repo_path, rel_path) + os.makedirs(nested_dir, exist_ok=True) + nested_repro = os.path.join(nested_dir, "reproduce_error.py") + if not os.path.exists(nested_repro): + with open(nested_repro, "w", encoding="utf-8") as f: + f.write('print("No repro needed yet.")\n') def run_swe_agent(self) -> bool: """Run the SWE-agent command and apply the resulting patch.""" @@ -197,15 +272,30 @@ def run_swe_agent(self) -> bool: def _build_swe_agent_command(self) -> List[str]: """Build the SWE-agent command with all required parameters.""" - return [ + cmd = [ "sweagent", "run", f"--agent.model.name={self._swe_agent_model_name}", f"--agent.model.per_instance_cost_limit={self._swe_agent_per_instance_cost_limit}", + f"--agent.model.total_cost_limit={self._swe_agent_total_cost_limit}", f"--env.repo.path={self._temp_repo_path}", "--env.deployment.image=python", f"--problem_statement.path={self._translation_task_path}", ] + # Optional / local-model-friendly overrides + if self._swe_agent_api_base: + cmd.append(f"--agent.model.api_base={self._swe_agent_api_base}") + if self._swe_agent_parser: + cmd.append(f"--agent.tools.parse_function.type={self._swe_agent_parser}") + if self._swe_agent_max_input_token: + cmd.append(f"--agent.model.max_input_tokens={self._swe_agent_max_input_token}") + if self._swe_agent_per_instance_call_limit is not None: + cmd.append(f"--agent.model.per_instance_call_limit={self._swe_agent_per_instance_call_limit}") + if self._swe_agent_config: + cmd.extend(["--config", self._swe_agent_config]) + + return cmd + def _apply_swe_agent_patch(self) -> bool: """Find and apply the patch file generated by SWE-agent.""" print("Applying patch...") diff --git a/targets/microXOR/cuda/repo/translation_task.md b/targets/microXOR/cuda/repo/translation_task.md index ac9db36..a7d0bc2 100644 --- a/targets/microXOR/cuda/repo/translation_task.md +++ b/targets/microXOR/cuda/repo/translation_task.md @@ -1,7 +1,7 @@ -You are a helpful coding assistant. You are helping a software developer translate a codebase from the cuda execution model to the openmp-offload execution model. +You are a helpful coding assistant. You are helping a software developer translate a codebase from the cuda execution model to the omp execution model. -The codebase is called microxor. Its path is targets/microXOR/openmp-offload/repo. Given this code repository, translate the microxor codebase's cuda-specific files to the openmp-offload execution model. +The codebase is called microxor. Its path is targets/microXOR/cuda/repo. Given this code repository, translate the microxor codebase's cuda-specific files to the omp execution model. The new files should be in C++ and all old cuda files must be deleted. A new Makefile should be made to compile accordingly with the new files. -Ensure that the user can compile this code using, for example, `make SM_VERSION=sm_80 CXX_COMPILER=clang++` to build the code for a system with an NVIDIA GPU with compute capability 80 compiled with clang++. Ensure also that the command line interface after translation still works as expected, so that, for example, `./microXOR.exe 1024 32` still works to run the code with a 1024 by 1024 input matrix and a kernel with 32 times 32 threads per block. \ No newline at end of file +Ensure that the user can compile this code using, for example, `make SM_VERSION=80` to build the code for a system with an NVIDIA GPU with compute capability 80. Ensure also that the command line interface after translation still works as expected, so that, for example, `./microXOR.exe 1024 32` still works to run the code with a 1024 by 1024 input matrix and a kernel with 32 times 32 threads per block. \ No newline at end of file diff --git a/targets/nanoXOR/cuda/repo/translation_task.md b/targets/nanoXOR/cuda/repo/translation_task.md new file mode 100644 index 0000000..393de41 --- /dev/null +++ b/targets/nanoXOR/cuda/repo/translation_task.md @@ -0,0 +1,7 @@ +You are a helpful coding assistant. You are helping a software developer translate a codebase from the cuda execution model to the omp execution model. + +The codebase is called nanoxor. Its path is targets/nanoXOR/cuda/repo. Given this code repository, translate the nanoxor codebase's cuda-specific files to the omp execution model. + +The new files should be in C++ and all old cuda files must be deleted. A new Makefile should be made to compile accordingly with the new files. + +Ensure that the user can compile this code using, for example, `make SM_VERSION=80` to build the code for a system with an NVIDIA GPU with compute capability 80. Ensure also that the command line interface after translation still works as expected, so that, for example, `./nanoXOR.exe 1024 32` still works to run the code with a 1024 by 1024 input matrix and a kernel with 32 times 32 threads per block. \ No newline at end of file From 3ac817df1733cea5e73257adb857bafbb095f551 Mon Sep 17 00:00:00 2001 From: Josh Davis Date: Tue, 7 Oct 2025 10:41:40 -0400 Subject: [PATCH 02/27] Add ollama server startup in swe-agent --- .../swe_agent/swe_agent_translator.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 261f617..62b50dc 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -5,6 +5,7 @@ import shutil import subprocess import json +import time from typing import List, Optional, Dict, Any # local imports @@ -20,6 +21,7 @@ class SWEAgentTranslator(Translator): TRAJECTORIES_DIR = "trajectories" PATCH_FILENAME = "temp.patch" EXPERIMENT_METADATA_FILENAME = "experiment_metadata.json" + SERVE_CHECK_COOLDOWN = 10 # File extensions to remove from output REMOVE_EXTENSIONS = (".cu", ".cuh") @@ -103,12 +105,37 @@ def __init__( # Local runs typically disable cost tracking if self._swe_agent_per_instance_cost_limit is None: self._swe_agent_per_instance_cost_limit = 0.0 + self._launch_ollama_server() @staticmethod def _is_ollama_model(name: str) -> bool: name = (name or "").lower() return name.startswith("ollama/") or name.startswith("ollama_chat/") + + def _launch_ollama_server(self) -> None: + """Launch an Ollama server in the background.""" + # Check that ollama is installed + if not shutil.which("ollama"): + raise ValueError("Ollama is not in the path. Please install Ollama and add it to the path.") + # Early exit if ollama is already running + if subprocess.run(["ollama", "list"], capture_output=True, text=True).returncode == 0: + return + ollama_command = ["ollama", "serve", self._swe_agent_model_name] + subprocess.Popen(ollama_command) + # Check that the server is running + checking = True + while checking: + status = subprocess.run(["ollama", "list"], capture_output=True, text=True) + if status.returncode == 0: + checking = False + else: + print(f"Ollama server not ready, checking again after {self.SERVE_CHECK_COOLDOWN} seconds...") + time.sleep(self.SERVE_CHECK_COOLDOWN) + print(f"Ollama server ready.") + return + + @staticmethod def add_args(parser: Any) -> None: """Add command line arguments for SWE-agent configuration.""" From 22600f24509de632931a10b02e61358660206083 Mon Sep 17 00:00:00 2001 From: Josh Davis Date: Tue, 7 Oct 2025 10:49:03 -0400 Subject: [PATCH 03/27] Remove model name from ollama serve command, not needed and in wrong format anyway --- src/translate/swe_agent/swe_agent_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 62b50dc..2214127 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -121,7 +121,7 @@ def _launch_ollama_server(self) -> None: # Early exit if ollama is already running if subprocess.run(["ollama", "list"], capture_output=True, text=True).returncode == 0: return - ollama_command = ["ollama", "serve", self._swe_agent_model_name] + ollama_command = ["ollama", "serve"] subprocess.Popen(ollama_command) # Check that the server is running checking = True From b2c6af1c443f8e713f6fe49500f00b334f2d25b6 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Wed, 22 Oct 2025 21:47:14 -0700 Subject: [PATCH 04/27] WIP w/ adding Klaud's config suggestions --- src/translate/swe_agent/swe_agent_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 2214127..a80bbf3 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -305,7 +305,7 @@ def _build_swe_agent_command(self) -> List[str]: f"--agent.model.per_instance_cost_limit={self._swe_agent_per_instance_cost_limit}", f"--agent.model.total_cost_limit={self._swe_agent_total_cost_limit}", f"--env.repo.path={self._temp_repo_path}", - "--env.deployment.image=python", + "--config", os.path.expanduser("~/SWE-agent/config/podman.yaml"), f"--problem_statement.path={self._translation_task_path}", ] From f426764e4da6fd9f8b4027814bd213f0535d6d53 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Thu, 23 Oct 2025 11:10:44 -0400 Subject: [PATCH 05/27] Modified .gitignore to include .envrc --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5220459..ec10849 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ __pycache__ tmp* .env +.envrc .venv .request_cache_*.pkl From 9da91461a02a50738de3a56e7b71fe950a6bb3f0 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Thu, 23 Oct 2025 11:37:49 -0400 Subject: [PATCH 06/27] Cleaned up unnecessary optional SWEAgentTranslator params --- .../swe_agent/swe_agent_translator.py | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index a80bbf3..c62ca86 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -34,9 +34,6 @@ class SWEAgentTranslator(Translator): # Instance variables _swe_agent_model_name: str _swe_agent_per_instance_cost_limit: float - _swe_agent_total_cost_limit: float - _swe_agent_per_instance_call_limit: Optional[int] - _swe_agent_api_base: Optional[str] _swe_agent_config: Optional[str] _swe_agent_parser: Optional[str] _swe_agent_max_input_token: Optional[int] @@ -57,9 +54,6 @@ def __init__( hide_progress: bool = False, swe_agent_model_name: Optional[str] = None, swe_agent_per_instance_cost_limit: float = 0.06, - swe_agent_total_cost_limit: float = 0.0, - swe_agent_per_instance_call_limit: Optional[int] = None, - swe_agent_api_base: Optional[str] = None, swe_agent_config: Optional[str] = None, swe_agent_parser: Optional[str] = None, swe_agent_max_input_token: Optional[int] = None, @@ -77,9 +71,6 @@ def __init__( self._swe_agent_model_name = swe_agent_model_name or "" self._swe_agent_per_instance_cost_limit = swe_agent_per_instance_cost_limit - self._swe_agent_total_cost_limit = swe_agent_total_cost_limit - self._swe_agent_per_instance_call_limit = swe_agent_per_instance_call_limit - self._swe_agent_api_base = swe_agent_api_base self._swe_agent_config = swe_agent_config self._swe_agent_parser = swe_agent_parser self._swe_agent_max_input_token = swe_agent_max_input_token @@ -91,26 +82,16 @@ def __init__( self._output_path = os.path.join(self._output_paths[0], "repo") if self._is_ollama_model(self._swe_agent_model_name): - if not self._swe_agent_api_base: - self._swe_agent_api_base = os.getenv("OLLAMA_HOST", "http://127.0.0.1:11434") if self._swe_agent_parser is None: self._swe_agent_parser = "thought_action" - if self._swe_agent_total_cost_limit is None: - self._swe_agent_total_cost_limit = 0.0 - if self._swe_agent_per_instance_call_limit is None: - self._swe_agent_per_instance_call_limit = 100 if self._swe_agent_max_input_token is None: - # quiets 'No max input tokens found' warnings for most local models self._swe_agent_max_input_token = 4096 - # Local runs typically disable cost tracking - if self._swe_agent_per_instance_cost_limit is None: - self._swe_agent_per_instance_cost_limit = 0.0 self._launch_ollama_server() @staticmethod def _is_ollama_model(name: str) -> bool: name = (name or "").lower() - return name.startswith("ollama/") or name.startswith("ollama_chat/") + return name.startswith("ollama/") def _launch_ollama_server(self) -> None: @@ -143,15 +124,6 @@ def add_args(parser: Any) -> None: help="Name of the agent model to use (e.g. 'gpt-4o', 'ollama/llama3.2:latest').") parser.add_argument("--swe-agent-per-instance-cost-limit", type=float, help="Per-instance cost limit for the agent model; set to 0 for local models.") - # New: local-model friendly options - parser.add_argument("--swe-agent-total-cost-limit", type=float, default=0.0, - help="Total cost limit; set to 0 for local models.") - parser.add_argument("--swe-agent-per-instance-call-limit", type=int, - help="Max LLM calls per instance (e.g., 100).") - parser.add_argument("--swe-agent-api-base", type=str, - help="Override model base URL (e.g., 'http://127.0.0.1:11434' for Ollama).") - parser.add_argument("--swe-agent-config", type=str, - help="Path to a SWE-agent YAML config (overrides defaults).") parser.add_argument("--swe-agent-parser", type=str, choices=["thought_action", "function_call"], help="Parsing strategy. Use 'thought_action' for local/Ollama models.") parser.add_argument("--swe-agent-max-input-token", type=int, @@ -163,9 +135,6 @@ def parse_args(args: Any) -> Dict[str, Any]: return { "swe_agent_model_name": args.swe_agent_model_name, "swe_agent_per_instance_cost_limit": args.swe_agent_per_instance_cost_limit, - "swe_agent_total_cost_limit": args.swe_agent_total_cost_limit, - "swe_agent_per_instance_call_limit": args.swe_agent_per_instance_call_limit, - "swe_agent_api_base": args.swe_agent_api_base, "swe_agent_config": args.swe_agent_config, "swe_agent_parser": args.swe_agent_parser, "swe_agent_max_input_token": args.swe_agent_max_input_token, @@ -303,21 +272,14 @@ def _build_swe_agent_command(self) -> List[str]: "sweagent", "run", f"--agent.model.name={self._swe_agent_model_name}", f"--agent.model.per_instance_cost_limit={self._swe_agent_per_instance_cost_limit}", - f"--agent.model.total_cost_limit={self._swe_agent_total_cost_limit}", f"--env.repo.path={self._temp_repo_path}", - "--config", os.path.expanduser("~/SWE-agent/config/podman.yaml"), f"--problem_statement.path={self._translation_task_path}", ] - # Optional / local-model-friendly overrides - if self._swe_agent_api_base: - cmd.append(f"--agent.model.api_base={self._swe_agent_api_base}") if self._swe_agent_parser: cmd.append(f"--agent.tools.parse_function.type={self._swe_agent_parser}") if self._swe_agent_max_input_token: cmd.append(f"--agent.model.max_input_tokens={self._swe_agent_max_input_token}") - if self._swe_agent_per_instance_call_limit is not None: - cmd.append(f"--agent.model.per_instance_call_limit={self._swe_agent_per_instance_call_limit}") if self._swe_agent_config: cmd.extend(["--config", self._swe_agent_config]) From 355427427accc9b3690a6e6c912442bc67219a4f Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Thu, 23 Oct 2025 11:39:39 -0400 Subject: [PATCH 07/27] Updated self._swe_agent_model_name initialization in __init__ --- src/translate/swe_agent/swe_agent_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index c62ca86..cc9ca0a 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -69,7 +69,7 @@ def __init__( hide_progress=hide_progress ) - self._swe_agent_model_name = swe_agent_model_name or "" + self._swe_agent_model_name = swe_agent_model_name self._swe_agent_per_instance_cost_limit = swe_agent_per_instance_cost_limit self._swe_agent_config = swe_agent_config self._swe_agent_parser = swe_agent_parser From 7ae197742bd59f1e727bfc7492885f34a2d30756 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Thu, 23 Oct 2025 11:54:19 -0400 Subject: [PATCH 08/27] Add --swe-agent-config to add_args() --- src/translate/swe_agent/swe_agent_translator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index cc9ca0a..0f5f34d 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -124,6 +124,8 @@ def add_args(parser: Any) -> None: help="Name of the agent model to use (e.g. 'gpt-4o', 'ollama/llama3.2:latest').") parser.add_argument("--swe-agent-per-instance-cost-limit", type=float, help="Per-instance cost limit for the agent model; set to 0 for local models.") + parser.add_argument("--swe-agent-config", type=str, + help="Custom config file provided to SWE-Agent. Default config file is used if none is provided.") parser.add_argument("--swe-agent-parser", type=str, choices=["thought_action", "function_call"], help="Parsing strategy. Use 'thought_action' for local/Ollama models.") parser.add_argument("--swe-agent-max-input-token", type=int, From 8f908633878b4940966ac721f1e5f70ffd089943 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Thu, 23 Oct 2025 12:00:05 -0400 Subject: [PATCH 09/27] Delete generated translation_task files --- targets/microXOR/cuda/repo/translation_task.md | 7 ------- targets/nanoXOR/cuda/repo/translation_task.md | 7 ------- 2 files changed, 14 deletions(-) delete mode 100644 targets/microXOR/cuda/repo/translation_task.md delete mode 100644 targets/nanoXOR/cuda/repo/translation_task.md diff --git a/targets/microXOR/cuda/repo/translation_task.md b/targets/microXOR/cuda/repo/translation_task.md deleted file mode 100644 index a7d0bc2..0000000 --- a/targets/microXOR/cuda/repo/translation_task.md +++ /dev/null @@ -1,7 +0,0 @@ -You are a helpful coding assistant. You are helping a software developer translate a codebase from the cuda execution model to the omp execution model. - -The codebase is called microxor. Its path is targets/microXOR/cuda/repo. Given this code repository, translate the microxor codebase's cuda-specific files to the omp execution model. - -The new files should be in C++ and all old cuda files must be deleted. A new Makefile should be made to compile accordingly with the new files. - -Ensure that the user can compile this code using, for example, `make SM_VERSION=80` to build the code for a system with an NVIDIA GPU with compute capability 80. Ensure also that the command line interface after translation still works as expected, so that, for example, `./microXOR.exe 1024 32` still works to run the code with a 1024 by 1024 input matrix and a kernel with 32 times 32 threads per block. \ No newline at end of file diff --git a/targets/nanoXOR/cuda/repo/translation_task.md b/targets/nanoXOR/cuda/repo/translation_task.md deleted file mode 100644 index 393de41..0000000 --- a/targets/nanoXOR/cuda/repo/translation_task.md +++ /dev/null @@ -1,7 +0,0 @@ -You are a helpful coding assistant. You are helping a software developer translate a codebase from the cuda execution model to the omp execution model. - -The codebase is called nanoxor. Its path is targets/nanoXOR/cuda/repo. Given this code repository, translate the nanoxor codebase's cuda-specific files to the omp execution model. - -The new files should be in C++ and all old cuda files must be deleted. A new Makefile should be made to compile accordingly with the new files. - -Ensure that the user can compile this code using, for example, `make SM_VERSION=80` to build the code for a system with an NVIDIA GPU with compute capability 80. Ensure also that the command line interface after translation still works as expected, so that, for example, `./nanoXOR.exe 1024 32` still works to run the code with a 1024 by 1024 input matrix and a kernel with 32 times 32 threads per block. \ No newline at end of file From 5dcddc66f9485f54c9b101c7ece63be4ad7ec71b Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Mon, 27 Oct 2025 22:02:03 -0700 Subject: [PATCH 10/27] Updated launch_ollama_server() to mute Ollama startup logs --- src/translate/swe_agent/swe_agent_translator.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 0f5f34d..5bdce37 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -103,7 +103,11 @@ def _launch_ollama_server(self) -> None: if subprocess.run(["ollama", "list"], capture_output=True, text=True).returncode == 0: return ollama_command = ["ollama", "serve"] - subprocess.Popen(ollama_command) + subprocess.Popen(ollama_command, stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + stdin=subprocess.DEVNULL, + start_new_session=True, + env=env) # Check that the server is running checking = True while checking: From 1dd87f73d18f2668cbd8430edeb8c6f4c615c002 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Mon, 27 Oct 2025 22:31:11 -0700 Subject: [PATCH 11/27] Deleted unnecessary repro_script() function --- .../swe_agent/swe_agent_translator.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 5bdce37..d8bcdfd 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -217,7 +217,6 @@ def initialize_temp_repo(self) -> None: self._prepare_temp_directory() self._copy_source_to_temp() self._initialize_git_repo() - self._ensure_repro_scripts() def _prepare_temp_directory(self) -> None: """Remove existing temp directory if it exists.""" @@ -235,24 +234,6 @@ def _initialize_git_repo(self) -> None: subprocess.run(self.GIT_ADD_ALL, cwd=self._temp_repo_path, check=True) subprocess.run(self.GIT_COMMIT_INITIAL, cwd=self._temp_repo_path, check=True) - def _ensure_repro_scripts(self) -> None: - """Create a minimal reproduce script at plausible paths to avoid tool-call loops.""" - # Root-level repro script - root_repro = os.path.join(self._temp_repo_path, "reproduce_error.py") - if not os.path.exists(root_repro): - with open(root_repro, "w", encoding="utf-8") as f: - f.write('print("No repro needed yet.")\n') - - # Also create under the declared relative path (if present) since some prompts try that path - rel_path = self._dst_config.get("path") - if isinstance(rel_path, str) and rel_path.strip(): - nested_dir = os.path.join(self._temp_repo_path, rel_path) - os.makedirs(nested_dir, exist_ok=True) - nested_repro = os.path.join(nested_dir, "reproduce_error.py") - if not os.path.exists(nested_repro): - with open(nested_repro, "w", encoding="utf-8") as f: - f.write('print("No repro needed yet.")\n') - def run_swe_agent(self) -> bool: """Run the SWE-agent command and apply the resulting patch.""" command = self._build_swe_agent_command() From 9829a6a286b8120918b5ecd4d3fe853aa75cd18a Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Mon, 27 Oct 2025 22:33:13 -0700 Subject: [PATCH 12/27] Fixed minor style issue --- src/translate/swe_agent/swe_agent_translator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index d8bcdfd..2425d3f 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -103,7 +103,8 @@ def _launch_ollama_server(self) -> None: if subprocess.run(["ollama", "list"], capture_output=True, text=True).returncode == 0: return ollama_command = ["ollama", "serve"] - subprocess.Popen(ollama_command, stdout=subprocess.DEVNULL, + subprocess.Popen(ollama_command, + stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL, start_new_session=True, From 7d7b08d575befc08b077409d84dbd44b3486606e Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 28 Oct 2025 10:09:42 -0700 Subject: [PATCH 13/27] Fixed small bug in launch_ollama_server --- src/translate/swe_agent/swe_agent_translator.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 2425d3f..7120355 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -107,8 +107,7 @@ def _launch_ollama_server(self) -> None: stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL, - start_new_session=True, - env=env) + start_new_session=True) # Check that the server is running checking = True while checking: From be519bc9e566155bb301b3a0102789400a4082ac Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 4 Nov 2025 09:57:15 -0800 Subject: [PATCH 14/27] Change typo for swe-agent-parser choices --- src/translate/swe_agent/swe_agent_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 7120355..8d0430a 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -130,7 +130,7 @@ def add_args(parser: Any) -> None: help="Per-instance cost limit for the agent model; set to 0 for local models.") parser.add_argument("--swe-agent-config", type=str, help="Custom config file provided to SWE-Agent. Default config file is used if none is provided.") - parser.add_argument("--swe-agent-parser", type=str, choices=["thought_action", "function_call"], + parser.add_argument("--swe-agent-parser", type=str, choices=["thought_action", "function_calling"], help="Parsing strategy. Use 'thought_action' for local/Ollama models.") parser.add_argument("--swe-agent-max-input-token", type=int, help="Override max input tokens to avoid local-model warnings.") From f2a7eb2accded17f7c7039bbfa8697790fcbe86c Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Fri, 14 Nov 2025 15:02:17 -0800 Subject: [PATCH 15/27] Added multi-config file support and vLLM support in SWEAgentTranslator --- .../swe_agent/swe_agent_translator.py | 61 ++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 8d0430a..be71f51 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -6,7 +6,8 @@ import subprocess import json import time -from typing import List, Optional, Dict, Any +import atexit +from typing import List, Optional, Dict, Any, Union # local imports from translator import Translator @@ -34,7 +35,7 @@ class SWEAgentTranslator(Translator): # Instance variables _swe_agent_model_name: str _swe_agent_per_instance_cost_limit: float - _swe_agent_config: Optional[str] + _swe_agent_config: Optional[List[str]] _swe_agent_parser: Optional[str] _swe_agent_max_input_token: Optional[int] @@ -54,7 +55,7 @@ def __init__( hide_progress: bool = False, swe_agent_model_name: Optional[str] = None, swe_agent_per_instance_cost_limit: float = 0.06, - swe_agent_config: Optional[str] = None, + swe_agent_config: Optional[Union[str, List[str]]] = None, swe_agent_parser: Optional[str] = None, swe_agent_max_input_token: Optional[int] = None, ) -> None: @@ -71,10 +72,15 @@ def __init__( self._swe_agent_model_name = swe_agent_model_name self._swe_agent_per_instance_cost_limit = swe_agent_per_instance_cost_limit - self._swe_agent_config = swe_agent_config self._swe_agent_parser = swe_agent_parser self._swe_agent_max_input_token = swe_agent_max_input_token + # Handle a single-config file or multi-config files + if isinstance(swe_agent_config, str): + self._swe_agent_config = [swe_agent_config] + else: + self._swe_agent_config = swe_agent_config + self._temp_repo_path = self.TEMP_REPO_PATH self._translation_task_path = os.path.join( self._input_repo.path, self.TRANSLATION_TASK_FILENAME @@ -87,6 +93,8 @@ def __init__( if self._swe_agent_max_input_token is None: self._swe_agent_max_input_token = 4096 self._launch_ollama_server() + else: + self._launch_vllm_server() @staticmethod def _is_ollama_model(name: str) -> bool: @@ -128,8 +136,8 @@ def add_args(parser: Any) -> None: help="Name of the agent model to use (e.g. 'gpt-4o', 'ollama/llama3.2:latest').") parser.add_argument("--swe-agent-per-instance-cost-limit", type=float, help="Per-instance cost limit for the agent model; set to 0 for local models.") - parser.add_argument("--swe-agent-config", type=str, - help="Custom config file provided to SWE-Agent. Default config file is used if none is provided.") + parser.add_argument("--swe-agent-config", action="append", + help="May be specified multiple times; default config file is used if none is provided.") parser.add_argument("--swe-agent-parser", type=str, choices=["thought_action", "function_calling"], help="Parsing strategy. Use 'thought_action' for local/Ollama models.") parser.add_argument("--swe-agent-max-input-token", type=int, @@ -268,7 +276,8 @@ def _build_swe_agent_command(self) -> List[str]: if self._swe_agent_max_input_token: cmd.append(f"--agent.model.max_input_tokens={self._swe_agent_max_input_token}") if self._swe_agent_config: - cmd.extend(["--config", self._swe_agent_config]) + for cfg in self._swe_agent_config: + cmd.extend(["--config", cfg]) return cmd @@ -393,3 +402,41 @@ def cleanup_temp_repo(self) -> None: except OSError as e: print(f"Error cleaning up temporary repository: {e}") # Don't raise here as this is cleanup code + + def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] = None): + """Launch a vLLM server in the background using the Python environment directory + provided. + """ + # Early exit if vLLM server is already running + if subprocess.run(["curl", "http://127.0.0.1:8000/health"], capture_output=True, + text=True, check=False).returncode == 0: + return None + py_executable = os.path.join(environment_path, "bin", "python") + vllm_command = [py_executable, "-m", "vllm.entrypoints.openai.api_server", + "--model", self._model, + "--host", "127.0.0.1", + "--port", "8000"] + vllm_api_key = os.getenv("VLLM_API_KEY") + if vllm_api_key is not None: + vllm_command.extend(["--api-key", vllm_api_key]) + if self._is_reasoning_model(self._model): + vllm_command.extend(["--enable-reasoning", "--reasoning-parser", "deepseek_r1"]) + if yaml_config: + vllm_command.extend(["--config", yaml_config]) + print("Full vLLM subprocess command: %s", ' '.join(vllm_command)) + vllm_server = subprocess.Popen(vllm_command) + # Ping the server until it is ready at the health endpoint + checking, num_attempts = True, 0 + while checking and num_attempts < self._MAX_SERVE_CHECK_ATTEMPTS: + status = subprocess.run(["curl", "http://127.0.0.1:8000/health"], capture_output=True, + text=True, check=False) + if status.returncode == 0: + checking = False + else: + print("VLLM server not ready, checking again after %d seconds...", + self._SERVE_CHECK_COOLDOWN) + time.sleep(self._SERVE_CHECK_COOLDOWN) + num_attempts += 1 + atexit.register(vllm_server.terminate) + print("VLLM server ready.") + return vllm_server From ab5de16f445f43c00648e0c32c5a6bb14ceacc88 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Sat, 15 Nov 2025 14:58:32 -0800 Subject: [PATCH 16/27] Fix bugs related to launching vllm server in SWEAgentTranslator --- .../swe_agent/swe_agent_translator.py | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index be71f51..958129a 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -23,6 +23,7 @@ class SWEAgentTranslator(Translator): PATCH_FILENAME = "temp.patch" EXPERIMENT_METADATA_FILENAME = "experiment_metadata.json" SERVE_CHECK_COOLDOWN = 10 + _MAX_SERVE_CHECK_ATTEMPTS = 100 # File extensions to remove from output REMOVE_EXTENSIONS = (".cu", ".cuh") @@ -58,6 +59,8 @@ def __init__( swe_agent_config: Optional[Union[str, List[str]]] = None, swe_agent_parser: Optional[str] = None, swe_agent_max_input_token: Optional[int] = None, + vllm_environment: Optional[str] = None, + vllm_yaml_config: Optional[str] = None, ) -> None: super().__init__( input_repo, @@ -87,14 +90,21 @@ def __init__( ) self._output_path = os.path.join(self._output_paths[0], "repo") + self._vllm_environment = vllm_environment + self._vllm_yaml_config = vllm_yaml_config + if self._is_ollama_model(self._swe_agent_model_name): if self._swe_agent_parser is None: self._swe_agent_parser = "thought_action" if self._swe_agent_max_input_token is None: self._swe_agent_max_input_token = 4096 self._launch_ollama_server() + else: - self._launch_vllm_server() + if self._vllm_environment: + self._launch_vllm_server(self._vllm_environment, self._vllm_yaml_config) + else: + print("Warning: vLLM environment not provided; assuming external vLLM server is running.") @staticmethod def _is_ollama_model(name: str) -> bool: @@ -142,6 +152,10 @@ def add_args(parser: Any) -> None: help="Parsing strategy. Use 'thought_action' for local/Ollama models.") parser.add_argument("--swe-agent-max-input-token", type=int, help="Override max input tokens to avoid local-model warnings.") + parser.add_argument("--vllm-environment", type=str, + help="Path to the Python environment that has vLLM installed (e.g. ~/pssg-venv).") + parser.add_argument("--vllm-yaml-config", type=str, + help="Path to vLLM YAML config file to pass via --config.") @staticmethod def parse_args(args: Any) -> Dict[str, Any]: @@ -152,6 +166,8 @@ def parse_args(args: Any) -> Dict[str, Any]: "swe_agent_config": args.swe_agent_config, "swe_agent_parser": args.swe_agent_parser, "swe_agent_max_input_token": args.swe_agent_max_input_token, + "vllm_environment": args.vllm_environment, + "vllm_yaml_config": args.vllm_yaml_config, } def translate(self) -> None: @@ -413,14 +429,12 @@ def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] return None py_executable = os.path.join(environment_path, "bin", "python") vllm_command = [py_executable, "-m", "vllm.entrypoints.openai.api_server", - "--model", self._model, + "--model", (self._swe_agent_model_name or "").split("/")[-1], "--host", "127.0.0.1", "--port", "8000"] vllm_api_key = os.getenv("VLLM_API_KEY") if vllm_api_key is not None: vllm_command.extend(["--api-key", vllm_api_key]) - if self._is_reasoning_model(self._model): - vllm_command.extend(["--enable-reasoning", "--reasoning-parser", "deepseek_r1"]) if yaml_config: vllm_command.extend(["--config", yaml_config]) print("Full vLLM subprocess command: %s", ' '.join(vllm_command)) From 1bf8a375e55263af0992d75d30cf579d98748660 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Sat, 15 Nov 2025 15:35:02 -0800 Subject: [PATCH 17/27] Fixed small errors w/ vLLM setup for SWE-agent on perlmutter --- src/translate/swe_agent/swe_agent_translator.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 958129a..3c53652 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -437,7 +437,7 @@ def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] vllm_command.extend(["--api-key", vllm_api_key]) if yaml_config: vllm_command.extend(["--config", yaml_config]) - print("Full vLLM subprocess command: %s", ' '.join(vllm_command)) + print("Full vLLM subprocess command:", " ".join(vllm_command)) vllm_server = subprocess.Popen(vllm_command) # Ping the server until it is ready at the health endpoint checking, num_attempts = True, 0 @@ -447,9 +447,8 @@ def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] if status.returncode == 0: checking = False else: - print("VLLM server not ready, checking again after %d seconds...", - self._SERVE_CHECK_COOLDOWN) - time.sleep(self._SERVE_CHECK_COOLDOWN) + print(f"VLLM server not ready, checking again after {self.SERVE_CHECK_COOLDOWN} seconds...") + time.sleep(self.SERVE_CHECK_COOLDOWN) num_attempts += 1 atexit.register(vllm_server.terminate) print("VLLM server ready.") From 24a4cd709698cdd13d474be7e3396e1150ee053f Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Sat, 15 Nov 2025 15:37:43 -0800 Subject: [PATCH 18/27] Fixed issue w/ removing openai part of model for vllm --- src/translate/swe_agent/swe_agent_translator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 3c53652..7dd0152 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -429,7 +429,7 @@ def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] return None py_executable = os.path.join(environment_path, "bin", "python") vllm_command = [py_executable, "-m", "vllm.entrypoints.openai.api_server", - "--model", (self._swe_agent_model_name or "").split("/")[-1], + "--model", self._swe_agent_model_name, "--host", "127.0.0.1", "--port", "8000"] vllm_api_key = os.getenv("VLLM_API_KEY") From 53a60f9b624a1ede81659b9c733a9500989f4f0b Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Wed, 3 Dec 2025 08:46:42 -0800 Subject: [PATCH 19/27] WIP Klaud's suggestions --- src/translate/swe_agent/swe_agent_translator.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 7dd0152..cf2f344 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -428,10 +428,18 @@ def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] text=True, check=False).returncode == 0: return None py_executable = os.path.join(environment_path, "bin", "python") - vllm_command = [py_executable, "-m", "vllm.entrypoints.openai.api_server", - "--model", self._swe_agent_model_name, - "--host", "127.0.0.1", - "--port", "8000"] + served_model_name = self._swe_agent_model_name + if "/" in served_model_name: + served_model_name = served_model_name.split("/", 1)[1] + vllm_command = [ + py_executable, "-m", "vllm.entrypoints.openai.api_server", + "--model", self._swe_agent_model_name, + "--tool-call-parser", "openai", + "--enable-auto-tool-choice", + "--reasoning-parser", "openai_gptoss", + "--host", "127.0.0.1", + "--port", "8000", + ] vllm_api_key = os.getenv("VLLM_API_KEY") if vllm_api_key is not None: vllm_command.extend(["--api-key", vllm_api_key]) From 66e51e556a11b0cc463ff8744e9e08f39308b027 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Wed, 3 Dec 2025 11:10:51 -0800 Subject: [PATCH 20/27] Made changes w/ Josh and Klaud WIP --- src/translate/swe_agent/swe_agent_translator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index cf2f344..f471b44 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -281,12 +281,14 @@ def _build_swe_agent_command(self) -> List[str]: """Build the SWE-agent command with all required parameters.""" cmd = [ "sweagent", "run", - f"--agent.model.name={self._swe_agent_model_name}", - f"--agent.model.per_instance_cost_limit={self._swe_agent_per_instance_cost_limit}", f"--env.repo.path={self._temp_repo_path}", f"--problem_statement.path={self._translation_task_path}", ] + if self._swe_agent_model_name: + cmd.append(f"--agent.model.name={self._swe_agent_model_name}") + if self._swe_agent_per_instance_cost_limit: + cmd.append(f"--agent.model.per_instance_cost_limit={self._swe_agent_per_instance_cost_limit}") if self._swe_agent_parser: cmd.append(f"--agent.tools.parse_function.type={self._swe_agent_parser}") if self._swe_agent_max_input_token: @@ -428,12 +430,8 @@ def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] text=True, check=False).returncode == 0: return None py_executable = os.path.join(environment_path, "bin", "python") - served_model_name = self._swe_agent_model_name - if "/" in served_model_name: - served_model_name = served_model_name.split("/", 1)[1] vllm_command = [ py_executable, "-m", "vllm.entrypoints.openai.api_server", - "--model", self._swe_agent_model_name, "--tool-call-parser", "openai", "--enable-auto-tool-choice", "--reasoning-parser", "openai_gptoss", @@ -441,6 +439,8 @@ def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] "--port", "8000", ] vllm_api_key = os.getenv("VLLM_API_KEY") + if self._swe_agent_model_name is not None: + vllm_command.extend(["--model", self._swe_agent_model_name]) if vllm_api_key is not None: vllm_command.extend(["--api-key", vllm_api_key]) if yaml_config: From 9ffd703f31d1b3e6d30fb626efed83c1abc74b19 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 16 Dec 2025 19:10:47 -0800 Subject: [PATCH 21/27] Updated prompt for SWE-agent --- src/translate/swe_agent/swe_agent_translator.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index f471b44..bd7ccb2 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -18,6 +18,7 @@ class SWEAgentTranslator(Translator): # Constants TEMP_REPO_PATH = "/tmp/temp_sweagent_repo" + CONTAINER_REPO_PATH = "/temp_sweagent_repo" TRANSLATION_TASK_FILENAME = "translation_task.md" TRAJECTORIES_DIR = "trajectories" PATCH_FILENAME = "temp.patch" @@ -221,12 +222,13 @@ def _create_translation_task_content(self) -> str: f"You are a helpful coding assistant. You are helping a software developer translate a " f"codebase from the {self._src_model} execution model to the {self._dst_model} execution " f"model.\n\n" - f"The codebase is called {data['app']}. Its path is {data['path']}. Given this code " + f"The codebase is called {data['app']}. Its path is {self.CONTAINER_REPO_PATH}. Given this code " f"repository, translate the {data['app']} codebase's {self._src_model}-specific files to " f"the {self._dst_model} execution model.\n\n" f"The new files should be in {data['filename_desc']} and all old {self._src_model} files " - f"must be deleted. A new {data['build_filename']} should be made to compile accordingly " - f"with the new files.\n\n" + f"must be deleted. You may use standard command-line tools (e.g., the `rm` command) to " + f"remove obsolete {self._src_model}-specific files. A new {data['build_filename']} should " + f"be made to compile accordingly with the new files.\n\n" f"Ensure that the user can compile this code using, for example, `{data['ex_build_cmd']}` " f"to build the code for {data['ex_build_desc']}. Ensure also that the command line " f"interface after translation still works as expected, so that, for example, " From 6062bdcf25218068164f7d933bb761a46023fcb9 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 16 Dec 2025 22:17:16 -0500 Subject: [PATCH 22/27] Added temporary Makefile fix --- .../swe_agent/swe_agent_translator.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index bd7ccb2..be4462c 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -7,6 +7,7 @@ import json import time import atexit +from pathlib import Path from typing import List, Optional, Dict, Any, Union # local imports @@ -192,6 +193,7 @@ def _execute_translation_workflow(self) -> None: self.initialize_temp_repo() if self.run_swe_agent(): + self._fix_makefile_tabs_and_duplicates() print("Saving translated output...") self.save_output(self._output_path) self.remove_unnecessary_output_files() @@ -378,6 +380,36 @@ def _remove_files_by_extension(self, directory: str, extensions: tuple) -> None: file_path = os.path.join(root, file) os.remove(file_path) + def _fix_makefile_tabs_and_duplicates(self) -> None: + makefile = Path(self._temp_repo_path) / "Makefile" + if not makefile.exists(): + return + + lines = makefile.read_text(encoding="utf-8", errors="replace").splitlines(True) + + # 1) Remove exact duplicate lines (preserve order) + print("Removing duplicate lines in the Makefile...") + seen = set() + duplicates = [] + for line in lines: + if line not in seen: + seen.add(line) + duplicates.append(line) + lines = duplicates + + # 2) Enforce Makefile tab rules + print("Fixing Makefile tabs...") + i = 0 + while i < len(lines) - 1: + curr = lines[i] + nxt = lines[i + 1] + + if ":" in curr: + if nxt.strip() and not nxt.startswith("\t") and not nxt.lstrip().startswith("#"): + lines[i + 1] = "\t" + nxt + i += 1 + + makefile.write_text("".join(lines), encoding="utf-8") def write_experiment_metadata(self) -> None: """Write experiment metadata to a JSON file in the output directory.""" From 10916b4d4efab98e454c05536ab6470dd354227c Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 16 Dec 2025 22:56:50 -0500 Subject: [PATCH 23/27] Update Makefile fix in SWEAgentTranslator --- src/translate/swe_agent/swe_agent_translator.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index be4462c..15096bb 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -401,10 +401,13 @@ def _fix_makefile_tabs_and_duplicates(self) -> None: print("Fixing Makefile tabs...") i = 0 while i < len(lines) - 1: - curr = lines[i] + curr = lines[i].lstrip() nxt = lines[i + 1] - if ":" in curr: + is_rule = ":" in curr + is_conditional = curr.startswith("ifneq") or curr.startswith("else") + + if is_rule or is_conditional: if nxt.strip() and not nxt.startswith("\t") and not nxt.lstrip().startswith("#"): lines[i + 1] = "\t" + nxt i += 1 From 1372b9308c812fdec4c929c3c766dd594cfd4b6b Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 16 Dec 2025 23:20:20 -0500 Subject: [PATCH 24/27] Fix conditional part of fixing Makefiles --- src/translate/swe_agent/swe_agent_translator.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/translate/swe_agent/swe_agent_translator.py b/src/translate/swe_agent/swe_agent_translator.py index 15096bb..0acb49a 100644 --- a/src/translate/swe_agent/swe_agent_translator.py +++ b/src/translate/swe_agent/swe_agent_translator.py @@ -405,8 +405,14 @@ def _fix_makefile_tabs_and_duplicates(self) -> None: nxt = lines[i + 1] is_rule = ":" in curr - is_conditional = curr.startswith("ifneq") or curr.startswith("else") - + is_conditional = curr.startswith(( + "ifeq", + "ifneq", + "ifdef", + "ifndef", + "else" + )) + if is_rule or is_conditional: if nxt.strip() and not nxt.startswith("\t") and not nxt.lstrip().startswith("#"): lines[i + 1] = "\t" + nxt From 51164689fa41b791c52045ff6c5f180c241ac178 Mon Sep 17 00:00:00 2001 From: Josh Davis Date: Fri, 6 Feb 2026 14:30:10 -0500 Subject: [PATCH 25/27] Add ninja dep for Kokkos --- config/gesserit-config.json | 1 + config/perlmutter-config.json | 1 + config/zaratan-config.json | 1 + targets/XSBench/kokkos/target.json | 2 +- targets/llm.c/kokkos/target.json | 2 +- targets/microXOR/kokkos/target.json | 2 +- targets/microXORh/kokkos/target.json | 2 +- targets/nanoXOR/kokkos/target.json | 2 +- 8 files changed, 8 insertions(+), 5 deletions(-) diff --git a/config/gesserit-config.json b/config/gesserit-config.json index c25a18a..15b5ded 100644 --- a/config/gesserit-config.json +++ b/config/gesserit-config.json @@ -3,6 +3,7 @@ "llvm": "", "cuda": "", "kokkos": "", + "ninja": "", "sm": "86", "exec_check": "ncu", "exec_check_fail_text": "==WARNING== No kernels were profiled." diff --git a/config/perlmutter-config.json b/config/perlmutter-config.json index d2d6dc5..e3bf83f 100644 --- a/config/perlmutter-config.json +++ b/config/perlmutter-config.json @@ -3,6 +3,7 @@ "llvm": "module load PrgEnv-llvm", "cuda": "module load cudatoolkit", "kokkos": "module load kokkos-gpu", + "ninja": "", "sm": "80", "exec_check": "ncu", "exec_check_fail_text": "==WARNING== No kernels were profiled." diff --git a/config/zaratan-config.json b/config/zaratan-config.json index cd4affe..1d3900a 100644 --- a/config/zaratan-config.json +++ b/config/zaratan-config.json @@ -3,6 +3,7 @@ "llvm": "spack load llvm && export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/scratch/zt1/project/bhatele-lab/user/jhdavis/spack-install/linux-rhel8-zen2/gcc-11.3.0/llvm-19.1.7-owz26zzxphj6x4xfgxsyxgfhvktvs4kd/lib/x86_64-unknown-linux-gnu/", "cuda": "module load cuda/gcc/11.3.0/zen2/12.3.0", "kokkos": "spack load kokkos", + "ninja": "spack load ninja", "sm": "80", "exec_check": "~/llms4hpc/code-translation/src/drivers/exec-check.sh", "exec_check_fail_text": "No GPU kernels launched!!" diff --git a/targets/XSBench/kokkos/target.json b/targets/XSBench/kokkos/target.json index bbade92..446358c 100644 --- a/targets/XSBench/kokkos/target.json +++ b/targets/XSBench/kokkos/target.json @@ -2,7 +2,7 @@ "app": "xsbench", "model": "kokkos", "path": "targets/XSBench/kokkos/repo", - "dependencies": ["gnu", "cuda", "kokkos"], + "dependencies": ["gnu", "cuda", "kokkos", "ninja"], "build_commands_debug": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_commands_perf": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_timeout": 120, diff --git a/targets/llm.c/kokkos/target.json b/targets/llm.c/kokkos/target.json index f92feb8..f6ef533 100644 --- a/targets/llm.c/kokkos/target.json +++ b/targets/llm.c/kokkos/target.json @@ -2,7 +2,7 @@ "app": "llm.c", "model": "kokkos", "path": "targets/llm.c/kokkos/repo", - "dependencies": ["gnu", "cuda", "kokkos"], + "dependencies": ["gnu", "cuda", "kokkos", "ninja"], "setup_commands": ["cp $SCRATCH/llmc_inputs/*.bin ."], "build_commands_debug": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_commands_perf": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", diff --git a/targets/microXOR/kokkos/target.json b/targets/microXOR/kokkos/target.json index bdd37b2..f4f072a 100644 --- a/targets/microXOR/kokkos/target.json +++ b/targets/microXOR/kokkos/target.json @@ -2,7 +2,7 @@ "app": "microxor", "model": "kokkos", "path": "targets/microXOR/kokkos/repo", - "dependencies": ["gnu", "cuda", "kokkos"], + "dependencies": ["gnu", "cuda", "kokkos", "ninja"], "build_commands_debug": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_commands_perf": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_timeout": 120, diff --git a/targets/microXORh/kokkos/target.json b/targets/microXORh/kokkos/target.json index bc2f65d..d4fd087 100644 --- a/targets/microXORh/kokkos/target.json +++ b/targets/microXORh/kokkos/target.json @@ -2,7 +2,7 @@ "app": "microxorh", "model": "kokkos", "path": "targets/microXORh/kokkos/repo", - "dependencies": ["gnu", "cuda", "kokkos"], + "dependencies": ["gnu", "cuda", "kokkos", "ninja"], "build_commands_debug": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_commands_perf": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_timeout": 120, diff --git a/targets/nanoXOR/kokkos/target.json b/targets/nanoXOR/kokkos/target.json index ab42118..62decf1 100644 --- a/targets/nanoXOR/kokkos/target.json +++ b/targets/nanoXOR/kokkos/target.json @@ -2,7 +2,7 @@ "app": "nanoxor", "model": "kokkos", "path": "targets/nanoXOR/kokkos/repo", - "dependencies": ["gnu", "cuda", "kokkos"], + "dependencies": ["gnu", "cuda", "kokkos", "ninja"], "build_commands_debug": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_commands_perf": "cmake -DKOKKOS_BACKEND=CUDA -DCMAKE_CXX_COMPILER=g++ -GNinja -Bbuild . && cmake --build build/", "build_timeout": 120, From 69fed2ecbd3b5de919678d1725bcd0c708613551 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Tue, 3 Mar 2026 13:42:19 -0500 Subject: [PATCH 26/27] Codex implementation WIP --- src/translate/codex/__init__.py | 0 src/translate/codex/codex_translator.py | 412 ++++++++++++++++++++++++ src/translate/translate.py | 9 +- 3 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 src/translate/codex/__init__.py create mode 100644 src/translate/codex/codex_translator.py diff --git a/src/translate/codex/__init__.py b/src/translate/codex/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/translate/codex/codex_translator.py b/src/translate/codex/codex_translator.py new file mode 100644 index 0000000..074044c --- /dev/null +++ b/src/translate/codex/codex_translator.py @@ -0,0 +1,412 @@ +""" Class that invokes Codex CLI to perform code translation. +""" +# std imports +import os +import shutil +import subprocess +import json +import time +import atexit +from pathlib import Path +from typing import List, Optional, Dict, Any + +# local imports +from translator import Translator +from repo import Repo + + +class CodexTranslator(Translator): + """Translator that uses OpenAI Codex CLI to perform code translation.""" + + # Constants + TEMP_REPO_PATH = "/tmp/temp_codex_repo" + CONTAINER_REPO_PATH = "/temp_codex_repo" + TRANSLATION_TASK_FILENAME = "translation_task.md" + EXPERIMENT_METADATA_FILENAME = "experiment_metadata.json" + SERVE_CHECK_COOLDOWN = 10 + _MAX_SERVE_CHECK_ATTEMPTS = 100 + VLLM_HOST = "127.0.0.1" + VLLM_PORT = 8000 + + # File extensions to remove from output + REMOVE_EXTENSIONS = (".cu", ".cuh") + + # Git commands + GIT_INIT = ["git", "init"] + GIT_ADD_ALL = ["git", "add", "."] + GIT_COMMIT_INITIAL = ["git", "commit", "-m", "Initial commit"] + + # Instance variables + _codex_model_name: Optional[str] + _vllm_environment: Optional[str] + _vllm_yaml_config: Optional[str] + + _temp_repo_path: str + _translation_task_path: str + _output_path: str + _vllm_launched_from_python: bool + + def __init__( + self, + input_repo: Repo, + output_repos: List[os.PathLike], + src_model: str, + dst_model: str, + dst_config: Dict[str, Any], + log_interactions: bool = False, + dry: bool = False, + hide_progress: bool = False, + codex_model_name: Optional[str] = None, + codex_vllm_environment: Optional[str] = None, + codex_vllm_yaml_config: Optional[str] = None, + ) -> None: + super().__init__( + input_repo, + output_repos, + src_model, + dst_model, + dst_config, + log_interactions=log_interactions, + dry=dry, + hide_progress=hide_progress, + ) + + self._codex_model_name = codex_model_name + self._vllm_environment = codex_vllm_environment + self._vllm_yaml_config = codex_vllm_yaml_config + self._vllm_launched_from_python = False + + self._temp_repo_path = self.TEMP_REPO_PATH + self._translation_task_path = os.path.join( + self._input_repo.path, self.TRANSLATION_TASK_FILENAME + ) + self._output_path = os.path.join(self._output_paths[0], "repo") + + if self._vllm_environment: + self._launch_vllm_server(self._vllm_environment, self._vllm_yaml_config) + self._vllm_launched_from_python = True + else: + print("Warning: --codex-vllm-environment not provided; assuming external vLLM server is running.") + + @staticmethod + def add_args(parser: Any) -> None: + """Add command line arguments for Codex configuration.""" + parser.add_argument("--codex-model-name", type=str, + help="Model name to pass to Codex (e.g. 'openai/gpt-oss-120b').") + parser.add_argument("--codex-vllm-environment", type=str, + help="Path to the Python environment that has vLLM installed (e.g. ~/pssg-venv).") + parser.add_argument("--codex-vllm-yaml-config", type=str, + help="Path to vLLM YAML config file to pass via --config.") + + @staticmethod + def parse_args(args: Any) -> Dict[str, Any]: + """Parse command line arguments for Codex configuration.""" + return { + "codex_model_name": args.codex_model_name, + "codex_vllm_environment": args.codex_vllm_environment, + "codex_vllm_yaml_config": args.codex_vllm_yaml_config, + } + + def translate(self) -> None: + """Execute the complete translation process using Codex CLI. + + The process includes: + 1. Generate translation task + 2. Initialize temporary repository + 3. Run Codex and capture in-place file changes + 4. Save translated output + 5. Clean up and write metadata + """ + try: + self._execute_translation_workflow() + finally: + self.cleanup_temp_repo() + + def _execute_translation_workflow(self) -> None: + """Execute the main translation workflow steps.""" + self.generate_translation_task() + self.initialize_temp_repo() + + if self.run_codex(): + self._fix_makefile_tabs_and_duplicates() + print("Saving translated output...") + self.save_output(self._output_path) + self.remove_unnecessary_output_files() + self.write_experiment_metadata() + else: + print("Translation failed.") + + def generate_translation_task(self) -> None: + """Generate the translation task file for Codex.""" + print("Generating translation task...") + + translation_task = self._create_translation_task_content() + + try: + with open(self._translation_task_path, "w", encoding="utf-8") as f: + f.write(translation_task) + print(f"Translation task generated: {self._translation_task_path}") + except IOError as e: + print(f"Error writing translation task: {e}") + raise + + def _create_translation_task_content(self) -> str: + """Create the content for the translation task file.""" + data = self._dst_config + + prompt = ( + f"You are a helpful coding assistant. You are helping a software developer translate a " + f"codebase from the {self._src_model} execution model to the {self._dst_model} execution " + f"model.\n\n" + f"The codebase is called {data['app']}. Its path is {self.TEMP_REPO_PATH}. Given this code " + f"repository, translate the {data['app']} codebase's {self._src_model}-specific files to " + f"the {self._dst_model} execution model.\n\n" + f"The new files should be in {data['filename_desc']} and all old {self._src_model} files " + f"must be deleted. You may use standard command-line tools (e.g., the `rm` command) to " + f"remove obsolete {self._src_model}-specific files. A new {data['build_filename']} should " + f"be made to compile accordingly with the new files.\n\n" + f"Ensure that the user can compile this code using, for example, `{data['ex_build_cmd']}` " + f"to build the code for {data['ex_build_desc']}. Ensure also that the command line " + f"interface after translation still works as expected, so that, for example, " + f"`{data['ex_run_cmd']}` still works to run the code with {data['ex_run_desc']}." + ) + return prompt.strip() + + def initialize_temp_repo(self) -> None: + """Initialize the temporary repository and perform initial Git setup.""" + print("Initializing temporary Git repository...") + self._prepare_temp_directory() + self._copy_source_to_temp() + self._initialize_git_repo() + + def _prepare_temp_directory(self) -> None: + """Remove existing temp directory if it exists.""" + if os.path.exists(self._temp_repo_path): + print("The temporary repository exists. Removing the repository...") + shutil.rmtree(self._temp_repo_path) + + def _copy_source_to_temp(self) -> None: + """Copy the original repository to the temporary directory.""" + shutil.copytree(self._input_repo.path, self._temp_repo_path, dirs_exist_ok=True) + + def _initialize_git_repo(self) -> None: + """Initialize Git repository and make initial commit.""" + subprocess.run(self.GIT_INIT, cwd=self._temp_repo_path, check=True) + subprocess.run(self.GIT_ADD_ALL, cwd=self._temp_repo_path, check=True) + subprocess.run(self.GIT_COMMIT_INITIAL, cwd=self._temp_repo_path, check=True) + + def run_codex(self) -> bool: + """Run the Codex CLI command. Codex modifies files in-place.""" + try: + with open(self._translation_task_path, "r", encoding="utf-8") as f: + prompt = f.read() + except IOError as e: + print(f"Error reading translation task: {e}") + return False + + command = self._build_codex_command(prompt) + env = self._build_codex_env() + print(f"Running Codex command: {' '.join(command[:4])} ...") + + try: + subprocess.run(command, text=True, cwd=self._temp_repo_path, env=env, check=True) + print("Codex command executed successfully.") + return True + except Exception as e: + print(f"An error occurred running Codex: {e}") + return False + + def _build_codex_command(self, prompt: str) -> List[str]: + """Build the Codex CLI command with all required parameters.""" + cmd = [ + "codex", "exec", prompt, + "--sandbox", "workspace-write", + "--ask-for-approval", "never", + ] + if self._codex_model_name: + cmd.extend(["--model", self._codex_model_name]) + return cmd + + def _build_codex_env(self) -> dict: + """Build the subprocess environment for the Codex command.""" + env = os.environ.copy() + if self._vllm_launched_from_python: + base_url = f"http://{self.VLLM_HOST}:{self.VLLM_PORT}/v1" + # Set both names: Node.js SDK (Codex) reads OPENAI_BASE_URL; + # some tools also check OPENAI_API_BASE. + env["OPENAI_BASE_URL"] = base_url + env["OPENAI_API_BASE"] = base_url + if "OPENAI_API_KEY" not in env: + env["OPENAI_API_KEY"] = "dummy-local-key" + # Prevent interactive pagers from blocking Codex when it runs + # git log, man, or other pager-triggering commands internally. + env.setdefault("PAGER", "cat") + env.setdefault("MANPAGER", "cat") + env.setdefault("GIT_PAGER", "cat") + env.setdefault("LESS", "-R") + return env + + def save_output(self, output_dir: str) -> None: + """Copy the contents of the temporary repository to the final output directory. + + Removes the .git directory to prepare for adding to the results repository. + """ + try: + if os.path.exists(output_dir): + shutil.rmtree(output_dir) + shutil.copytree(self._temp_repo_path, output_dir, dirs_exist_ok=True) + + # Remove .git directory + git_dir = os.path.join(output_dir, ".git") + if os.path.exists(git_dir): + shutil.rmtree(git_dir) + except (OSError, shutil.Error) as e: + print(f"Error saving output: {e}") + raise + + def remove_unnecessary_output_files(self) -> None: + """Remove unnecessary files (any .cu or .cuh files) from the output.""" + print(f"Cleaning the output repository: {self._output_path}") + + try: + self._remove_files_by_extension(self._output_path, self.REMOVE_EXTENSIONS) + print(f"Finished cleaning the output repository: {self._output_path}") + except OSError as e: + print(f"Error cleaning output files: {e}") + raise + + def _remove_files_by_extension(self, directory: str, extensions: tuple) -> None: + """Remove files with specified extensions from a directory tree.""" + for root, _, files in os.walk(directory): + for file in files: + if file.endswith(extensions): + file_path = os.path.join(root, file) + os.remove(file_path) + + def _fix_makefile_tabs_and_duplicates(self) -> None: + makefile = Path(self._temp_repo_path) / "Makefile" + if not makefile.exists(): + return + + lines = makefile.read_text(encoding="utf-8", errors="replace").splitlines(True) + + # 1) Remove exact duplicate lines (preserve order) + print("Removing duplicate lines in the Makefile...") + seen = set() + duplicates = [] + for line in lines: + if line not in seen: + seen.add(line) + duplicates.append(line) + lines = duplicates + + # 2) Enforce Makefile tab rules + print("Fixing Makefile tabs...") + i = 0 + while i < len(lines) - 1: + curr = lines[i].lstrip() + nxt = lines[i + 1] + + is_rule = ":" in curr + is_conditional = curr.startswith(( + "ifeq", + "ifneq", + "ifdef", + "ifndef", + "else" + )) + + if is_rule or is_conditional: + if nxt.strip() and not nxt.startswith("\t") and not nxt.lstrip().startswith("#"): + lines[i + 1] = "\t" + nxt + i += 1 + + makefile.write_text("".join(lines), encoding="utf-8") + + def write_experiment_metadata(self) -> None: + """Write experiment metadata to a JSON file in the output directory.""" + exp_meta_fpath = os.path.join(self._output_path, "..", self.EXPERIMENT_METADATA_FILENAME) + + try: + os.makedirs(os.path.dirname(exp_meta_fpath), exist_ok=True) + + metadata = self._create_experiment_metadata() + + with open(exp_meta_fpath, "w", encoding="utf-8") as f: + json.dump(metadata, f, indent=4) + + print(f"Experiment metadata written to {exp_meta_fpath}.") + except (OSError, json.JSONEncodeError) as e: + print(f"Error writing experiment metadata: {e}") + raise + + def _create_experiment_metadata(self) -> Dict[str, Any]: + """Create the experiment metadata dictionary.""" + output_number = int(self._output_path.split("/")[-2][7:]) + + return { + "app": self._dst_config["app"], + "prompt_strategy": "Codex", + "llm_name": self._codex_model_name, + "source_model": self._src_model, + "dest_model": self._dst_model, + "output_number": output_number, + "path": self._output_path, + } + + def cleanup_temp_repo(self) -> None: + """Remove the temporary repository.""" + print("Cleaning up temporary repository...") + + try: + if os.path.exists(self._temp_repo_path): + shutil.rmtree(self._temp_repo_path) + print("Temporary repository cleaned up.") + except OSError as e: + print(f"Error cleaning up temporary repository: {e}") + # Don't raise here as this is cleanup code + + def _launch_vllm_server(self, environment_path: str, yaml_config: Optional[str] = None): + """Launch a vLLM server in the background using the Python environment directory + provided. + """ + # Early exit if vLLM server is already running + if subprocess.run( + ["curl", f"http://{self.VLLM_HOST}:{self.VLLM_PORT}/health"], + capture_output=True, text=True, check=False + ).returncode == 0: + return None + py_executable = os.path.join(environment_path, "bin", "python") + vllm_command = [ + py_executable, "-m", "vllm.entrypoints.openai.api_server", + "--tool-call-parser", "openai", + "--enable-auto-tool-choice", + "--reasoning-parser", "openai_gptoss", + "--host", self.VLLM_HOST, + "--port", str(self.VLLM_PORT), + ] + vllm_api_key = os.getenv("VLLM_API_KEY") + if self._codex_model_name is not None: + vllm_command.extend(["--model", self._codex_model_name]) + if vllm_api_key is not None: + vllm_command.extend(["--api-key", vllm_api_key]) + if yaml_config: + vllm_command.extend(["--config", yaml_config]) + print("Full vLLM subprocess command:", " ".join(vllm_command)) + vllm_server = subprocess.Popen(vllm_command) + # Ping the server until it is ready at the health endpoint + checking, num_attempts = True, 0 + while checking and num_attempts < self._MAX_SERVE_CHECK_ATTEMPTS: + status = subprocess.run( + ["curl", f"http://{self.VLLM_HOST}:{self.VLLM_PORT}/health"], + capture_output=True, text=True, check=False + ) + if status.returncode == 0: + checking = False + else: + print(f"vLLM server not ready, checking again after {self.SERVE_CHECK_COOLDOWN} seconds...") + time.sleep(self.SERVE_CHECK_COOLDOWN) + num_attempts += 1 + atexit.register(vllm_server.terminate) + print("vLLM server ready.") + return vllm_server diff --git a/src/translate/translate.py b/src/translate/translate.py index 87a59df..71d06fb 100755 --- a/src/translate/translate.py +++ b/src/translate/translate.py @@ -18,6 +18,7 @@ from naive.naive_translator import NaiveTranslator from top_down_agentic.top_down_agentic import TopDownAgenticTranslator from swe_agent.swe_agent_translator import SWEAgentTranslator +from codex.codex_translator import CodexTranslator def get_args(): parser = ArgumentParser(description=__doc__) @@ -25,7 +26,7 @@ def get_args(): parser.add_argument("-o", "--output", type=str, required=True, help="Path to the output source code repository.") parser.add_argument("-c", "--config", type=str, required=True, help="Path to translation destination model configuration file containing prompt fill-ins.") parser.add_argument("-f", "--force-overwrite", action="store_true", help="Force overwrite of existing output directory.") - parser.add_argument("--method", choices=["naive", "top-down-agentic", "swe-agent"], required=True, help="The translation method to use.") + parser.add_argument("--method", choices=["naive", "top-down-agentic", "swe-agent", "codex"], required=True, help="The translation method to use.") parser.add_argument("--src-model", type=str, required=True, help="The source execution model.") parser.add_argument("--dst-model", type=str, required=True, help="The destination execution model.") parser.add_argument("--output-id", type=int, required=True, help="The integer ID of the output, used to count repeat instances of the same translation configuration.") @@ -48,6 +49,10 @@ def get_args(): swe_agent_args = parser.add_argument_group("SWE-agent translation") SWEAgentTranslator.add_args(swe_agent_args) + # subgroup for Codex translation method + codex_args = parser.add_argument_group("Codex translation") + CodexTranslator.add_args(codex_args) + return parser.parse_args() def get_translator_cls(method: str): @@ -57,6 +62,8 @@ def get_translator_cls(method: str): return TopDownAgenticTranslator if method == "swe-agent": return SWEAgentTranslator + if method == "codex": + return CodexTranslator raise ValueError(f"Translation method {method} not recognized.") From 82c95527e4bddf3b09bfab283a5207bef3a6d1d4 Mon Sep 17 00:00:00 2001 From: Ishan Khillan Date: Sun, 8 Mar 2026 16:47:54 -0400 Subject: [PATCH 27/27] Modified codex command call --- src/translate/codex/codex_translator.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/translate/codex/codex_translator.py b/src/translate/codex/codex_translator.py index 074044c..775dbad 100644 --- a/src/translate/codex/codex_translator.py +++ b/src/translate/codex/codex_translator.py @@ -218,13 +218,10 @@ def run_codex(self) -> bool: def _build_codex_command(self, prompt: str) -> List[str]: """Build the Codex CLI command with all required parameters.""" - cmd = [ - "codex", "exec", prompt, - "--sandbox", "workspace-write", - "--ask-for-approval", "never", - ] + cmd = ["codex", "exec", "--sandbox", "workspace-write"] if self._codex_model_name: cmd.extend(["--model", self._codex_model_name]) + cmd.append(prompt) return cmd def _build_codex_env(self) -> dict: