From fe70599d3d15619d12399cf46e88e9992af430b4 Mon Sep 17 00:00:00 2001 From: houren Antony <2212222@mail.nankai.edu.cn> Date: Fri, 24 Jul 2026 23:29:34 +0800 Subject: [PATCH] fix: default reasoning context for codex proxy Responses API (#380) * fix: set langgraph and codex proxy runtime defaults * fix: address runtime default review feedback * fix: drop langgraph dev env defaults per maintainer review langgraph dev patches DATABASE_URI/REDIS_URI itself via patch_environment, so the reported KeyError cannot come from this flow; the env defaults added here were unnecessary. Scope the PR back to the codex proxy reasoning context fix only. --------- Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com> --- EvoScientist/llm/models.py | 7 ++++ tests/test_llm.py | 67 +++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/EvoScientist/llm/models.py b/EvoScientist/llm/models.py index 45ffad3..f7d7fbd 100644 --- a/EvoScientist/llm/models.py +++ b/EvoScientist/llm/models.py @@ -728,6 +728,13 @@ def get_chat_model( elif _responses_api_setting == "true": kwargs["use_responses_api"] = True + if _is_openai_proxy and kwargs.get("use_responses_api") is True: + reasoning = kwargs.setdefault("reasoning", {}) + if isinstance(reasoning, dict): + reasoning = dict(reasoning) + reasoning.setdefault("context", "all_turns") + kwargs["reasoning"] = reasoning + if _uses_native_deepseek: chat_model = EvoChatDeepSeek(model=model_id, **kwargs) else: diff --git a/tests/test_llm.py b/tests/test_llm.py index 33c0fef..92e0056 100644 --- a/tests/test_llm.py +++ b/tests/test_llm.py @@ -2672,7 +2672,11 @@ class TestAutoConfig: assert call_kwargs["base_url"] == "http://127.0.0.1:8000/codex/v1" assert call_kwargs["api_key"] == "ccproxy-oauth" # ccproxy uses the Responses API, so reasoning configuration is valid. - assert call_kwargs["reasoning"] == {"effort": "high", "summary": "auto"} + assert call_kwargs["reasoning"] == { + "effort": "high", + "summary": "auto", + "context": "all_turns", + } # Proxy mode: Responses API (bypasses format chain), streaming ON assert call_kwargs["use_responses_api"] is True assert "streaming" not in call_kwargs @@ -2729,6 +2733,7 @@ class TestAutoConfig: assert headers["User-Agent"].startswith("codex_cli_rs/0.144.1") mock_installed_version.assert_called_once_with() assert mock_init.call_args[1]["reasoning"]["effort"] == "xhigh" + assert mock_init.call_args[1]["reasoning"]["context"] == "all_turns" @patch("EvoScientist.llm.models.init_chat_model") def test_openai_ccproxy_codex_client_version_env(self, mock_init, monkeypatch): @@ -2802,6 +2807,66 @@ class TestAutoConfig: assert headers["version"] == "9.9.9" assert headers["User-Agent"].startswith("codex_cli_rs/9.9.9") + @patch("EvoScientist.llm.models.init_chat_model") + def test_openai_ccproxy_codex_reasoning_context_respects_caller( + self, mock_init, monkeypatch + ): + """Caller-supplied reasoning.context is not overridden.""" + mock_init.return_value = "mock_model" + monkeypatch.setenv("OPENAI_BASE_URL", "http://127.0.0.1:8000/codex/v1") + monkeypatch.setenv("OPENAI_API_KEY", "ccproxy-oauth") + reasoning = {"effort": "low", "context": "previous_response_id"} + + get_chat_model( + "gpt-5.5", + provider="openai", + reasoning=reasoning, + ) + + assert mock_init.call_args[1]["reasoning"] == { + "effort": "low", + "context": "previous_response_id", + } + assert mock_init.call_args[1]["reasoning"] is not reasoning + assert reasoning == {"effort": "low", "context": "previous_response_id"} + + @patch("EvoScientist.llm.models.init_chat_model") + def test_openai_ccproxy_codex_reasoning_context_requires_responses_api( + self, mock_init, monkeypatch + ): + """Responses-only reasoning.context is not sent when Chat Completions is forced.""" + mock_init.return_value = "mock_model" + monkeypatch.setenv("OPENAI_BASE_URL", "http://127.0.0.1:8000/codex/v1") + monkeypatch.setenv("OPENAI_API_KEY", "ccproxy-oauth") + monkeypatch.delenv("EVOSCIENTIST_USE_RESPONSES_API", raising=False) + + get_chat_model( + "gpt-5.5", + provider="openai", + use_responses_api=False, + reasoning={"effort": "low"}, + ) + + call_kwargs = mock_init.call_args[1] + assert call_kwargs["use_responses_api"] is False + assert call_kwargs["reasoning"] == {"effort": "low"} + + @patch("EvoScientist.llm.models.init_chat_model") + def test_openai_ccproxy_codex_env_false_drops_reasoning( + self, mock_init, monkeypatch + ): + """The global Chat Completions override still removes reasoning entirely.""" + mock_init.return_value = "mock_model" + monkeypatch.setenv("OPENAI_BASE_URL", "http://127.0.0.1:8000/codex/v1") + monkeypatch.setenv("OPENAI_API_KEY", "ccproxy-oauth") + monkeypatch.setenv("EVOSCIENTIST_USE_RESPONSES_API", "false") + + get_chat_model("gpt-5.5", provider="openai") + + call_kwargs = mock_init.call_args[1] + assert call_kwargs["use_responses_api"] is False + assert "reasoning" not in call_kwargs + @patch("EvoScientist.llm.models.init_chat_model") def test_openai_ccproxy_codex_none_headers(self, mock_init, monkeypatch): """An explicit default_headers=None is normalized before gap-filling."""