Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions agent/auxiliary_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,12 @@ def _is_arcee_trinity_thinking(model: Optional[str]) -> bool:
return bare == "trinity-large-thinking"


def _is_gpt55_family(model: Optional[str]) -> bool:
"""Return whether *model* belongs to the gpt-5.5 family."""
bare = (model or "").strip().lower().rsplit("/", 1)[-1]
return bare == "gpt-5.5" or bare.startswith(("gpt-5.5-", "gpt-5.5."))


# Context window enforced by ChatGPT's Codex OAuth backend for the
# gpt-5.4 / gpt-5.5 / gpt-5.6 families. The raw OpenAI API and OpenRouter
# expose 1.05M for the same slugs, but the Codex backend hard-caps at 272K
Expand Down Expand Up @@ -426,6 +432,9 @@ def _fixed_temperature_for_model(
return OMIT_TEMPERATURE
if _is_arcee_trinity_thinking(model):
return 0.5
if _is_gpt55_family(model):
logger.debug("Omitting temperature for gpt-5.5 family model %r (default-only)", model)
return OMIT_TEMPERATURE
return None


Expand Down
72 changes: 68 additions & 4 deletions tests/agent/test_unsupported_temperature_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
import pytest

from agent.auxiliary_client import (
OMIT_TEMPERATURE,
_build_call_kwargs,
_fixed_temperature_for_model,
_is_gpt55_family,
call_llm,
async_call_llm,
_is_unsupported_temperature_error,
Expand Down Expand Up @@ -74,6 +78,42 @@ def _dummy_response():
return {"ok": True}


@pytest.mark.parametrize(
"model",
[
"gpt-5.5",
"gpt-5.5-pro",
"gpt-5.5-2026-04-23",
"openai/gpt-5.5",
"GPT-5.5",
" gpt-5.5 ",
],
)
def test_gpt55_family_omits_temperature(model):
assert _is_gpt55_family(model) is True
assert _fixed_temperature_for_model(model) is OMIT_TEMPERATURE


@pytest.mark.parametrize(
"model",
[None, "", "gpt-5.4", "gpt-5.55", "gpt-5.50", "gpt-55", "gpt-4o"],
)
def test_gpt55_family_rejects_near_misses(model):
assert _is_gpt55_family(model) is False


def test_gpt55_call_kwargs_omit_temperature():
kwargs = _build_call_kwargs(
provider="openai-api",
model="gpt-5.5-pro",
messages=[{"role": "user", "content": "hi"}],
temperature=0.1,
timeout=120.0,
)
assert "temperature" not in kwargs
assert kwargs["model"] == "gpt-5.5-pro"


class TestCallLlmUnsupportedTemperatureRetry:
"""``call_llm`` retries once without temperature and returns on success."""

Expand All @@ -93,9 +133,9 @@ def test_retries_once_without_temperature(self, error_message):

with (
patch("agent.auxiliary_client._resolve_task_provider_model",
return_value=("openai-codex", "gpt-5.5", None, None, None)),
return_value=("openai-codex", "gpt-5.4", None, None, None)),
patch("agent.auxiliary_client._get_cached_client",
return_value=(client, "gpt-5.5")),
return_value=(client, "gpt-5.4")),
patch("agent.auxiliary_client._validate_llm_response",
side_effect=lambda resp, _task, **_kw: resp),
):
Expand All @@ -120,6 +160,30 @@ def test_retries_once_without_temperature(self, error_message):
assert "max_tokens" not in retry_kwargs
assert retry_kwargs["model"] == first_kwargs["model"]

def test_gpt55_family_succeeds_without_reactive_retry(self):
client = MagicMock()
client.base_url = "https://api.openai.com/v1"
client.chat.completions.create.return_value = _dummy_response()

with (
patch("agent.auxiliary_client._resolve_task_provider_model",
return_value=("openai-codex", "gpt-5.5", None, None, None)),
patch("agent.auxiliary_client._get_cached_client",
return_value=(client, "gpt-5.5")),
patch("agent.auxiliary_client._validate_llm_response",
side_effect=lambda resp, _task, **_kw: resp),
):
result = call_llm(
task="compression",
messages=[{"role": "user", "content": "remember this"}],
temperature=0.3,
max_tokens=500,
)

assert result == {"ok": True}
assert client.chat.completions.create.call_count == 1
assert "temperature" not in client.chat.completions.create.call_args.kwargs

def test_non_temperature_400_does_not_retry_as_temperature(self):
"""Unrelated 400s (e.g. bad tool role) must not silently drop temp."""
client = MagicMock()
Expand Down Expand Up @@ -193,9 +257,9 @@ async def test_async_retries_once_without_temperature(self):

with (
patch("agent.auxiliary_client._resolve_task_provider_model",
return_value=("openai-codex", "gpt-5.5", None, None, None)),
return_value=("openai-codex", "gpt-5.4", None, None, None)),
patch("agent.auxiliary_client._get_cached_client",
return_value=(client, "gpt-5.5")),
return_value=(client, "gpt-5.4")),
patch("agent.auxiliary_client._validate_llm_response",
side_effect=lambda resp, _task, **_kw: resp),
):
Expand Down
Loading