Skip to content

Commit cf14220

Browse files
nodeeeeeeclaude
andcommitted
Add codex CLI as a parallel note-generation backend
Selecting --model codex-cli (GUI switch or CLI prompt) routes every note_generation.py _call() through `codex exec` via stdin, captured with -o. Verify + translate stay on codex when NOTE_MODEL is codex-cli so users who only have a codex login (no OpenAI key) still get full output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent afa4e50 commit cf14220

3 files changed

Lines changed: 59 additions & 3 deletions

File tree

gui.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,6 +1705,11 @@ def _on_course_select(e) -> None:
17051705
on_change=lambda e: image_src_val.update({"v": e.data or e.control.value}),
17061706
width=260,
17071707
)
1708+
# Generator backend: default = configured NOTE_MODEL (OpenAI/etc.);
1709+
# opt-in = route all generation calls through the `codex` CLI
1710+
# (assumes the user has already run `codex login`).
1711+
codex_sw = ft.Switch(label="Use Codex CLI (runs `codex exec`, uses existing codex login)",
1712+
value=False, active_color=C_PRIMARY)
17081713

17091714
note_model = _read_constant("generate", "NOTE_MODEL")
17101715
verify_model = _read_constant("generate", "VERIFY_MODEL")
@@ -1729,6 +1734,8 @@ def _run(_) -> None:
17291734
cmd.append("--merged")
17301735
if image_src_val["v"] != "frames":
17311736
cmd += ["--image-source", image_src_val["v"]]
1737+
if codex_sw.value:
1738+
cmd += ["--model", "codex-cli"]
17321739
console.run(cmd)
17331740

17341741
detail_styles = [
@@ -1777,6 +1784,7 @@ def _run(_) -> None:
17771784
_label("Options"),
17781785
per_video_sw,
17791786
image_src_dd,
1787+
codex_sw,
17801788
force_sw,
17811789
merge_sw,
17821790
iterate_sw,

note_generation.py

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ def _max_tokens(level: int) -> int:
417417
def _provider(model: str) -> str:
418418
if model == "claude-cli":
419419
return "claude-cli"
420+
if model == "codex-cli":
421+
return "codex-cli"
420422
if model.startswith("gemini"):
421423
return "gemini"
422424
if model.startswith("claude"):
@@ -535,7 +537,8 @@ def _translate(text: str, lang: str) -> str:
535537
f"时使用同一个 key。\n\n"
536538
f"---\n\n{text}"
537539
)
538-
return _call(TRANSLATE_MODEL, system, prompt, len(text) * 3)
540+
_tmodel = NOTE_MODEL if NOTE_MODEL in ("claude-cli", "codex-cli") else TRANSLATE_MODEL
541+
return _call(_tmodel, system, prompt, len(text) * 3)
539542

540543

541544
_MODEL_MAX_COMPLETION = {
@@ -588,6 +591,48 @@ def _call(model: str, system: str, user: str, max_tokens: int,
588591
raise RuntimeError(f"claude -p failed (code {result.returncode}): {result.stderr[:500]}")
589592
return content
590593

594+
# ── Codex CLI mode: call `codex exec` as subprocess ──────────────────
595+
# Auth is handled by the `codex` CLI itself (prior `codex login`).
596+
# We run non-interactively, read-only sandbox, outside a git repo, and
597+
# capture only the agent's final message via `-o <file>` so we don't
598+
# have to parse the streaming event log on stdout.
599+
if _provider(model) == "codex-cli":
600+
import subprocess as _sp
601+
import tempfile as _tf
602+
import os as _os2
603+
out_fd, out_file = _tf.mkstemp(prefix="codex_out_", suffix=".txt")
604+
_os2.close(out_fd)
605+
try:
606+
prompt_text = f"{system}\n\n{user}" if system else user
607+
cmd = [
608+
"codex", "exec",
609+
"--skip-git-repo-check",
610+
"-s", "read-only",
611+
"-o", out_file,
612+
"-", # read prompt from stdin
613+
]
614+
result = _sp.run(
615+
cmd, input=prompt_text, capture_output=True, text=True,
616+
timeout=1800,
617+
)
618+
try:
619+
content = Path(out_file).read_text(encoding="utf-8").strip()
620+
except Exception:
621+
content = ""
622+
if _truncated is not None:
623+
_truncated.append(False) # CLI handles its own limits
624+
if result.returncode != 0 and not content:
625+
err = (result.stderr or result.stdout or "").strip()
626+
raise RuntimeError(
627+
f"codex exec failed (code {result.returncode}): {err[:500]}"
628+
)
629+
return content
630+
finally:
631+
try:
632+
Path(out_file).unlink()
633+
except Exception:
634+
pass
635+
591636
client = _get_client_for(model)
592637

593638
if _provider(model) == "anthropic":
@@ -996,7 +1041,7 @@ def generate_section(
9961041
terms.add(t)
9971042
term_list = ", ".join(sorted(terms)[:30])
9981043
v_user = _P("verify").format(term_list=term_list, draft=draft[:2500])
999-
_vmodel = NOTE_MODEL if NOTE_MODEL == "claude-cli" else VERIFY_MODEL
1044+
_vmodel = NOTE_MODEL if NOTE_MODEL in ("claude-cli", "codex-cli") else VERIFY_MODEL
10001045
v_result = _call(_vmodel, "", v_user, 1500)
10011046
if not v_result.strip().upper().startswith("APPROVED"):
10021047
if len(v_result) > len(draft) * 0.3:
@@ -2138,7 +2183,7 @@ def main() -> None:
21382183
parser.add_argument("--score", action="store_true",
21392184
help="Enable self-scoring (dev mode: prints coverage/terminology scores)")
21402185
parser.add_argument("--model", metavar="MODEL", default=None,
2141-
help="Override NOTE_MODEL (e.g. claude-cli, gemini-2.5-flash)")
2186+
help="Override NOTE_MODEL (e.g. codex-cli, claude-cli, gemini-2.5-flash)")
21422187
args = parser.parse_args()
21432188

21442189
if args.score:

run.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ def menu_generate() -> None:
493493
force = Confirm.ask(" Force regenerate existing sections?", default=False)
494494
merge_only = Confirm.ask(" Merge-only? (skip generation, re-merge + re-filter images)", default=False)
495495
iterate = Confirm.ask(" Iterative mode? (keep raising detail until quality target)", default=False)
496+
use_codex = Confirm.ask(" Use Codex CLI? (routes generation through `codex exec`; requires prior `codex login`)", default=False)
496497

497498
# ── Build command ─────────────────────────────────────────────────────────
498499
cmd = [
@@ -509,6 +510,8 @@ def menu_generate() -> None:
509510
cmd.append("--merge-only")
510511
if iterate:
511512
cmd.append("--iterate")
513+
if use_codex:
514+
cmd += ["--model", "codex-cli"]
512515

513516
console.print()
514517
_run(cmd)

0 commit comments

Comments
 (0)