refactor(input): bake codex cli runtime

This commit is contained in:
2025-10-15 17:29:06 -05:00
parent 5cd30ef5f2
commit 6a5ce586eb
5 changed files with 32 additions and 39 deletions

View File

@@ -20,13 +20,13 @@ cd input/Docker
./run-input-processor.sh up -d ./run-input-processor.sh up -d
``` ```
Environment variables you can pass before the command: - Environment variables you can pass before the command:
- `CODEX_COMMAND_TEMPLATE` override the Codex CLI invocation for the resume customization run (defaults to `codex prompt --input {prompt} --output {output} --format markdown`).
- `CODEX_NORMALIZER_COMMAND_TEMPLATE` optional override for the job-description normalization run (defaults to the same value as `CODEX_COMMAND_TEMPLATE`).
- `POLL_INTERVAL_SECONDS` watcher polling cadence (default `5`). - `POLL_INTERVAL_SECONDS` watcher polling cadence (default `5`).
- `CODEX_TIMEOUT_SECONDS` hard timeout for Codex calls (default `600`). - `CODEX_TIMEOUT_SECONDS` hard timeout for Codex calls (default `600`).
- `CODEX_CONFIG_DIR` optional override for the host directory that should mount into `/home/codex/.codex`. - `CODEX_CONFIG_DIR` optional override for the host directory that should mount into `/home/codex/.codex`.
The container bundles the Codex CLI and expects the caller to mount their `~/.codex` directory for credentials.
Stop or inspect the stack with: Stop or inspect the stack with:
```bash ```bash
@@ -44,4 +44,4 @@ cd input/Docker
## Troubleshooting ## Troubleshooting
- If Codex CLI fails, the job description moves to `ForCustomizing/failed/`. Check container logs, adjust the Markdown, then requeue it. - If Codex CLI fails, the job description moves to `ForCustomizing/failed/`. Check container logs, adjust the Markdown, then requeue it.
- Fatal errors (multiple resumes, multiple job descriptions, missing template, or missing Codex binary) stop the container. Resolve the issue and restart via the wrapper. - Fatal errors (multiple resumes, multiple job descriptions, missing template, or missing Codex binary) stop the container. Resolve the issue and restart via the wrapper.
- To change the Codex command format, pass a quoted template (e.g., `CODEX_COMMAND_TEMPLATE='codex run --input {prompt} --output {output}' ./run-input-processor.sh up -d'`). The template must include `{prompt}` and `{output}` placeholders. - The watcher logs the exact `codex prompt --input --output … --format markdown` command before each invocation; inspect container logs if troubleshooting is needed.

View File

@@ -15,7 +15,9 @@ RUN apt-get update \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
RUN npm install --location=global codex-cli || true # Install the official Codex CLI so the container can invoke `codex prompt`.
# The CLI expects credentials/configuration from the mounted ~/.codex directory.
RUN npm install --location=global @openai/codex
RUN groupadd -r codex && \ RUN groupadd -r codex && \
useradd -r -m -g codex -s /bin/bash codex useradd -r -m -g codex -s /bin/bash codex

View File

@@ -12,8 +12,6 @@ services:
PGID: "${LOCAL_GID:-1000}" PGID: "${LOCAL_GID:-1000}"
POLL_INTERVAL_SECONDS: "${POLL_INTERVAL_SECONDS:-5}" POLL_INTERVAL_SECONDS: "${POLL_INTERVAL_SECONDS:-5}"
CODEX_TIMEOUT_SECONDS: "${CODEX_TIMEOUT_SECONDS:-600}" CODEX_TIMEOUT_SECONDS: "${CODEX_TIMEOUT_SECONDS:-600}"
CODEX_COMMAND_TEMPLATE: "${CODEX_COMMAND_TEMPLATE:-codex prompt --input {prompt} --output {output} --format markdown}"
CODEX_NORMALIZER_COMMAND_TEMPLATE: "${CODEX_NORMALIZER_COMMAND_TEMPLATE:-codex prompt --input {prompt} --output {output} --format markdown}"
volumes: volumes:
- ../ForCustomizing/inbox:/workspace/inbox - ../ForCustomizing/inbox:/workspace/inbox
- ../ForCustomizing/outbox:/workspace/outbox - ../ForCustomizing/outbox:/workspace/outbox
@@ -22,3 +20,4 @@ services:
- ../resume:/workspace/resume:ro - ../resume:/workspace/resume:ro
- ../templates:/templates:ro - ../templates:/templates:ro
- ${CODEX_CONFIG_DIR:-/workspace/.codex}:/home/codex/.codex - ${CODEX_CONFIG_DIR:-/workspace/.codex}:/home/codex/.codex
- /etc/localtime:/etc/localtime:ro

View File

