From b1cadf53c541ac85b0af814a660e0a0873e77252 Mon Sep 17 00:00:00 2001 From: chengyongru <2755839590@qq.com> Date: Mon, 24 Aug 2026 01:02:18 +0800 Subject: [PATCH] fix(codex): reuse TLS contexts across requests --- nanobot/providers/openai_codex_provider.py | 19 +++++++-- tests/providers/test_openai_codex_provider.py | 42 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/nanobot/providers/openai_codex_provider.py b/nanobot/providers/openai_codex_provider.py index fdfbcef7d..1a3881dd0 100644 --- a/nanobot/providers/openai_codex_provider.py +++ b/nanobot/providers/openai_codex_provider.py @@ -7,6 +7,7 @@ from __future__ import annotations import asyncio import hashlib import json +import ssl from collections.abc import Awaitable, Callable from typing import Any, cast @@ -56,6 +57,18 @@ class OpenAICodexProvider(LLMProvider): self.proxy = proxy or None self._extra_body = dict(extra_body or {}) self._native_compaction_available = True + self._ssl_contexts: dict[bool, ssl.SSLContext] = {} + + def _ssl_context(self, *, verify: bool) -> ssl.SSLContext: + """Reuse synchronous TLS setup across requests on the shared event loop.""" + context = self._ssl_contexts.get(verify) + if context is None: + context = httpx.create_ssl_context( + verify=verify, + trust_env=self.proxy is None, + ) + self._ssl_contexts[verify] = context + return context async def _call_codex( self, @@ -129,7 +142,7 @@ class OpenAICodexProvider(LLMProvider): DEFAULT_CODEX_URL, headers, wire_body, - verify=True, + verify=self._ssl_context(verify=True), proxy=self.proxy, on_content_delta=on_content_delta if emit_deltas else None, on_thinking_delta=on_thinking_delta if emit_deltas else None, @@ -145,7 +158,7 @@ class OpenAICodexProvider(LLMProvider): DEFAULT_CODEX_URL, headers, wire_body, - verify=False, + verify=self._ssl_context(verify=False), proxy=self.proxy, on_content_delta=on_content_delta if emit_deltas else None, on_thinking_delta=on_thinking_delta if emit_deltas else None, @@ -411,7 +424,7 @@ async def _request_codex( url: str, headers: dict[str, str], body: dict[str, Any], - verify: bool, + verify: ssl.SSLContext | bool, proxy: str | None = None, on_content_delta: Callable[[str], Awaitable[None]] | None = None, on_thinking_delta: Callable[[str], Awaitable[None]] | None = None, diff --git a/tests/providers/test_openai_codex_provider.py b/tests/providers/test_openai_codex_provider.py index cccc5fb6e..1bbad4ba1 100644 --- a/tests/providers/test_openai_codex_provider.py +++ b/tests/providers/test_openai_codex_provider.py @@ -1,6 +1,8 @@ from __future__ import annotations +import asyncio import io +import ssl from types import SimpleNamespace from typing import Any @@ -42,6 +44,46 @@ def test_codex_default_model_matches_curated_flagship() -> None: assert OpenAICodexProvider().get_default_model() == spec.builtin_models[0].id +@pytest.mark.asyncio +async def test_codex_provider_reuses_tls_context_for_concurrent_requests(monkeypatch) -> None: + _mock_codex_token(monkeypatch) + proxy = "http://127.0.0.1:23458" + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context_calls: list[tuple[bool, bool]] = [] + request_contexts: list[object] = [] + + def fake_create_ssl_context( + *, + verify: bool, + cert: object = None, + trust_env: bool = True, + ) -> ssl.SSLContext: + _ = cert + context_calls.append((verify, trust_env)) + return context + + async def fake_request(_url, _headers, _body, *, verify, **_kwargs): + request_contexts.append(verify) + await asyncio.sleep(0) + return provider_base.LLMResponse(content="ok") + + monkeypatch.setattr( + "nanobot.providers.openai_codex_provider.httpx.create_ssl_context", + fake_create_ssl_context, + ) + monkeypatch.setattr("nanobot.providers.openai_codex_provider._request_codex", fake_request) + + provider = OpenAICodexProvider(proxy=proxy) + responses = await asyncio.gather(*( + provider.chat([{"role": "user", "content": f"request {index}"}]) + for index in range(3) + )) + + assert [response.content for response in responses] == ["ok", "ok", "ok"] + assert context_calls == [(True, False)] + assert request_contexts == [context, context, context] + + class _WarningCaptureLogger: def __init__(self) -> None: self.calls: list[tuple[str, tuple[Any, ...]]] = []