from __future__ import annotations from dataclasses import replace from types import SimpleNamespace import pytest from EvoScientist.llm.configuration.provider import ResolvedSecret from EvoScientist.llm.contracts import ( AgentInputV3, EvoRuntimeError, HmacGrantAuthority, WebHostContext, ) from EvoScientist.llm.model_config import EvoModelConfig from EvoScientist.llm.runtime import EvoModelRuntime from EvoScientist.llm.user_options import model_options_schema_hash from tests.test_provider_model_config_v3 import v3_payload as provider_v3_payload from tests.test_web_model_runtime import ( _input, _preparation, _runtime, _Sink, ) from tests.v3_fixtures import ( IDENTITY_KEY_ID, RUNTIME_KEY_ID, RUNTIME_SECRET, identity_ring, ) @pytest.mark.asyncio async def test_prepare_rejects_gateway_catalog_schema_that_became_stale( monkeypatch, tmp_path ): runtime, authority = _runtime(tmp_path, monkeypatch) original = _input() agent_input = replace( original, metadata={ **dict(original.metadata), "model_options_schema_hash": "sha256:stale", }, ) host = WebHostContext( "/tmp", "/tmp", object(), object(), runtime_event_sink=_Sink() ) with pytest.raises(EvoRuntimeError, match="MODEL_OPTIONS_STALE"): await runtime.prepare_model_run( _preparation(authority, agent_input), agent_input, host, ) @pytest.mark.asyncio async def test_prepare_filters_main_only_options_from_inherited_auxiliary_routes(): payload = provider_v3_payload() payload["config_identity_key_id"] = IDENTITY_KEY_ID provider = next( item for item in payload["providers"] if item["adapter_id"] == "openai" ) model_payload = provider["models"][0] model_payload["capabilities"]["thinking"] = True model_payload["parameters"]["reasoning_policy"] = { "mode": "effort", "allowed_efforts": ["low", "medium", "high"], "default_effort": "high", } model_payload["parameters"]["user_options"]["temperature"] = { "default": 1, "applies_to": ["main_agent"], "minimum": 0, "maximum_exclusive": 2, } alias_payload = next( item for item in payload["aliases"] if item["alias"] == "openai-prod-general" ) alias_payload["defaults"] = {"temperature": 1} config = EvoModelConfig.parse(payload, require_evidence=False) authority = HmacGrantAuthority(RUNTIME_SECRET, RUNTIME_KEY_ID) def resolve_secret(reference): return ResolvedSecret( "test-secret", reference.revision, "1", "test-fingerprint" ) runtime = EvoModelRuntime( SimpleNamespace(load=lambda: config), admission_verifier=authority, quote_authority=authority, identity_key_ring=identity_ring(), secret_resolver=resolve_secret, ) selector = config.resolve_main_selector("openai-prod-general") selected_model = config.providers[selector.provider].models[selector.model] selected_provider = config.providers[selector.provider] schema_hash = model_options_schema_hash( model_profile_id="openai-prod-general", user_options=selected_model.user_options, supports_reasoning=selected_model.supports_reasoning, reasoning_mode=selected_model.reasoning_mode, allowed_reasoning_efforts=selected_model.allowed_reasoning_efforts, parameter_constraints=selected_model.parameter_constraints, adapter_id=selected_provider.adapter_id, adapter_revision=selected_provider.adapter_revision, ) agent_input = AgentInputV3( "hello", "web:user:thread", metadata={ "source": "web", "model_options": {"temperature": 0.7}, "model_options_schema_hash": schema_hash, }, ) grant = authority.sign_preparation( request_id="11111111-1111-4111-8111-111111111111", turn_id="22222222-2222-4222-8222-222222222222", thread_id="thread", subject_id="user", requested_model_ref="openai-prod-general", plan="starter", roles=("user",), requires_vision=False, reasoning_effort="high", title_policy="best_effort", gateway_input_digest=authority.agent_input_digest(agent_input.projection()), checkpoint_thread_id=agent_input.checkpoint_thread_id, checkpoint_snapshot_id="sha256:checkpoint", turn_fencing_token=1, ttl_ms=60_000, ) await runtime.prepare_model_run( grant, agent_input, WebHostContext("/tmp", "/tmp", object(), object(), runtime_event_sink=_Sink()), ) snapshot = next(iter(runtime._prepared.values())).snapshot assert snapshot.purpose_routes["main_agent"][0].params["temperature"] == 0.7 assert "temperature" not in snapshot.purpose_routes["tool_selector"][0].params assert "temperature" not in snapshot.purpose_routes["deepagents_summarizer"][0].params