feat: preserve Responses reasoning state and compact context (#5172)

This commit is contained in:
chengyongru
2026-07-30 22:39:43 +08:00
committed by GitHub
parent 511c764f45
commit 6a1a45d07a
37 changed files with 4778 additions and 153 deletions
+61 -1
View File
@@ -11,7 +11,7 @@ from nanobot.providers.azure_openai_provider import (
AzureOpenAIProvider,
_AzureTokenProvider,
)
from nanobot.providers.base import LLMResponse
from nanobot.providers.base import LLMResponse, ProviderCallContext
# ---------------------------------------------------------------------------
# Init & validation
@@ -234,6 +234,7 @@ def test_build_body_basic():
assert body["max_output_tokens"] == 4096
assert body["store"] is False
assert "reasoning" not in body
assert "include" not in body
# input should contain the converted user message only (system extracted)
assert any(
item.get("role") == "user"
@@ -241,6 +242,30 @@ def test_build_body_basic():
)
def test_build_body_enables_server_compaction():
provider = AzureOpenAIProvider(
api_key="k",
api_base="https://res.openai.azure.com",
default_model="gpt-5.6",
)
body = provider._build_body(
[{"role": "user", "content": "hello"}],
None,
None,
10_000,
0.1,
"high",
None,
provider_context=ProviderCallContext(context_window_tokens=200_000),
)
assert body["context_management"] == [{
"type": "compaction",
"compact_threshold": 180_000,
}]
def test_build_body_max_tokens_minimum():
"""max_output_tokens should never be less than 1."""
provider = AzureOpenAIProvider(api_key="k", api_base="https://r.com", default_model="gpt-4o")
@@ -358,6 +383,38 @@ async def test_chat_success():
assert result.usage["prompt_tokens"] == 10
@pytest.mark.asyncio
async def test_chat_retries_without_unsupported_server_compaction():
provider = AzureOpenAIProvider(
api_key="test-key",
api_base="https://test.openai.azure.com",
default_model="gpt-5.6",
)
class UnsupportedCompactionError(Exception):
status_code = 400
body = {"error": {"message": "Unknown parameter: context_management"}}
provider._client.responses = MagicMock()
provider._client.responses.create = AsyncMock(side_effect=[
UnsupportedCompactionError(),
_make_sdk_response(content="compaction fallback"),
])
result = await provider.chat(
[{"role": "user", "content": "Hi"}],
provider_context=ProviderCallContext(context_window_tokens=200_000),
)
create = provider._client.responses.create
assert result.content == "compaction fallback"
assert result.provider_state is not None
assert create.await_count == 2
assert "context_management" in create.call_args_list[0].kwargs
assert "context_management" not in create.call_args_list[1].kwargs
assert provider.supports_native_compaction() is False
@pytest.mark.asyncio
async def test_chat_uses_default_model():
provider = AzureOpenAIProvider(
@@ -411,6 +468,7 @@ async def test_chat_with_tool_calls():
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "get_weather"
assert result.tool_calls[0].arguments == {"location": "SF"}
assert result.provider_state is not None
@pytest.mark.asyncio
@@ -510,6 +568,7 @@ async def test_chat_stream_with_tool_calls():
item_done.name = "get_weather"
ev_item_done = MagicMock(type="response.output_item.done", item=item_done)
resp_obj = MagicMock(status="completed")
resp_obj.model_dump.return_value = {"status": "completed", "output": []}
ev_completed = MagicMock(type="response.completed", response=resp_obj)
async def mock_stream():
@@ -527,6 +586,7 @@ async def test_chat_stream_with_tool_calls():
assert len(result.tool_calls) == 1
assert result.tool_calls[0].name == "get_weather"
assert result.tool_calls[0].arguments == {"location": "SF"}
assert result.provider_state is not None
@pytest.mark.asyncio