mirror of
https://github.com/usestrix/strix.git
synced 2026-08-18 01:39:19 +02:00
fix: build fallback-specific model settings for the denial fallback
This commit is contained in:
+13
-1
@@ -18,6 +18,7 @@ if TYPE_CHECKING:
|
||||
|
||||
from agents.items import TResponseInputItem
|
||||
from agents.memory import Session
|
||||
from agents.model_settings import ModelSettings
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -56,6 +57,7 @@ class AgentCoordinator:
|
||||
self.denial_counts: dict[str, int] = {}
|
||||
self.denial_fallback: set[str] = set()
|
||||
self._denial_fallback_model: str | None = None
|
||||
self._denial_fallback_model_settings: ModelSettings | None = None
|
||||
self._denied_retries: int = 3
|
||||
self.wait_kinds: dict[str, WaitKind] = {}
|
||||
self.runtimes: dict[str, AgentRuntime] = {}
|
||||
@@ -71,15 +73,25 @@ class AgentCoordinator:
|
||||
def set_snapshot_path(self, path: Path) -> None:
|
||||
self._snapshot_path = path
|
||||
|
||||
def configure_denial_fallback(self, model: str | None, denied_retries: int) -> None:
|
||||
def configure_denial_fallback(
|
||||
self,
|
||||
model: str | None,
|
||||
denied_retries: int,
|
||||
model_settings: ModelSettings | None = None,
|
||||
) -> None:
|
||||
"""Configure the per-agent content denial fallback."""
|
||||
self._denial_fallback_model = model
|
||||
self._denial_fallback_model_settings = model_settings
|
||||
self._denied_retries = denied_retries
|
||||
|
||||
@property
|
||||
def denial_fallback_model(self) -> str | None:
|
||||
return self._denial_fallback_model
|
||||
|
||||
@property
|
||||
def denial_fallback_model_settings(self) -> ModelSettings | None:
|
||||
return self._denial_fallback_model_settings
|
||||
|
||||
@property
|
||||
def denied_retries(self) -> int:
|
||||
return self._denied_retries
|
||||
|
||||
@@ -648,7 +648,11 @@ async def _run_cycle( # noqa: PLR0912, PLR0915
|
||||
active_run_config = run_config
|
||||
if coordinator.denial_fallback_model and await coordinator.is_on_denial_fallback(agent_id):
|
||||
active_run_config = dataclasses.replace(
|
||||
run_config, model=coordinator.denial_fallback_model
|
||||
run_config,
|
||||
model=coordinator.denial_fallback_model,
|
||||
model_settings=(
|
||||
coordinator.denial_fallback_model_settings or run_config.model_settings
|
||||
),
|
||||
)
|
||||
stream: Any = None
|
||||
pre_run_items: list[Any] = []
|
||||
|
||||
+12
-1
@@ -175,9 +175,20 @@ async def run_strix_scan(
|
||||
if coordinator is None:
|
||||
coordinator = AgentCoordinator()
|
||||
coordinator.set_snapshot_path(agents_path)
|
||||
fallback_model = settings.llm.fallback_model
|
||||
coordinator.configure_denial_fallback(
|
||||
settings.llm.fallback_model,
|
||||
fallback_model,
|
||||
settings.llm.denied_retries,
|
||||
model_settings=make_model_settings(
|
||||
settings.llm.reasoning_effort,
|
||||
model_name=fallback_model,
|
||||
force_required_tool_choice=settings.llm.force_required_tool_choice,
|
||||
request_timeout=settings.llm.timeout,
|
||||
prompt_cache=settings.llm.prompt_cache,
|
||||
extra_headers=settings.llm.extra_headers,
|
||||
)
|
||||
if fallback_model
|
||||
else None,
|
||||
)
|
||||
|
||||
from strix.tools.notes.tools import hydrate_notes_from_disk
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from agents import RunConfig, Runner
|
||||
from agents import ModelSettings, RunConfig, Runner
|
||||
|
||||
from strix.config import codex
|
||||
from strix.core import execution
|
||||
@@ -39,13 +39,14 @@ async def _run_once(
|
||||
fallback_model: str | None = None,
|
||||
denied_retries: int = 3,
|
||||
primary_model: str = "openai/gpt-5.6-sol",
|
||||
) -> tuple[Any, list[str | None], AgentCoordinator]:
|
||||
fallback_model_settings: ModelSettings | None = None,
|
||||
) -> tuple[Any, list[tuple[str | None, ModelSettings]], AgentCoordinator]:
|
||||
_patch_fast_backoff(monkeypatch)
|
||||
calls: list[str | None] = []
|
||||
calls: list[tuple[str | None, ModelSettings]] = []
|
||||
|
||||
def _fake_run_streamed(*_args: Any, **kwargs: Any) -> _FakeStream:
|
||||
run_config = kwargs["run_config"]
|
||||
calls.append(run_config.model)
|
||||
calls.append((run_config.model, run_config.model_settings))
|
||||
return streams[len(calls) - 1]
|
||||
|
||||
monkeypatch.setattr(Runner, "run_streamed", _fake_run_streamed)
|
||||
@@ -53,14 +54,16 @@ async def _run_once(
|
||||
coordinator = AgentCoordinator()
|
||||
await coordinator.register("root", "strix", parent_id=None)
|
||||
if fallback_model is not None:
|
||||
coordinator.configure_denial_fallback(fallback_model, denied_retries)
|
||||
coordinator.configure_denial_fallback(
|
||||
fallback_model, denied_retries, model_settings=fallback_model_settings
|
||||
)
|
||||
|
||||
result = await execution._run_cycle(
|
||||
object(),
|
||||
coordinator,
|
||||
"root",
|
||||
input_data="task",
|
||||
run_config=RunConfig(model=primary_model),
|
||||
run_config=RunConfig(model=primary_model, model_settings=ModelSettings()),
|
||||
context={},
|
||||
max_turns=5,
|
||||
session=None,
|
||||
@@ -83,7 +86,7 @@ async def test_run_cycle_falls_back_after_repeated_content_denials(
|
||||
)
|
||||
|
||||
assert result is streams[3]
|
||||
assert models == ["openai/gpt-5.6-sol"] * 3 + ["openai/gpt-5.4"]
|
||||
assert [model for model, _ in models] == ["openai/gpt-5.6-sol"] * 3 + ["openai/gpt-5.4"]
|
||||
assert await coordinator.is_on_denial_fallback("root") is True
|
||||
|
||||
|
||||
@@ -109,10 +112,29 @@ async def test_run_cycle_switches_on_first_denial_at_boundary(
|
||||
)
|
||||
|
||||
assert result is streams[1]
|
||||
assert models == ["openai/gpt-5.6-sol", "openai/gpt-5.4"]
|
||||
assert [model for model, _ in models] == ["openai/gpt-5.6-sol", "openai/gpt-5.4"]
|
||||
assert await coordinator.is_on_denial_fallback("root") is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_fallback_uses_its_own_model_settings(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
fallback_settings = ModelSettings(parallel_tool_calls=True)
|
||||
streams = [_guardrail_stream(), _FakeStream()]
|
||||
result, calls, _coordinator = await _run_once(
|
||||
monkeypatch,
|
||||
streams,
|
||||
fallback_model="openai/gpt-5.4",
|
||||
denied_retries=1,
|
||||
fallback_model_settings=fallback_settings,
|
||||
)
|
||||
|
||||
assert result is streams[1]
|
||||
assert calls[0][1] is not fallback_settings
|
||||
assert calls[1][1] is fallback_settings
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_denial_fallback_state_round_trips_through_snapshot() -> None:
|
||||
coordinator = AgentCoordinator()
|
||||
|
||||
Reference in New Issue
Block a user