@@ -13,7 +13,6 @@ from __future__ import annotations
import logging import logging
import os import os
import shlex
import shutil import shutil
import subprocess import subprocess
import time import time
@@ -32,17 +31,9 @@ TEMPLATES_DIR = Path("/templates")
TEMPLATE_CACHE = Path("/tmp/templates") TEMPLATE_CACHE = Path("/tmp/templates")
PROMPT_TEMPLATE = TEMPLATES_DIR / "ResumeCustomizerPrompt.md" PROMPT_TEMPLATE = TEMPLATES_DIR / "ResumeCustomizerPrompt.md"
PROMPT_TEMPLATE_EXAMPLE = TEMPLATES_DIR / "ResumeCustomizerPrompt.md.example" PROMPT_TEMPLATE_EXAMPLE = TEMPLATES_DIR / "ResumeCustomizerPrompt.md.example"
NORMALIZER_TEMPLATE = Path('/app/JobDescriptionNormalizerPrompt.md') NORMALIZER_TEMPLATE = Path("/app/JobDescriptionNormalizerPrompt.md")
POLL_INTERVAL_SECONDS = int(os.environ.get("POLL_INTERVAL_SECONDS", "5")) POLL_INTERVAL_SECONDS = int(os.environ.get("POLL_INTERVAL_SECONDS", "5"))
CODEX_COMMAND_TEMPLATE = os.environ.get(
"CODEX_COMMAND_TEMPLATE",
"codex prompt --input {prompt} --output {output} --format markdown",
)
CODEX_NORMALIZER_COMMAND_TEMPLATE = os.environ.get(
"CODEX_NORMALIZER_COMMAND_TEMPLATE",
CODEX_COMMAND_TEMPLATE,
)
CODEX_TIMEOUT_SECONDS = int(os.environ.get("CODEX_TIMEOUT_SECONDS", "600")) CODEX_TIMEOUT_SECONDS = int(os.environ.get("CODEX_TIMEOUT_SECONDS", "600"))
RESOLVED_PROMPT_TEMPLATE: Path | None = None RESOLVED_PROMPT_TEMPLATE: Path | None = None
@@ -193,20 +184,19 @@ def slugify(component: str) -> str:
return "-".join(parts) return "-".join(parts)
def run_codex(prompt_path: Path, output_path: Path, command_template: str) -> None: def run_codex(prompt_path: Path, output_path: Path) -> None:
"""Execute the Codex CLI using the provided command template.""" """Execute the Codex CLI."""
command_text = command_template.format( command = [
prompt=str(prompt_path), "codex",
output=str(output_path), "prompt",
) "--input",
logging.info("Running Codex CLI command: %s", command_text) str(prompt_path),
"--output",
try: str(output_path),
command = shlex.split(command_text) "--format",
except ValueError as exc: "markdown",
raise FatalConfigurationError( ]
f"Unable to parse Codex command template into arguments: {exc}" logging.info("Running Codex CLI command: %s", " ".join(command))
) from exc
try: try:
subprocess.run( subprocess.run(
@@ -217,7 +207,7 @@ def run_codex(prompt_path: Path, output_path: Path, command_template: str) -> No
) )
except FileNotFoundError as exc: except FileNotFoundError as exc:
raise FatalConfigurationError( raise FatalConfigurationError(
f"Executable not found while running Codex CLI command: {command[0]}" "Codex CLI executable 'codex' not found in PATH"
) from exc ) from exc
except subprocess.TimeoutExpired as exc: except subprocess.TimeoutExpired as exc:
raise RuntimeError("Codex CLI timed out") from exc raise RuntimeError("Codex CLI timed out") from exc
@@ -301,7 +291,7 @@ def normalize_job_description(job_file: Path) -> NormalizedJobDescription:
prompt_path.write_text(prompt_text, encoding="utf-8") prompt_path.write_text(prompt_text, encoding="utf-8")
output_path = tmp_dir / "normalize_output.md" output_path = tmp_dir / "normalize_output.md"
run_codex(prompt_path, output_path, CODEX_NORMALIZER_COMMAND_TEMPLATE) run_codex(prompt_path, output_path)
normalized_text = output_path.read_text(encoding="utf-8").strip() normalized_text = output_path.read_text(encoding="utf-8").strip()
return parse_normalized_output(normalized_text) return parse_normalized_output(normalized_text)
@@ -356,7 +346,7 @@ def process_job(job_file: Path) -> None:
prompt_path.write_text(prompt_text, encoding="utf-8") prompt_path.write_text(prompt_text, encoding="utf-8")
output_path = tmp_dir / "codex_output.md" output_path = tmp_dir / "codex_output.md"
run_codex(prompt_path, output_path, CODEX_COMMAND_TEMPLATE) run_codex(prompt_path, output_path)
generated_output = out_dir / output_filename generated_output = out_dir / output_filename
counter = 1 counter = 1

View File

@@ -24,9 +24,11 @@ The watcher lives in `input/Docker/`:
- `templates/ResumeCustomizerPrompt.md.example` ships with default resume-customization instructions. Copy it to `ResumeCustomizerPrompt.md` to override. - `templates/ResumeCustomizerPrompt.md.example` ships with default resume-customization instructions. Copy it to `ResumeCustomizerPrompt.md` to override.
- The `.gitignore` in `templates/` keeps local overrides out of version control. - The `.gitignore` in `templates/` keeps local overrides out of version control.
### Codex CLI
- The container image installs the official Codex CLI and expects credentials/configuration from the mounted `~/.codex` directory on the host.
- No additional command-line customization is required; the watcher invokes `codex prompt --input … --output … --format markdown` internally for both passes.
### Key Environment Variables ### Key Environment Variables
- `CODEX_COMMAND_TEMPLATE` format string for the resume-customization Codex run (placeholders: `{prompt}`, `{output}`).
- `CODEX_NORMALIZER_COMMAND_TEMPLATE` optional override for the normalization Codex run (defaults to `CODEX_COMMAND_TEMPLATE`).
- `POLL_INTERVAL_SECONDS` watch loop delay (defaults to 5). - `POLL_INTERVAL_SECONDS` watch loop delay (defaults to 5).
- `CODEX_TIMEOUT_SECONDS` wall-clock timeout for each Codex call (defaults to 600). - `CODEX_TIMEOUT_SECONDS` wall-clock timeout for each Codex call (defaults to 600).
- `CODEX_CONFIG_DIR` host path to mount as `/home/codex/.codex` (defaults to `${HOME}/.codex` via the wrapper). - `CODEX_CONFIG_DIR` host path to mount as `/home/codex/.codex` (defaults to `${HOME}/.codex` via the wrapper).