5a581c78a2
Build / build (push) Has been cancelled
Docker / build (push) Has been cancelled
Lint / ruff (push) Has been cancelled
Test / pytest (ubuntu-latest, 3.11) (push) Has been cancelled
Test / pytest (ubuntu-latest, 3.12) (push) Has been cancelled
Test / pytest (windows-latest, 3.11) (push) Has been cancelled
Test / pytest (windows-latest, 3.12) (push) Has been cancelled
Introduce provider, model, and invocation contracts with encrypted configuration persistence. Add web runtime fencing, route fallback, recovery middleware, workspace scoping, and comprehensive tests.
823 lines
29 KiB
Python
823 lines
29 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import sqlite3
|
|
import uuid
|
|
from datetime import UTC, datetime, timedelta
|
|
|
|
import pytest
|
|
|
|
from EvoScientist.llm.contracts import EvoRuntimeError, HmacGrantAuthority
|
|
from EvoScientist.llm.crypto import HmacKeyRing, KeyMaterial, sha256_id
|
|
from EvoScientist.llm.model_config_v4 import (
|
|
EvoModelConfig,
|
|
UnifiedModelConfigStore,
|
|
build_supported_v3_evidence,
|
|
model_profile_identity_map,
|
|
normalize_v4_config,
|
|
project_v4_to_v3,
|
|
)
|
|
from EvoScientist.llm.runtime import EvoModelRuntime
|
|
|
|
|
|
def _payload(*, provider_id: str | None = None, profile_id: str | None = None) -> dict:
|
|
provider_id = provider_id or str(uuid.uuid4())
|
|
profile_id = profile_id or str(uuid.uuid4())
|
|
return {
|
|
"schema_version": 4,
|
|
"default_model_profile_id": profile_id,
|
|
"purpose_defaults": {},
|
|
"purpose_call_limits": {},
|
|
"providers": [
|
|
{
|
|
"provider_id": provider_id,
|
|
"display_name": "OpenAI Production",
|
|
"adapter_id": "openai",
|
|
"adapter_revision": "openai-v1",
|
|
"enabled": True,
|
|
"connection": {"base_url": "https://api.openai.com/v1"},
|
|
"models": [
|
|
{
|
|
"model_profile_id": profile_id,
|
|
"provider_model_id": "gpt-test",
|
|
"display_name": "General",
|
|
"enabled": True,
|
|
"version_policy": "rolling",
|
|
"invocation": {
|
|
"api_mode": "responses",
|
|
"tool_call_transport": "native",
|
|
},
|
|
"capabilities": {"text": True, "tools": True},
|
|
"limits": {
|
|
"context_tokens": 128_000,
|
|
"max_output_tokens": 8_192,
|
|
},
|
|
"parameters": {},
|
|
"billing": {
|
|
"sku": "internal/gpt-test",
|
|
"pricing_revision": "configured-v1",
|
|
"currency": "CNY",
|
|
"unit_scale": 1_000_000,
|
|
"input_microunits_per_million": 1_000_000,
|
|
"cached_microunits_per_million": 200_000,
|
|
"output_microunits_per_million": 4_000_000,
|
|
"multiplier": 1,
|
|
},
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
|
|
|
|
def _ring() -> HmacKeyRing:
|
|
return HmacKeyRing(KeyMaterial.create("identity-v1", "i" * 32))
|
|
|
|
|
|
def test_v4_normalization_uses_stable_profile_identity():
|
|
payload = normalize_v4_config(_payload())
|
|
identities = model_profile_identity_map(payload)
|
|
|
|
assert payload["schema_version"] == 4
|
|
assert payload["default_model_profile_id"] in identities
|
|
assert identities[payload["default_model_profile_id"]][1] == "gpt-test"
|
|
|
|
|
|
def test_v4_rejects_enabled_model_without_billing_configuration():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0].pop("billing")
|
|
|
|
with pytest.raises(EvoRuntimeError) as raised:
|
|
normalize_v4_config(raw, lenient=True, warnings=[])
|
|
|
|
assert raised.value.details[0]["code"] == "CONFIG_PRICING_REQUIRED"
|
|
|
|
|
|
def test_v4_accepts_explicit_zero_price_for_free_model():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["billing"].update(
|
|
input_microunits_per_million=0,
|
|
cached_microunits_per_million=0,
|
|
output_microunits_per_million=0,
|
|
)
|
|
|
|
payload = normalize_v4_config(raw, lenient=True, warnings=[])
|
|
|
|
assert payload["providers"][0]["models"][0]["billing"][
|
|
"input_microunits_per_million"
|
|
] == 0
|
|
|
|
|
|
def test_v4_normalization_preserves_model_output_token_limit():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["parameters"] = {
|
|
"defaults": {"output_token_limit": 2048, "temperature": 0.2},
|
|
"purpose_overrides": {
|
|
"main_agent": {"output_token_limit": 1024, "temperature": 0.3}
|
|
},
|
|
"user_options": {
|
|
"output_token_limit": {
|
|
"default": 512,
|
|
"applies_to": ["main_agent"],
|
|
}
|
|
},
|
|
}
|
|
|
|
payload = normalize_v4_config(raw)
|
|
parameters = payload["providers"][0]["models"][0]["parameters"]
|
|
|
|
assert parameters["defaults"] == {"output_token_limit": 2048, "temperature": 0.2}
|
|
assert parameters["purpose_overrides"]["main_agent"] == {
|
|
"output_token_limit": 1024,
|
|
"temperature": 0.3,
|
|
}
|
|
assert parameters["user_options"] == {}
|
|
for limit in payload["purpose_call_limits"].values():
|
|
assert "max_output_tokens" not in limit
|
|
assert payload["purpose_call_limits"]["main_agent"] == {
|
|
"max_attempts_per_run": 4
|
|
}
|
|
|
|
|
|
def test_v4_rejects_output_token_limit_above_model_capability():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["parameters"] = {
|
|
"defaults": {"output_token_limit": 16_384}
|
|
}
|
|
|
|
with pytest.raises(EvoRuntimeError) as raised:
|
|
normalize_v4_config(raw)
|
|
|
|
assert raised.value.code == "LLM_ROUTE_CONFIGURATION_REQUIRED"
|
|
assert raised.value.details[0]["code"] == "CONFIG_LIMIT_EXCEEDED"
|
|
|
|
|
|
def test_v4_lenient_clamps_output_token_limit_to_model_capability():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["parameters"] = {
|
|
"defaults": {"output_token_limit": 16_384}
|
|
}
|
|
warnings: list[dict] = []
|
|
|
|
payload = normalize_v4_config(raw, lenient=True, warnings=warnings)
|
|
|
|
defaults = payload["providers"][0]["models"][0]["parameters"]["defaults"]
|
|
assert defaults["output_token_limit"] == 8_192
|
|
assert any(item["code"] == "CONFIG_LIMIT_EXCEEDED" for item in warnings)
|
|
|
|
|
|
def test_v4_removes_legacy_tool_transport_from_admin_config():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["capabilities"]["tools"] = False
|
|
|
|
payload = normalize_v4_config(raw)
|
|
|
|
model = payload["providers"][0]["models"][0]
|
|
assert model["invocation"] == {"api_mode": "responses"}
|
|
|
|
|
|
def test_v4_runtime_projection_derives_tool_transport_from_capabilities():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["capabilities"]["tools"] = False
|
|
warnings: list[dict] = []
|
|
|
|
payload = normalize_v4_config(raw, lenient=True, warnings=warnings)
|
|
|
|
model = payload["providers"][0]["models"][0]
|
|
assert "tool_call_transport" not in model["invocation"]
|
|
assert not any(
|
|
item["code"] == "CONFIG_INVOCATION_CONTRACT_INVALID" for item in warnings
|
|
)
|
|
|
|
projection = project_v4_to_v3(
|
|
payload,
|
|
revision=1,
|
|
identity_key_id=_ring().current.key_id,
|
|
bindings={payload["providers"][0]["provider_id"]: 1},
|
|
)
|
|
config = EvoModelConfig.parse(projection, require_evidence=False)
|
|
route = config.concrete_routes(next(iter(config.route_selectors)))[0]
|
|
assert route.tool_call_transport == "disabled"
|
|
|
|
|
|
def test_v4_rejects_non_uuid_business_ids():
|
|
raw = _payload(provider_id="openai-main")
|
|
|
|
with pytest.raises(EvoRuntimeError) as raised:
|
|
normalize_v4_config(raw)
|
|
|
|
assert raised.value.code == "LLM_ROUTE_CONFIGURATION_REQUIRED"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"base_url",
|
|
[
|
|
"",
|
|
"not a url",
|
|
"ftp://user:pass@example.test/path?key=value#fragment",
|
|
"http://127.0.0.1:11434/v1",
|
|
"https://169.254.169.254/latest/meta-data",
|
|
"custom://model.internal:70000/path",
|
|
],
|
|
)
|
|
def test_v4_base_url_is_not_validated(base_url):
|
|
raw = _payload()
|
|
raw["providers"][0]["connection"]["base_url"] = base_url
|
|
|
|
payload = normalize_v4_config(raw)
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
projection = project_v4_to_v3(
|
|
payload,
|
|
revision=1,
|
|
identity_key_id=_ring().current.key_id,
|
|
bindings={provider_id: 1},
|
|
)
|
|
|
|
assert payload["providers"][0]["connection"]["base_url"] == base_url
|
|
assert projection["providers"][0]["connection"]["base_url"] == base_url
|
|
|
|
|
|
def test_unified_store_commits_config_and_secret_without_evidence_gate(tmp_path):
|
|
ring = _ring()
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
payload = normalize_v4_config(_payload())
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
plan = store.plan_credentials(payload, {provider_id: "sk-secret-value"})
|
|
projection = project_v4_to_v3(
|
|
payload,
|
|
revision=1,
|
|
identity_key_id=ring.current.key_id,
|
|
bindings=plan.bindings,
|
|
)
|
|
evidence = build_supported_v3_evidence(
|
|
projection,
|
|
identity_key_ring=ring,
|
|
verified_profiles={provider_id: frozenset({profile_id})},
|
|
)
|
|
request_hash = sha256_id({"payload": payload, "fingerprints": plan.fingerprints})
|
|
store.begin_operation(
|
|
"save-1",
|
|
request_hash=request_hash,
|
|
expected_revision=0,
|
|
actor="admin",
|
|
lease_owner="worker-1",
|
|
)
|
|
result = store.commit(
|
|
payload,
|
|
expected_revision=0,
|
|
operation_id="save-1",
|
|
actor="admin",
|
|
request_hash=request_hash,
|
|
credential_plan=plan,
|
|
evidence=evidence,
|
|
)
|
|
|
|
assert result.config_revision == 1
|
|
assert result.changed is True
|
|
runtime = store.load()
|
|
assert runtime.main_routes.default_alias == profile_id
|
|
assert store.resolve(
|
|
runtime.providers[provider_id].endpoints[provider_id].auth
|
|
).value == "sk-secret-value"
|
|
revision, admin_payload, credentials = store.get_admin_config()
|
|
assert revision == 1
|
|
assert admin_payload == payload
|
|
assert credentials[provider_id].configured is True
|
|
assert "sk-secret-value" not in str(credentials)
|
|
with sqlite3.connect(store.path) as connection:
|
|
assert connection.execute("SELECT COUNT(*) FROM model_config_revisions").fetchone()[0] == 1
|
|
assert connection.execute("SELECT COUNT(*) FROM provider_secret_versions").fetchone()[0] == 1
|
|
assert connection.execute("SELECT COUNT(*) FROM capability_evidence").fetchone()[0] == 0
|
|
store.record_runtime_observation(
|
|
config_revision=1,
|
|
provider_id=provider_id,
|
|
model_profile_id=profile_id,
|
|
provider_model_id="gpt-test",
|
|
api_mode="responses",
|
|
purpose="main_agent",
|
|
outcome="succeeded",
|
|
error_code=None,
|
|
strategy={"structured_output": False},
|
|
)
|
|
summary = store.runtime_observation_summary()
|
|
assert len(summary) == 1
|
|
assert summary[0]["model_profile_id"] == profile_id
|
|
assert summary[0]["provider_id"] == provider_id
|
|
assert summary[0]["latest_outcome"] == "succeeded"
|
|
assert summary[0]["latest_error_code"] is None
|
|
assert summary[0]["total_calls"] == 1
|
|
assert summary[0]["successful_calls"] == 1
|
|
assert summary[0]["strategy"] == {"structured_output": False}
|
|
assert summary[0]["latest_at"]
|
|
|
|
|
|
def test_unified_store_idempotency_rejects_same_id_different_request(tmp_path):
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=_ring(),
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
store.begin_operation(
|
|
"save-1",
|
|
request_hash="hmac:one",
|
|
expected_revision=0,
|
|
actor="admin",
|
|
lease_owner="worker-1",
|
|
)
|
|
|
|
with pytest.raises(EvoRuntimeError) as raised:
|
|
store.begin_operation(
|
|
"save-1",
|
|
request_hash="hmac:two",
|
|
expected_revision=0,
|
|
actor="admin",
|
|
lease_owner="worker-2",
|
|
)
|
|
|
|
assert raised.value.code == "IDEMPOTENCY_CONFLICT"
|
|
|
|
|
|
def test_operation_failure_preserves_last_progress(tmp_path):
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=_ring(),
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
store.begin_operation(
|
|
"save-1", request_hash="request-1", expected_revision=0,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
store.set_operation_status(
|
|
"save-1", "PROBING", progress={"completed": 2, "total": 3}
|
|
)
|
|
store.set_operation_status(
|
|
"save-1",
|
|
"FAILED_RETRYABLE",
|
|
stage="PROBING",
|
|
error_code="MODEL_PROBE_FAILED",
|
|
error_details=({"path": "models.test", "code": "MODEL_TIMEOUT"},),
|
|
)
|
|
|
|
operation = store.get_operation("save-1")
|
|
assert operation is not None
|
|
assert operation["progress"] == {"completed": 2, "total": 3}
|
|
assert operation["error_details"] == [
|
|
{"path": "models.test", "code": "MODEL_TIMEOUT"}
|
|
]
|
|
|
|
|
|
|
|
def test_unified_store_load_ignores_historical_evidence(tmp_path):
|
|
ring = _ring()
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
payload = normalize_v4_config(_payload())
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
plan = store.plan_credentials(payload, {provider_id: "sk-secret-value"})
|
|
projection = project_v4_to_v3(
|
|
payload, revision=1, identity_key_id=ring.current.key_id, bindings=plan.bindings
|
|
)
|
|
evidence = build_supported_v3_evidence(
|
|
projection,
|
|
identity_key_ring=ring,
|
|
verified_profiles={provider_id: frozenset({profile_id})},
|
|
)
|
|
store.begin_operation(
|
|
"save-1", request_hash="request-1", expected_revision=0,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
store.commit(
|
|
payload, expected_revision=0, operation_id="save-1", actor="admin",
|
|
request_hash="request-1", credential_plan=plan, evidence=evidence,
|
|
)
|
|
# The supplied evidence is intentionally not persisted or read by load.
|
|
# A legacy/tampered evidence row therefore cannot make the revision stale.
|
|
assert store.load_revision(1).config_revision == 1
|
|
|
|
|
|
def test_rollback_publishes_a_new_monotonic_revision(tmp_path):
|
|
ring = _ring()
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
first = normalize_v4_config(_payload())
|
|
provider_id = first["providers"][0]["provider_id"]
|
|
profile_id = first["default_model_profile_id"]
|
|
def publish(payload: dict, revision: int, operation_id: str, request_hash: str):
|
|
plan = store.plan_credentials(
|
|
payload, {provider_id: "sk-secret-value"} if revision == 1 else {}
|
|
)
|
|
projection = project_v4_to_v3(
|
|
payload,
|
|
revision=revision,
|
|
identity_key_id=ring.current.key_id,
|
|
bindings=plan.bindings,
|
|
)
|
|
evidence = build_supported_v3_evidence(
|
|
projection,
|
|
identity_key_ring=ring,
|
|
verified_profiles={provider_id: frozenset({profile_id})},
|
|
)
|
|
store.begin_operation(
|
|
operation_id, request_hash=request_hash, expected_revision=revision - 1,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
return store.commit(
|
|
payload, expected_revision=revision - 1, operation_id=operation_id,
|
|
actor="admin", request_hash=request_hash, credential_plan=plan,
|
|
evidence=evidence,
|
|
)
|
|
|
|
publish(first, 1, "save-1", "request-1")
|
|
second = json.loads(json.dumps(first))
|
|
second["providers"][0]["display_name"] = "Changed display name"
|
|
publish(second, 2, "save-2", "request-2")
|
|
store.begin_operation(
|
|
"rollback-1", request_hash="rollback-request", expected_revision=2,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
result = store.rollback(
|
|
target_revision=1,
|
|
expected_revision=2,
|
|
operation_id="rollback-1",
|
|
actor="admin",
|
|
request_hash="rollback-request",
|
|
)
|
|
|
|
assert result.config_revision == 3
|
|
assert store.current_revision() == 3
|
|
assert store.get_admin_config()[1]["providers"][0]["display_name"] == "OpenAI Production"
|
|
assert store.load().config_revision == 3
|
|
|
|
|
|
def test_identity_key_rotation_does_not_rotate_unchanged_credential(tmp_path):
|
|
first_ring = _ring()
|
|
path = tmp_path / "model-config.sqlite"
|
|
store = UnifiedModelConfigStore(
|
|
path,
|
|
identity_key_ring=first_ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
payload = normalize_v4_config(_payload())
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
plan = store.plan_credentials(payload, {provider_id: "sk-same-secret"})
|
|
projection = project_v4_to_v3(
|
|
payload, revision=1, identity_key_id=first_ring.current.key_id,
|
|
bindings=plan.bindings,
|
|
)
|
|
evidence = build_supported_v3_evidence(
|
|
projection,
|
|
identity_key_ring=first_ring,
|
|
verified_profiles={provider_id: frozenset({profile_id})},
|
|
)
|
|
store.begin_operation(
|
|
"save-1", request_hash="request-1", expected_revision=0,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
store.commit(
|
|
payload, expected_revision=0, operation_id="save-1", actor="admin",
|
|
request_hash="request-1", credential_plan=plan, evidence=evidence,
|
|
)
|
|
rotated_ring = HmacKeyRing(
|
|
KeyMaterial.create("identity-v2", "j" * 32), retained=(first_ring.current,)
|
|
)
|
|
rotated_store = UnifiedModelConfigStore(
|
|
path,
|
|
identity_key_ring=rotated_ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
|
|
rotated_plan = rotated_store.plan_credentials(
|
|
payload, {provider_id: "sk-same-secret"}
|
|
)
|
|
|
|
assert rotated_plan.new_versions == frozenset()
|
|
assert rotated_plan.bindings[provider_id] == 1
|
|
assert rotated_plan.fingerprints[provider_id] == plan.fingerprints[provider_id]
|
|
|
|
|
|
def test_evidence_requires_observed_results_for_every_declared_capability():
|
|
ring = _ring()
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["capabilities"] = {
|
|
"text": True,
|
|
"vision": True,
|
|
"tools": True,
|
|
}
|
|
payload = normalize_v4_config(raw)
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
projection = project_v4_to_v3(
|
|
payload,
|
|
revision=1,
|
|
identity_key_id=ring.current.key_id,
|
|
bindings={provider_id: 1},
|
|
)
|
|
|
|
with pytest.raises(EvoRuntimeError) as raised:
|
|
build_supported_v3_evidence(
|
|
projection,
|
|
identity_key_ring=ring,
|
|
probe_results={
|
|
provider_id: {profile_id: {"connectivity": "supported"}}
|
|
},
|
|
)
|
|
|
|
assert raised.value.code == "CAPABILITY_EVIDENCE_STALE"
|
|
|
|
|
|
def test_historical_evidence_is_not_reused_for_route_admission(tmp_path):
|
|
ring = _ring()
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
payload = normalize_v4_config(_payload())
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
plan = store.plan_credentials(payload, {provider_id: "sk-secret-value"})
|
|
projection = project_v4_to_v3(
|
|
payload,
|
|
revision=1,
|
|
identity_key_id=ring.current.key_id,
|
|
bindings=plan.bindings,
|
|
)
|
|
evidence = build_supported_v3_evidence(
|
|
projection,
|
|
identity_key_ring=ring,
|
|
verified_profiles={provider_id: frozenset({profile_id})},
|
|
)
|
|
store.begin_operation(
|
|
"save-1", request_hash="request-1", expected_revision=0,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
store.commit(
|
|
payload, expected_revision=0, operation_id="save-1", actor="admin",
|
|
request_hash="request-1", credential_plan=plan, evidence=evidence,
|
|
)
|
|
|
|
next_payload = json.loads(json.dumps(payload))
|
|
next_payload["providers"][0]["display_name"] = "Renamed Provider"
|
|
next_plan = store.plan_credentials(next_payload, {})
|
|
next_projection = project_v4_to_v3(
|
|
next_payload,
|
|
revision=2,
|
|
identity_key_id=ring.current.key_id,
|
|
bindings=next_plan.bindings,
|
|
)
|
|
observed, windows, missing = store.reusable_probe_results(
|
|
next_projection,
|
|
fingerprints=next_plan.fingerprints,
|
|
versions=next_plan.bindings,
|
|
)
|
|
|
|
assert observed == {}
|
|
assert windows == {}
|
|
assert missing == frozenset({(provider_id, profile_id)})
|
|
|
|
|
|
def test_availability_reflects_enabled_state(tmp_path):
|
|
ring = _ring()
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
raw = _payload()
|
|
disabled_profile_id = str(uuid.uuid4())
|
|
disabled = json.loads(json.dumps(raw["providers"][0]["models"][0]))
|
|
disabled.update(
|
|
{
|
|
"model_profile_id": disabled_profile_id,
|
|
"provider_model_id": "disabled-model",
|
|
"display_name": "Disabled",
|
|
"enabled": False,
|
|
}
|
|
)
|
|
raw["providers"][0]["models"].append(disabled)
|
|
payload = normalize_v4_config(raw)
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
plan = store.plan_credentials(payload, {provider_id: "sk-secret-value"})
|
|
store.begin_operation(
|
|
"save-1", request_hash="request-1", expected_revision=0,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
store.commit(
|
|
payload, expected_revision=0, operation_id="save-1", actor="admin",
|
|
request_hash="request-1", credential_plan=plan, evidence=[],
|
|
)
|
|
|
|
availability = store.get_model_availability()
|
|
|
|
assert availability[profile_id] == {
|
|
"model_profile_id": profile_id,
|
|
"enabled": True,
|
|
"selectable": True,
|
|
}
|
|
assert availability[disabled_profile_id] == {
|
|
"model_profile_id": disabled_profile_id,
|
|
"enabled": False,
|
|
"selectable": False,
|
|
}
|
|
|
|
|
|
def test_schema_upgrade_adds_availability_and_operation_columns(tmp_path):
|
|
path = tmp_path / "model-config.sqlite"
|
|
UnifiedModelConfigStore(
|
|
path,
|
|
identity_key_ring=_ring(),
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
with sqlite3.connect(path) as connection:
|
|
connection.execute("DROP INDEX idx_capability_evidence_semantics")
|
|
connection.execute("ALTER TABLE active_model_config DROP COLUMN evidence_epoch")
|
|
connection.execute("ALTER TABLE capability_evidence DROP COLUMN credential_version")
|
|
connection.execute("ALTER TABLE model_config_operations DROP COLUMN stage")
|
|
connection.execute("ALTER TABLE model_config_operations DROP COLUMN progress_json")
|
|
connection.execute("ALTER TABLE model_config_operations DROP COLUMN error_details_json")
|
|
|
|
UnifiedModelConfigStore(
|
|
path,
|
|
identity_key_ring=_ring(),
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
|
|
with sqlite3.connect(path) as connection:
|
|
assert "evidence_epoch" in {
|
|
row[1] for row in connection.execute("PRAGMA table_info(active_model_config)")
|
|
}
|
|
assert "credential_version" in {
|
|
row[1] for row in connection.execute("PRAGMA table_info(capability_evidence)")
|
|
}
|
|
operation_columns = {
|
|
row[1] for row in connection.execute("PRAGMA table_info(model_config_operations)")
|
|
}
|
|
assert {"stage", "progress_json", "error_details_json"} <= operation_columns
|
|
|
|
|
|
def test_v3_runtime_accepts_expired_historical_evidence(tmp_path):
|
|
ring = _ring()
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
payload = normalize_v4_config(_payload())
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
plan = store.plan_credentials(payload, {provider_id: "sk-secret-value"})
|
|
projection = project_v4_to_v3(
|
|
payload, revision=1, identity_key_id=ring.current.key_id,
|
|
bindings=plan.bindings,
|
|
)
|
|
now = datetime.now(UTC)
|
|
evidence = build_supported_v3_evidence(
|
|
projection,
|
|
identity_key_ring=ring,
|
|
verified_profiles={provider_id: frozenset({profile_id})},
|
|
evidence_windows={
|
|
provider_id: {
|
|
profile_id: (
|
|
(now - timedelta(hours=2)).isoformat(timespec="microseconds"),
|
|
(now - timedelta(hours=1)).isoformat(timespec="microseconds"),
|
|
)
|
|
}
|
|
},
|
|
)
|
|
store.begin_operation(
|
|
"save-1", request_hash="request-1", expected_revision=0,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
store.commit(
|
|
payload, expected_revision=0, operation_id="save-1", actor="admin",
|
|
request_hash="request-1", credential_plan=plan, evidence=evidence,
|
|
)
|
|
config = store.load()
|
|
authority = HmacGrantAuthority("r" * 32)
|
|
runtime = EvoModelRuntime(
|
|
store,
|
|
admission_verifier=authority,
|
|
quote_authority=authority,
|
|
identity_key_ring=ring,
|
|
secret_resolver=store.resolve,
|
|
)
|
|
selector = config.resolve_main_selector(None)
|
|
route = config.concrete_routes(selector.selector_id)[0]
|
|
|
|
resolved = runtime._resolve_route(config, route, "main_agent")
|
|
|
|
assert resolved.identity.model_id == "gpt-test"
|
|
|
|
|
|
def test_lenient_normalization_ignores_unknown_fields_with_warning():
|
|
raw = _payload()
|
|
raw["unexpected_top"] = True
|
|
raw["providers"][0]["unexpected_provider"] = "x"
|
|
raw["providers"][0]["models"][0]["unexpected_model"] = 1
|
|
warnings: list[dict] = []
|
|
|
|
payload = normalize_v4_config(raw, lenient=True, warnings=warnings)
|
|
|
|
assert payload["schema_version"] == 4
|
|
assert [item["code"] for item in warnings] == ["CONFIG_UNKNOWN_FIELDS"] * 3
|
|
|
|
|
|
def test_lenient_normalization_clamps_out_of_range_and_timeout_conflict():
|
|
raw = _payload()
|
|
raw["providers"][0]["connection"] = {
|
|
"base_url": "https://api.openai.com/v1",
|
|
"connect_timeout_seconds": 120,
|
|
"attempt_timeout_seconds": 30,
|
|
}
|
|
warnings: list[dict] = []
|
|
|
|
payload = normalize_v4_config(raw, lenient=True, warnings=warnings)
|
|
connection = payload["providers"][0]["connection"]
|
|
|
|
assert connection["connect_timeout_seconds"] == 60
|
|
assert connection["attempt_timeout_seconds"] == 60
|
|
codes = {item["code"] for item in warnings}
|
|
assert "CONFIG_VALUE_OUT_OF_RANGE" in codes
|
|
assert "CONFIG_VALUE_CONFLICT" in codes
|
|
|
|
|
|
def test_lenient_normalization_forces_text_capability_and_keeps_unknown_purpose():
|
|
raw = _payload()
|
|
raw["providers"][0]["models"][0]["capabilities"] = {"text": False, "vision": True}
|
|
raw["providers"][0]["models"][0]["parameters"] = {
|
|
"purpose_overrides": {"custom_purpose": {"temperature": 0.5}}
|
|
}
|
|
warnings: list[dict] = []
|
|
|
|
payload = normalize_v4_config(raw, lenient=True, warnings=warnings)
|
|
model = payload["providers"][0]["models"][0]
|
|
|
|
assert model["capabilities"]["text"] is True
|
|
assert model["parameters"]["purpose_overrides"]["custom_purpose"] == {"temperature": 0.5}
|
|
assert any(item["code"] == "CONFIG_REQUIRED" for item in warnings)
|
|
assert any(item["code"] == "CONFIG_REFERENCE_INVALID" for item in warnings)
|
|
|
|
|
|
def test_strict_normalization_still_rejects_unknown_fields():
|
|
raw = _payload()
|
|
raw["unexpected_top"] = True
|
|
|
|
with pytest.raises(EvoRuntimeError):
|
|
normalize_v4_config(raw)
|
|
|
|
|
|
def test_unverified_enabled_model_is_selectable(tmp_path):
|
|
ring = _ring()
|
|
store = UnifiedModelConfigStore(
|
|
tmp_path / "model-config.sqlite",
|
|
identity_key_ring=ring,
|
|
encryption_keys={"enc-v1": "e" * 32},
|
|
current_encryption_key_id="enc-v1",
|
|
)
|
|
payload = normalize_v4_config(_payload())
|
|
provider_id = payload["providers"][0]["provider_id"]
|
|
profile_id = payload["default_model_profile_id"]
|
|
plan = store.plan_credentials(payload, {provider_id: "sk-secret-value"})
|
|
store.begin_operation(
|
|
"save-1", request_hash="request-1", expected_revision=0,
|
|
actor="admin", lease_owner="worker",
|
|
)
|
|
store.commit(
|
|
payload, expected_revision=0, operation_id="save-1", actor="admin",
|
|
request_hash="request-1", credential_plan=plan, evidence=[],
|
|
)
|
|
|
|
availability = store.get_model_availability()[profile_id]
|
|
|
|
assert availability == {
|
|
"model_profile_id": profile_id,
|
|
"enabled": True,
|
|
"selectable": True,
|
|
}
|