patch: Format development versions for ChatGPT model discovery (#1956)
Some checks failed
Post merge / Warm dependency cache (Linux) (push) Has been cancelled
Post merge / Warm dependency cache (Windows) (push) Has been cancelled
Post merge / Warm dependency cache (macOS) (push) Has been cancelled
Post merge / Publish to PyPI (push) Has been cancelled

## Why

ChatGPT model discovery rejects Python development versions such as
`6.5.9.dev3+gb89b44b2a` with HTTP 400. This leaves a connected account
unable to load its models when FCC runs from a development checkout.

## How

Convert the model-list `client_version` parameter to SemVer using the
existing `packaging.Version` parser. Preserve the development counter
and local build metadata, producing values such as
`6.5.9-dev.3+gb89b44b2a`, and give source-only installs a valid
development version.


<!-- greptile_comment -->

<!-- greptile_summary -->

<h2><a
href="https://app.greptile.com/api/retrigger?id=71923240"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/RetriggerDark.svg?v=2"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=2"><img
alt="Retrigger"
src="https://greptile-static-assets.s3.amazonaws.com/badges/Retrigger.svg?v=2"
align="right"></picture></a>Confidence Score: 4/5</h2>

<!-- greptile-risk -->

The inaccurate version parameter is a non-blocking concern; the observed
model request still succeeded with the mocked response.

<h2><a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22ali%2Fopenai-client-version-format%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22ali%2Fopenai-client-version-format%22.%0A%0A%23%23%23%20Issue%201%0Asrc%2Ffree_claude_code%2Fproviders%2Fopenai_codex%2Fprovider.py%3A210-212%0AFor%20a%20%606.5.9rc1.dev3%60%20build%2C%20model%20discovery%20sends%20%60client_version%3D6.5.9-dev.3%60.%20The%20parameter%20no%20longer%20identifies%20the%20release%20candidate%2C%20making%20the%20catalog%20request's%20version%20information%20misleading.%20This%20is%20a%20non-blocking%20reporting%20concern%3B%20preserve%20the%20release-candidate%20identifier%20in%20the%20converted%20value.%0A%0ANote%3A%20If%20this%20suggestion%20doesn't%20match%20your%20team's%20coding%20style%2C%20reply%20to%20this%20and%20let%20me%20know.%20I'll%20remember%20it%20for%20next%20time!%0A%0A---%0A%0AFor%20each%20issue%20above%2C%20determine%20whether%20it%20is%20valid%20and%20should%20be%20fixed.%20If%20so%2C%20fix%20it%20directly.&repo=alishahryar1%2Ffree-claude-code&pr=1956&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=7"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=7"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=7"
align="right"></picture></a>Findings</h2>

1. <img alt="P2"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p2.svg?v=9"
align="top">&nbsp;**Release candidate is omitted** <a
href="https://github.com/Alishahryar1/free-claude-code/pull/1956#discussion_r4142083284">▶</a>

<details open><summary>Summary</summary>

The PR converts package versions for the Codex model-list request and
adds version-formatting tests. For release-candidate builds, the
conversion omits the release-candidate identifier, so the request
reports a different version.
</details>

<!-- greptile_confidence_score:4 -->

<sub>Reviews (1) · Last reviewed commit: ["patch: Format development
versions for
C..."](b4a6b0886b)</sub>

<!-- /greptile_comment -->
This commit is contained in:
Ali Khokhar 2026-09-30 00:52:30 -07:00 • committed by GitHub
parent 216bcfb619
commit 7195cefd5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 63 additions and 1 deletions

View file

@ -9,6 +9,7 @@ from typing import Any
import httpx2
from openai import AsyncOpenAI
from packaging.version import Version
from free_claude_code.application.model_metadata import ProviderModelInfo
from free_claude_code.core.anthropic.models import MessagesRequest
@ -126,7 +127,9 @@ class OpenAICodexProvider(BaseProvider):
payload = await client.get(
"models",
cast_to=object,
options={"params": {"client_version": FCC_VERSION}},
options={
"params": {"client_version": _model_list_client_version()}
},
)
await attempt.accept()
execution.succeed()
@ -199,6 +202,19 @@ class OpenAICodexProvider(BaseProvider):
)
def _model_list_client_version() -> str:
"""Format FCC's release and VCS development versions for the catalog API."""
if FCC_VERSION == "dev":
return "0.0.0-dev"
parsed = Version(FCC_VERSION)
formatted = f"{parsed.major}.{parsed.minor}.{parsed.micro}"
if parsed.dev is not None:
formatted += f"-dev.{parsed.dev}"
if parsed.local is not None:
formatted += f"+{parsed.local}"
return formatted
async def _endpoint_required() -> str:
raise RuntimeError(
"Codex requests require request-scoped subscription credentials."

View file

@ -23,6 +23,7 @@ from free_claude_code.core.openai_responses import OpenAIResponsesRequest
from free_claude_code.core.reasoning import ReasoningEffort, ReasoningPolicy
from free_claude_code.providers.admission import ProviderAdmissionController
from free_claude_code.providers.base import ProviderConfig
from free_claude_code.providers.openai_codex import provider as codex_provider_module
from free_claude_code.providers.openai_codex.auth import (
OpenAIAccess,
OpenAIAuthManager,
@ -256,6 +257,51 @@ async def test_streamed_authentication_error_has_authentication_status(
assert auth.recovery_calls == 0
@pytest.mark.asyncio
@pytest.mark.parametrize(
"package_version,client_version",
[
("6.5.9", "6.5.9"),
("6.5.9.dev0", "6.5.9-dev.0"),
("6.5.9.dev3+gb89b44b2a", "6.5.9-dev.3+gb89b44b2a"),
("6.5.9.dev3+gb89b44b2a.d20260930", "6.5.9-dev.3+gb89b44b2a.d20260930"),
("6.5.9+d20260930", "6.5.9+d20260930"),
("dev", "0.0.0-dev"),
],
)
async def test_model_discovery_formats_package_version_for_upstream(
monkeypatch: pytest.MonkeyPatch, package_version: str, client_version: str
) -> None:
monkeypatch.setattr(codex_provider_module, "FCC_VERSION", package_version)
requests: list[httpx2.Request] = []
def handler(request: httpx2.Request) -> httpx2.Response:
requests.append(request)
if request.url.params.get("client_version") != client_version:
return httpx2.Response(
400, json={"detail": "Invalid client_version format"}
)
return httpx2.Response(
200, json={"models": [{"slug": "gpt-visible", "visibility": "list"}]}
)
provider = OpenAICodexProvider(
_config(),
auth=_FakeAuth(),
admission=immediate_admission(max_attempts=1),
transport=httpx2.MockTransport(handler),
)
try:
infos = await provider.list_model_infos()
finally:
await provider.cleanup()
assert {info.model_id for info in infos} == {"gpt-visible"}
assert len(requests) == 1
assert requests[0].headers["version"] == package_version
assert requests[0].headers["user-agent"] == f"codex_cli_rs/{package_version}"
@pytest.mark.asyncio
async def test_provider_uses_subscription_headers_and_visible_model_catalog() -> None:
requests: list[httpx2.Request] = []