feat(providers): support Codex fast mode
This commit is contained in:
@@ -676,6 +676,25 @@ Then run:
|
|||||||
nanobot agent -m "Hello!"
|
nanobot agent -m "Hello!"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To opt in to Codex Fast mode, merge this provider setting into `config.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"providers": {
|
||||||
|
"openaiCodex": {
|
||||||
|
"extraBody": {
|
||||||
|
"service_tier": "priority"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`priority` is the Responses API request value used by Codex Fast mode. The setting only works
|
||||||
|
for models and accounts that support Fast mode; remove `service_tier` to return to standard
|
||||||
|
processing. Fast mode consumes Codex credits at a higher rate. See the
|
||||||
|
[OpenAI Codex rate card](https://help.openai.com/en/articles/20001106) for current details.
|
||||||
|
|
||||||
For proxy, remote/headless login, model-name, or config-key errors, see [`troubleshooting.md`](./troubleshooting.md#provider-and-model-problems).
|
For proxy, remote/headless login, model-name, or config-key errors, see [`troubleshooting.md`](./troubleshooting.md#provider-and-model-problems).
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ def _make_provider_core(
|
|||||||
provider = OpenAICodexProvider(
|
provider = OpenAICodexProvider(
|
||||||
default_model=model,
|
default_model=model,
|
||||||
proxy=getattr(p, "proxy", None) if p else None,
|
proxy=getattr(p, "proxy", None) if p else None,
|
||||||
|
extra_body=p.extra_body if p else None,
|
||||||
)
|
)
|
||||||
elif backend == "azure_openai":
|
elif backend == "azure_openai":
|
||||||
from nanobot.providers.azure_openai_provider import AzureOpenAIProvider
|
from nanobot.providers.azure_openai_provider import AzureOpenAIProvider
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ class OpenAICodexProvider(LLMProvider):
|
|||||||
self,
|
self,
|
||||||
default_model: str = "openai-codex/gpt-5.6-sol",
|
default_model: str = "openai-codex/gpt-5.6-sol",
|
||||||
proxy: str | None = None,
|
proxy: str | None = None,
|
||||||
|
extra_body: dict[str, Any] | None = None,
|
||||||
):
|
):
|
||||||
super().__init__(api_key=None, api_base=None)
|
super().__init__(api_key=None, api_base=None)
|
||||||
self.default_model = default_model
|
self.default_model = default_model
|
||||||
self.proxy = proxy or None
|
self.proxy = proxy or None
|
||||||
|
self._extra_body = dict(extra_body or {})
|
||||||
|
|
||||||
async def _call_codex(
|
async def _call_codex(
|
||||||
self,
|
self,
|
||||||
@@ -74,6 +76,9 @@ class OpenAICodexProvider(LLMProvider):
|
|||||||
body["reasoning"] = reasoning_options
|
body["reasoning"] = reasoning_options
|
||||||
if tools:
|
if tools:
|
||||||
body["tools"] = convert_tools(tools)
|
body["tools"] = convert_tools(tools)
|
||||||
|
if self._extra_body:
|
||||||
|
# Apply explicit provider overrides last, matching other provider backends.
|
||||||
|
body.update(self._extra_body)
|
||||||
|
|
||||||
stage = "oauth_token"
|
stage = "oauth_token"
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import pytest
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
import nanobot.providers.base as provider_base
|
import nanobot.providers.base as provider_base
|
||||||
|
from nanobot.config.schema import Config
|
||||||
|
from nanobot.providers.factory import make_provider
|
||||||
from nanobot.providers.openai_codex_provider import (
|
from nanobot.providers.openai_codex_provider import (
|
||||||
OpenAICodexProvider,
|
OpenAICodexProvider,
|
||||||
_build_reasoning_options,
|
_build_reasoning_options,
|
||||||
@@ -220,6 +222,38 @@ async def test_codex_prompt_cache_key_uses_stable_conversation_prefix(monkeypatc
|
|||||||
|
|
||||||
assert bodies[0]["prompt_cache_key"] == bodies[1]["prompt_cache_key"]
|
assert bodies[0]["prompt_cache_key"] == bodies[1]["prompt_cache_key"]
|
||||||
assert bodies[0]["prompt_cache_key"] != bodies[2]["prompt_cache_key"]
|
assert bodies[0]["prompt_cache_key"] != bodies[2]["prompt_cache_key"]
|
||||||
|
assert all("service_tier" not in body for body in bodies)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_codex_provider_applies_extra_body_from_config(monkeypatch) -> None:
|
||||||
|
bodies: list[dict[str, Any]] = []
|
||||||
|
_mock_codex_token(monkeypatch)
|
||||||
|
|
||||||
|
async def fake_request(_url, _headers, body, **_kwargs):
|
||||||
|
bodies.append(body)
|
||||||
|
return "ok", [], "stop", {}, None
|
||||||
|
|
||||||
|
monkeypatch.setattr("nanobot.providers.openai_codex_provider._request_codex", fake_request)
|
||||||
|
config = Config.model_validate({
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"model": "openai-codex/gpt-5.6-sol",
|
||||||
|
"provider": "openai_codex",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"providers": {
|
||||||
|
"openaiCodex": {
|
||||||
|
"extraBody": {"service_tier": "priority"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
provider = make_provider(config)
|
||||||
|
response = await provider.chat([{"role": "user", "content": "hello"}])
|
||||||
|
|
||||||
|
assert response.content == "ok"
|
||||||
|
assert bodies[0]["service_tier"] == "priority"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
Reference in New Issue
Block a user