mirror of
https://github.com/usestrix/strix.git
synced 2026-10-13 03:57:59 +00:00
fix(compaction): fall back to reasoning text when the summary has none
A reasoning model can spend the whole summary output budget thinking and return only a reasoning item. _extract_text read only output messages, so compaction logged 'summary returned no content' and the agent failed. Use the reasoning text, with a warning, when there is no output text.
This commit is contained in:
parent
f1386cad37
commit
5227960e99
2 changed files with 46 additions and 9 deletions
|
|
@ -15,7 +15,11 @@ from typing import TYPE_CHECKING, Any
|
|||
|
||||
from agents.model_settings import ModelSettings
|
||||
from agents.models.interface import ModelTracing
|
||||
from openai.types.responses import ResponseOutputMessage, ResponseOutputText
|
||||
from openai.types.responses import (
|
||||
ResponseOutputMessage,
|
||||
ResponseOutputText,
|
||||
ResponseReasoningItem,
|
||||
)
|
||||
|
||||
from strix.config import load_settings
|
||||
from strix.config.models import StrixProvider
|
||||
|
|
@ -288,15 +292,22 @@ def _checkpoint_item(summary: str) -> dict[str, Any]:
|
|||
|
||||
def _extract_text(response: ModelResponse) -> str:
|
||||
parts: list[str] = []
|
||||
reasoning: list[str] = []
|
||||
for item in response.output:
|
||||
if not isinstance(item, ResponseOutputMessage):
|
||||
continue
|
||||
parts.extend(
|
||||
chunk.text
|
||||
for chunk in item.content
|
||||
if isinstance(chunk, ResponseOutputText) and chunk.text
|
||||
)
|
||||
return "".join(parts)
|
||||
if isinstance(item, ResponseOutputMessage):
|
||||
parts.extend(
|
||||
chunk.text
|
||||
for chunk in item.content
|
||||
if isinstance(chunk, ResponseOutputText) and chunk.text
|
||||
)
|
||||
elif isinstance(item, ResponseReasoningItem):
|
||||
reasoning.extend(summary.text for summary in item.summary if summary.text)
|
||||
if parts or not reasoning:
|
||||
return "".join(parts)
|
||||
# Reasoning models can spend the whole output budget thinking and return no
|
||||
# message; their reasoning is a degraded but usable summary.
|
||||
logger.warning("compaction summary had no output text; using reasoning text")
|
||||
return "\n".join(reasoning)
|
||||
|
||||
|
||||
async def _summarize(model: str, prompt: str, max_tokens: int) -> str | None:
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from types import SimpleNamespace
|
|||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import pytest
|
||||
from agents.extensions.models.litellm_model import InternalChatCompletionMessage
|
||||
from agents.models.chatcmpl_converter import Converter
|
||||
from litellm.exceptions import BadRequestError, ContextWindowExceededError, RateLimitError
|
||||
from openai.types.responses import ResponseOutputMessage, ResponseOutputText
|
||||
|
||||
|
|
@ -242,6 +244,30 @@ async def test_summarize_routes_through_provider_with_settings(
|
|||
assert settings.max_tokens == 64
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_summarize_falls_back_to_reasoning_when_no_output_text(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
# A reasoning model that spends its budget thinking returns only a reasoning item.
|
||||
_patch_budget(monkeypatch, keep_tokens=30, window=4_000)
|
||||
message = InternalChatCompletionMessage(
|
||||
role="assistant", content=None, reasoning_content="REASONED SUMMARY"
|
||||
)
|
||||
response = SimpleNamespace(output=Converter.message_to_output_items(message))
|
||||
|
||||
class FakeModel:
|
||||
async def get_response(self, **_kwargs: Any) -> Any:
|
||||
return response
|
||||
|
||||
class FakeProvider:
|
||||
def get_model(self, _model_name: str | None) -> Any:
|
||||
return FakeModel()
|
||||
|
||||
monkeypatch.setattr(compaction, "StrixProvider", FakeProvider)
|
||||
|
||||
assert await compaction._summarize("m", "p", 64) == "REASONED SUMMARY"
|
||||
|
||||
|
||||
def test_fit_to_tokens_truncates_oversized_text(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(compaction, "count_tokens", lambda _m, t: len(t))
|
||||
text = "x" * 10_000
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue