mirror of
https://github.com/usestrix/strix.git
synced 2026-11-14 09:01:25 +00:00
Merge 501a0c4625 into 851743adcf
This commit is contained in:
commit
9517cddde2
11 changed files with 53 additions and 2 deletions
|
|
@ -73,6 +73,11 @@ Configure Strix using environment variables or a config file.
|
|||
failing with `LLM CONNECTION FAILED`. Scan requests use `LLM_TIMEOUT`.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="STRIX_LLM_MAX_TOKENS" type="integer">
|
||||
Optional maximum output tokens for each agent LLM request. Leave unset to use
|
||||
the provider/model default.
|
||||
</ParamField>
|
||||
|
||||
<ParamField path="STRIX_LLM_MAX_RETRIES" default="5" type="integer">
|
||||
Maximum number of retries for LLM API calls on transient failures.
|
||||
</ParamField>
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ class LlmSettings(BaseSettings):
|
|||
default=False,
|
||||
alias="LLM_DISABLE_STREAMING",
|
||||
)
|
||||
max_tokens: int | None = Field(default=None, gt=0, alias="STRIX_LLM_MAX_TOKENS")
|
||||
timeout: int = Field(default=300, alias="LLM_TIMEOUT")
|
||||
preflight_timeout: int = Field(default=30, ge=1, alias="LLM_PREFLIGHT_TIMEOUT")
|
||||
stream_idle_timeout: int = Field(default=300, ge=0, alias="LLM_STREAM_IDLE_TIMEOUT")
|
||||
|
|
|
|||
|
|
@ -252,12 +252,14 @@ def make_model_settings(
|
|||
extra_headers: dict[str, str] | None = None,
|
||||
extra_body: dict[str, Any] | None = None,
|
||||
has_tools: bool = True,
|
||||
max_tokens: int | None = None,
|
||||
) -> ModelSettings:
|
||||
headers = _request_headers(model_name, extra_headers)
|
||||
model_settings = ModelSettings(
|
||||
parallel_tool_calls=parallel_tool_calls if has_tools else None,
|
||||
retry=DEFAULT_MODEL_RETRY,
|
||||
include_usage=True,
|
||||
max_tokens=max_tokens,
|
||||
extra_args=request_timeout_extra_args(request_timeout),
|
||||
extra_headers=headers,
|
||||
extra_body=dict(extra_body) if extra_body else None,
|
||||
|
|
|
|||
|
|
@ -431,6 +431,7 @@ async def run_strix_scan(
|
|||
prompt_cache=settings.llm.prompt_cache,
|
||||
extra_headers=settings.llm.extra_headers,
|
||||
extra_body=settings.llm.extra_body,
|
||||
max_tokens=settings.llm.max_tokens,
|
||||
)
|
||||
run_config = RunConfig(
|
||||
model=resolved_model,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from pydantic import AliasChoices, Field, ValidationError
|
|||
from pydantic.fields import FieldInfo
|
||||
|
||||
from strix.config import loader
|
||||
from strix.config.settings import ContextSettings
|
||||
from strix.config.settings import ContextSettings, LlmSettings
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -28,6 +28,7 @@ _LLM_ENV_KEYS = [
|
|||
"OLLAMA_API_BASE",
|
||||
"STRIX_REASONING_EFFORT",
|
||||
"STRIX_FORCE_REQUIRED_TOOL_CHOICE",
|
||||
"STRIX_LLM_MAX_TOKENS",
|
||||
"LLM_TIMEOUT",
|
||||
"PERPLEXITY_API_KEY",
|
||||
"EXA_API_KEY",
|
||||
|
|
@ -148,6 +149,10 @@ def test_tool_output_max_bytes_accepts_floor() -> None:
|
|||
assert ContextSettings(STRIX_TOOL_OUTPUT_MAX_BYTES=1024).tool_output_max_bytes == 1024
|
||||
|
||||
|
||||
def test_llm_max_tokens_env_alias() -> None:
|
||||
assert LlmSettings(STRIX_LLM_MAX_TOKENS=12_000).max_tokens == 12_000
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# _aliases_for
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
|
|
|||
|
|
@ -337,6 +337,16 @@ def test_make_model_settings_sets_request_timeout() -> None:
|
|||
assert settings.extra_args["timeout"] == 300.0
|
||||
|
||||
|
||||
def test_make_model_settings_sets_configured_token_budget() -> None:
|
||||
settings = make_model_settings(
|
||||
"none",
|
||||
model_name="gpt-4o",
|
||||
max_tokens=12_000,
|
||||
)
|
||||
|
||||
assert settings.max_tokens == 12_000
|
||||
|
||||
|
||||
def test_make_model_settings_omits_timeout_when_unset() -> None:
|
||||
settings = make_model_settings("none", model_name="gpt-4o")
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ def _wire_runner(monkeypatch: pytest.MonkeyPatch, tmp_path: Any) -> None:
|
|||
prompt_cache=True,
|
||||
extra_headers=None,
|
||||
extra_body=None,
|
||||
max_tokens=None,
|
||||
),
|
||||
runtime=types.SimpleNamespace(max_context_images=3),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ def _settings() -> Any:
|
|||
prompt_cache=True,
|
||||
extra_headers=None,
|
||||
extra_body=None,
|
||||
max_tokens=None,
|
||||
),
|
||||
runtime=types.SimpleNamespace(max_context_images=3),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ async def test_persistent_rate_limit_stops_gracefully(
|
|||
prompt_cache=True,
|
||||
extra_headers=None,
|
||||
extra_body=None,
|
||||
max_tokens=None,
|
||||
),
|
||||
runtime=types.SimpleNamespace(max_context_images=3),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ def _patch_engine_scaffold(
|
|||
prompt_cache=True,
|
||||
extra_headers=None,
|
||||
extra_body=None,
|
||||
max_tokens=12_000,
|
||||
),
|
||||
runtime=types.SimpleNamespace(max_context_images=3),
|
||||
telemetry=types.SimpleNamespace(run_record_timings=False),
|
||||
|
|
@ -92,7 +93,6 @@ def _patch_engine_scaffold(
|
|||
|
||||
monkeypatch.setattr(runner, "build_root_task", lambda _scan_config: "task")
|
||||
monkeypatch.setattr(runner, "build_scope_context", lambda _scan_config: scope_context)
|
||||
monkeypatch.setattr(runner, "make_model_settings", lambda *_args, **_kwargs: ModelSettings())
|
||||
monkeypatch.setattr(
|
||||
mcp_client,
|
||||
"_build_server",
|
||||
|
|
@ -101,6 +101,12 @@ def _patch_engine_scaffold(
|
|||
|
||||
captured: dict[str, Any] = {}
|
||||
|
||||
def _make_model_settings(*_args: Any, **kwargs: Any) -> ModelSettings:
|
||||
captured["model_settings_kwargs"] = kwargs
|
||||
return ModelSettings()
|
||||
|
||||
monkeypatch.setattr(runner, "make_model_settings", _make_model_settings)
|
||||
|
||||
def _build_strix_agent(**kwargs: Any) -> object:
|
||||
if kwargs.get("is_root") and "kwargs" not in captured:
|
||||
captured["kwargs"] = kwargs
|
||||
|
|
@ -397,3 +403,20 @@ def test_cache_point_marker_is_removed_without_cache_points() -> None:
|
|||
prompt = "shared\n<cache_point>\ntargets"
|
||||
|
||||
assert _split_cached_prefix(prompt, "go", settings) == ("shared\n\ntargets", "go")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_llm_max_tokens_flows_into_model_settings(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
tmp_path: Any,
|
||||
) -> None:
|
||||
captured = _patch_engine_scaffold(monkeypatch, tmp_path, {"scope": "built-in"})
|
||||
|
||||
await runner.run_strix_scan(
|
||||
scan_config={"targets": [], "scan_mode": "deep"},
|
||||
scan_id="scan-token-budget",
|
||||
image="img",
|
||||
coordinator=AgentCoordinator(),
|
||||
)
|
||||
|
||||
assert captured["model_settings_kwargs"]["max_tokens"] == 12_000
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ def _settings() -> Any:
|
|||
prompt_cache=True,
|
||||
extra_headers=None,
|
||||
extra_body=None,
|
||||
max_tokens=None,
|
||||
),
|
||||
runtime=types.SimpleNamespace(max_context_images=3),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue