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", 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 diff --git a/tests/tools/test_langchain_client.py b/tests/tools/test_langchain_client.py index ccdfd4a76..006b245ed 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( @@ -801,11 +802,43 @@ def test_build_openai_client_normal_model_has_temperature( assert resolved.client.kwargs["temperature"] == 0.1 +class _CaptureChatOpenAI: + def __init__(self, **kwargs): + self.kwargs = kwargs + + class _CaptureChatAnthropic: 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" + + def _build_anthropic(model: str): return langchain_client._build_anthropic_client( _CaptureChatAnthropic, model=model, token="t", timeout=30, max_retries=2 diff --git a/tools/langchain_client.py b/tools/langchain_client.py index cf1d97ce4..48a6e6039 100644 --- a/tools/langchain_client.py +++ b/tools/langchain_client.py @@ -200,11 +200,26 @@ def _build_anthropic_client( return chat_anthropic(**kwargs) +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,