From 6403846bbe64cd78434d32fe8a0edd46e8b7c96f Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Wed, 27 May 2026 14:44:15 +0000 Subject: [PATCH] Studio: spoof-aware Codex availability probe + autouse fixture When ``UNSLOTH_CODEX_SPOOF=1`` is exported (the credit-free dev / CI path the previous commit added), the in-process spoof IS the Codex SDK and a real ``codex`` CLI is irrelevant. The status endpoint at ``/api/codex/status`` used to gate ``installed`` on the real CLI + real SDK only, which made the frontend hide the Codex provider in the connections dropdown even when the spoof was active. Now both ``_sdk_importable`` and ``probe_codex_availability`` short-circuit on ``codex_spoof.is_spoof_enabled()`` so the provider becomes visible under the spoof. ``installed=True``, ``cli_path=""``, ``logged_in=True``, ``version="spoof"`` -- a sentinel that lets devs read off "yes I am under the spoof" at a glance. Real production code path (no spoof flag) is unchanged: still gates on bool(cli_path) AND sdk_ok the same as round 6. Tests: added an autouse fixture in ``test_codex_provider.py`` that clears ``UNSLOTH_CODEX_SPOOF`` before every test so the existing availability / import gating tests are not polluted when a dev runs the suite with the flag exported. The spoof-targeted tests still call ``monkeypatch.setenv(...)`` to flip it back on inside their own scope. 69/69 pass with and without the env flag. --- .../core/inference/codex_availability.py | 31 ++++++++++++++++--- studio/backend/tests/test_codex_provider.py | 12 +++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/studio/backend/core/inference/codex_availability.py b/studio/backend/core/inference/codex_availability.py index 7d273d9ed9..d48d59c877 100644 --- a/studio/backend/core/inference/codex_availability.py +++ b/studio/backend/core/inference/codex_availability.py @@ -146,7 +146,19 @@ def _sdk_importable() -> bool: Probes both ``openai_codex`` (the canonical upstream package name at ``openai/codex/sdk/python``) and ``codex_app_server`` (the Rust crate name, kept as a forward-compat alias). + + When ``UNSLOTH_CODEX_SPOOF=1`` is set we report importable=True so + the frontend exposes the Codex provider in dev / CI without a real + SDK install. The spoof module gets swapped into ``sys.modules`` on + first ``_import_codex`` call, so any downstream consumer that + actually imports also succeeds. """ + try: + from core.inference import codex_spoof + if codex_spoof.is_spoof_enabled(): + return True + except Exception: + pass for name in _SDK_MODULE_NAMES: try: if importlib.util.find_spec(name) is not None: @@ -338,17 +350,28 @@ async def probe_codex_availability() -> dict[str, Any]: cli_path = _which_codex() sdk_ok = _sdk_importable() + # Spoof mode also fakes the CLI half of the install signal so the + # frontend stops hiding the Codex provider in dev / CI. ``installed`` + # gates on the spoof being explicitly opted in, so production hosts + # without the flag still see the real CLI / SDK gating intact. + spoof_active = False + try: + from core.inference import codex_spoof + spoof_active = codex_spoof.is_spoof_enabled() + except Exception: + pass + payload: dict[str, Any] = { # Gate on BOTH because the login flow shells out to `codex`. # Round 5 briefly set this to `sdk_ok` alone, but round 6 # caught that the login route would then fail with # `codex CLI not found on PATH` after the user clicked # Sign in, leaving them with an unusable provider row. - "installed": bool(cli_path) and sdk_ok, - "cli_path": cli_path, + "installed": (bool(cli_path) and sdk_ok) or spoof_active, + "cli_path": cli_path or ("" if spoof_active else None), "sdk_importable": sdk_ok, - "logged_in": False, - "version": None, + "logged_in": spoof_active, + "version": "spoof" if spoof_active else None, "supported_models": list(_DEFAULT_SUPPORTED_MODELS), } diff --git a/studio/backend/tests/test_codex_provider.py b/studio/backend/tests/test_codex_provider.py index a14f6faf56..f6d2c99952 100644 --- a/studio/backend/tests/test_codex_provider.py +++ b/studio/backend/tests/test_codex_provider.py @@ -34,6 +34,18 @@ from typing import Any import pytest +@pytest.fixture(autouse = True) +def _no_spoof_by_default(monkeypatch): + """Tests in this file assume the real-SDK gating path unless they + explicitly re-enable the spoof. The dev environment sometimes has + UNSLOTH_CODEX_SPOOF=1 exported for the live UI; clearing it here + keeps the existing availability + import tests deterministic. + Tests that exercise the spoof use ``monkeypatch.setenv(...)`` to + flip it back on inside their own scope. + """ + monkeypatch.delenv("UNSLOTH_CODEX_SPOOF", raising = False) + + _backend = os.path.join(os.path.dirname(__file__), "..") if _backend not in sys.path: sys.path.insert(0, _backend)