mirror of
https://github.com/usestrix/strix.git
synced 2026-11-09 05:19:39 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e011253ebc | ||
|
|
5227960e99 |
No files matched your search
@@ -905,12 +905,6 @@ def model_supports_reasoning(model_name: str) -> bool:
|
||||
return bool(entry and entry.get("supports_reasoning"))
|
||||
|
||||
|
||||
def model_supports_images(model_name: str) -> bool:
|
||||
"""Return whether the model accepts image input. Assume yes until proven otherwise."""
|
||||
entry = _catalog_entry(model_name)
|
||||
return entry is None or bool(entry.get("supports_vision"))
|
||||
|
||||
|
||||
def _bare_openai_name(model_name: str) -> str:
|
||||
name = model_name.strip().lower()
|
||||
for prefix in ("litellm/", "any-llm/", "openai/"):
|
||||
|
||||
+20
-9
@@ -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,29 @@ 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:
|
||||
_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
|
||||
|
||||
@@ -16,7 +16,6 @@ from strix.config.models import (
|
||||
_NonStreamingModel,
|
||||
_TurnGuardModel,
|
||||
configure_sdk_model_defaults,
|
||||
model_supports_images,
|
||||
request_timeout_extra_args,
|
||||
resolve_api_type,
|
||||
routes_through_litellm,
|
||||
@@ -203,11 +202,3 @@ def test_configure_sdk_api_route_follows_the_given_model(
|
||||
models.configure_sdk_api_route("my-private-model", settings)
|
||||
|
||||
assert routes == ["responses", "chat_completions"]
|
||||
|
||||
|
||||
def test_image_support_follows_the_catalog(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setitem(litellm.model_cost, "acme-text", {})
|
||||
monkeypatch.setitem(litellm.model_cost, "acme-vision", {"supports_vision": True})
|
||||
assert model_supports_images("acme-text") is False
|
||||
assert model_supports_images("litellm/openai/acme-vision") is True
|
||||
assert model_supports_images("acme-unknown-model") is True
|
||||
Reference in new issue
Block a user