From a487c8d2227f565c1159c30304fcb9706cfec7dd Mon Sep 17 00:00:00 2001 From: Tim Stranske Date: Fri, 24 Jul 2026 17:01:06 -0500 Subject: [PATCH 1/4] fix(llm): namespace github-models model ids for the GA endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GitHub Models GA endpoint (models.github.ai/inference) requires publisher-namespaced ids (e.g. openai/gpt-5). A bare id returns "404 page not found" — which is why github-models/codex-mini-latest failed the maint-78 pilot (run 30112277194). Add _github_model_id(): default a bare id to the openai/ publisher; already-namespaced ids pass through unchanged. Scope/honesty: this fixes the bare-id-on-GA-endpoint 404 CLASS. If a specific id (e.g. codex-mini-latest) is not in the GitHub Models catalog at all, namespacing won't help and the durable fix is an owner-reviewed change of the github-models selection to a catalogued id (openai/gpt-5 is already catalogued). Per-candidate preflight tolerance (#2824) already prevents this from aborting a whole pilot. Updated two tests that asserted verbatim pass-through (a pre-GA assumption) to the namespaced expectation. ruff/black/mypy (CI-pinned) clean; 63 tests pass incl. 4 new. Co-Authored-By: Claude Opus 4.8 --- tests/tools/test_langchain_client.py | 37 ++++++++++++++++++++++++++-- tools/langchain_client.py | 17 ++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/tests/tools/test_langchain_client.py b/tests/tools/test_langchain_client.py index 55f8c92b9..63f29aeb1 100644 --- a/tests/tools/test_langchain_client.py +++ b/tests/tools/test_langchain_client.py @@ -158,7 +158,8 @@ def test_explicit_github_provider_uses_reviewed_model_when_model_is_omitted( assert resolved.provider == langchain_client.PROVIDER_GITHUB assert resolved.model == "github-reviewed" assert isinstance(resolved.client, FakeChatOpenAI) - assert resolved.client.kwargs["model"] == "github-reviewed" + # github-models GA namespaces bare ids with the openai/ publisher + assert resolved.client.kwargs["model"] == "openai/github-reviewed" def test_build_chat_client_env_model_override(monkeypatch: pytest.MonkeyPatch) -> None: @@ -175,7 +176,7 @@ def test_build_chat_client_env_model_override(monkeypatch: pytest.MonkeyPatch) - assert resolved is not None assert resolved.model == "gpt-4o-mini" assert isinstance(resolved.client, FakeChatOpenAI) - assert resolved.client.kwargs["model"] == "gpt-4o-mini" + assert resolved.client.kwargs["model"] == "openai/gpt-4o-mini" def test_build_chat_client_blocked_model_override_does_not_shift_provider( @@ -799,3 +800,35 @@ def test_build_openai_client_normal_model_has_temperature( assert resolved is not None assert isinstance(resolved.client, FakeChatOpenAI) assert resolved.client.kwargs["temperature"] == 0.1 + + +class _CaptureChatOpenAI: + def __init__(self, **kwargs): + self.kwargs = kwargs + + +def test_github_model_id_namespaces_bare_ids(): + assert langchain_client._github_model_id("codex-mini-latest") == "openai/codex-mini-latest" + assert langchain_client._github_model_id("gpt-5") == "openai/gpt-5" + + +def test_github_model_id_leaves_namespaced_ids_unchanged(): + assert langchain_client._github_model_id("openai/gpt-5") == "openai/gpt-5" + assert ( + langchain_client._github_model_id("mistral-ai/mistral-large") == "mistral-ai/mistral-large" + ) + + +def test_build_github_client_sends_namespaced_model(): + client = langchain_client._build_github_client( + _CaptureChatOpenAI, model="codex-mini-latest", token="t", timeout=30, max_retries=2 + ) + assert client.kwargs["model"] == "openai/codex-mini-latest" + assert client.kwargs["base_url"] == langchain_client.GITHUB_MODELS_BASE_URL + + +def test_build_github_client_preserves_already_namespaced(): + client = langchain_client._build_github_client( + _CaptureChatOpenAI, model="openai/gpt-5", token="t", timeout=30, max_retries=2 + ) + assert client.kwargs["model"] == "openai/gpt-5" diff --git a/tools/langchain_client.py b/tools/langchain_client.py index 33a3b14a1..9ba1241cf 100644 --- a/tools/langchain_client.py +++ b/tools/langchain_client.py @@ -179,11 +179,26 @@ def _build_anthropic_client( ) +def _github_model_id(model: str) -> str: + """Return a GitHub Models GA-compatible model id (``publisher/model``). + + The GA endpoint (``models.github.ai/inference``) requires publisher-namespaced ids + such as ``openai/gpt-5``. A bare id (e.g. ``codex-mini-latest``) 404s ("page not + found") there. Default a bare id to the ``openai/`` publisher; already-namespaced + ids (containing ``/``) are returned unchanged. (If a bare id maps to a model the + GitHub Models catalog does not offer at all, the durable fix is an owner-reviewed + change of the github-models selection to a catalogued id — this only normalizes + the id format.) + """ + name = model.strip() + return name if "/" in name else f"openai/{name}" + + def _build_github_client( chat_openai: type, *, model: str, token: str, timeout: int, max_retries: int ) -> object: kwargs: dict = { - "model": model, + "model": _github_model_id(model), "base_url": GITHUB_MODELS_BASE_URL, "api_key": token, "timeout": timeout, From 72aa320f88b0403bea852a3583f4ccd384be693d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 24 Jul 2026 22:54:45 +0000 Subject: [PATCH 2/4] chore(autofix): formatting/lint --- tests/tools/test_langchain_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/tools/test_langchain_client.py b/tests/tools/test_langchain_client.py index 716edfa15..006b245ed 100644 --- a/tests/tools/test_langchain_client.py +++ b/tests/tools/test_langchain_client.py @@ -837,6 +837,8 @@ def test_build_github_client_preserves_already_namespaced(): _CaptureChatOpenAI, model="openai/gpt-5", token="t", timeout=30, max_retries=2 ) assert client.kwargs["model"] == "openai/gpt-5" + + def _build_anthropic(model: str): return langchain_client._build_anthropic_client( _CaptureChatAnthropic, model=model, token="t", timeout=30, max_retries=2 From 62081730e19bdf5bdfdc90fe53cf00bd82a303b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Jul 2026 22:55:54 +0000 Subject: [PATCH 3/4] chore(codex-autofix): apply updates (PR #2825) --- langsmith-fleet-worker-attempt.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/langsmith-fleet-worker-attempt.json b/langsmith-fleet-worker-attempt.json index aca5d45e3..901cef897 100644 --- a/langsmith-fleet-worker-attempt.json +++ b/langsmith-fleet-worker-attempt.json @@ -1,13 +1,13 @@ { "agent": "codex", "cli_version": "0.125.0", - "emitted_at": "2026-07-24T21:53:26.737479Z", + "emitted_at": "2026-07-24T22:55:53.506408Z", "execution_profile": "codex-default", "fallback_models": [ "gpt-5.4" ], "operation_role": "worker", - "pr_number": "2822", + "pr_number": "2825", "requested_model": "gpt-5.5", "runner": "reusable-codex-run", "schema": "langsmith-fleet/v1", From 8199d300a58015cb3c4a472c8cc9e9d05f1f7095 Mon Sep 17 00:00:00 2001 From: Tim Stranske Date: Fri, 24 Jul 2026 17:56:18 -0500 Subject: [PATCH 4/4] fix(ci): restage black-formatted test + update github-model assertion for GA namespacing Two follow-ups to the #2825 merge: - tests/tools/test_langchain_client.py: commit the black-formatted version (the post-merge black ran in the worktree but the staged pre-format version was committed, so CI black flagged 'would reformat'). - tests/scripts/test_task_decomposer.py: the github client model kwarg is now publisher-namespaced (openai/...), so assert against _github_model_id(...) of the configured model rather than the bare id. ruff/black (CI-pinned) clean on the whole tree; affected test files pass. Co-Authored-By: Claude Opus 4.8 --- tests/scripts/test_task_decomposer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/scripts/test_task_decomposer.py b/tests/scripts/test_task_decomposer.py index 6c92b13b7..2be4ba8bc 100644 --- a/tests/scripts/test_task_decomposer.py +++ b/tests/scripts/test_task_decomposer.py @@ -508,10 +508,13 @@ def test_get_llm_client_github_token_defaults(monkeypatch) -> None: client, provider = client_info assert provider == "github-models" assert isinstance(client, FakeChatOpenAI) + from tools.langchain_client import _github_model_id from tools.llm_provider import GITHUB_MODELS_BASE_URL from tools.llm_registry import configured_model_for_provider - assert client.kwargs["model"] == configured_model_for_provider("github-models") + assert client.kwargs["model"] == _github_model_id( + configured_model_for_provider("github-models") + ) assert client.kwargs["base_url"] == GITHUB_MODELS_BASE_URL