"""Authoritative generation-route contract shared by deploy and runtime. This module intentionally depends only on the Python standard library so the same validation can run before a Space is mutated and before its API starts. """ from __future__ import annotations from typing import Mapping LLM_SYNOD_BACKEND = "llm-synod" CATHOLIC_WEBSITE_VARIANT = "catholic" RESEARCH_WEBSITE_VARIANT = "research" WEBSITE_VARIANTS = (CATHOLIC_WEBSITE_VARIANT, RESEARCH_WEBSITE_VARIANT) DEFAULT_LLM_SYNOD_MODEL_ID = "synderesis-v3-catholic-llm-synod" DEFAULT_RESEARCH_LLM_SYNOD_MODEL_ID = "synderesis-v3-research-sol-opus" DEFAULT_LLM_SYNOD_QWEN_MODEL = "qwen/qwen3.7-max" DEFAULT_LLM_SYNOD_GROK_MODEL = "x-ai/grok-4.5" DEFAULT_LLM_SYNOD_MINIMAX_MODEL = "minimax/minimax-m3" DEFAULT_LLM_SYNOD_DEEPSEEK_MODEL = "deepseek/deepseek-v4-pro" DEFAULT_LLM_SYNOD_GLM_MODEL = "z-ai/glm-5.2" PRO_MODEL_ID = "moonshotai/kimi-k3" DEFAULT_LLM_SYNOD_OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1" DEFAULT_RESEARCH_OPENAI_MODEL = "gpt-5.6-sol" DEFAULT_RESEARCH_ANTHROPIC_MODEL = "claude-opus-5" DEFAULT_RESEARCH_OPENAI_BASE_URL = "https://api.openai.com/v1" DEFAULT_RESEARCH_ANTHROPIC_BASE_URL = "https://api.anthropic.com/v1" CATHOLIC_LLM_SYNOD_ROUTE_VARIABLES = { "SYNDERESIS_LLM_SYNOD_MODEL_ID": DEFAULT_LLM_SYNOD_MODEL_ID, "SYNDERESIS_LLM_SYNOD_QWEN_MODEL": DEFAULT_LLM_SYNOD_QWEN_MODEL, "SYNDERESIS_LLM_SYNOD_GROK_MODEL": DEFAULT_LLM_SYNOD_GROK_MODEL, "SYNDERESIS_LLM_SYNOD_MINIMAX_MODEL": DEFAULT_LLM_SYNOD_MINIMAX_MODEL, "SYNDERESIS_LLM_SYNOD_DEEPSEEK_MODEL": DEFAULT_LLM_SYNOD_DEEPSEEK_MODEL, "SYNDERESIS_LLM_SYNOD_GLM_MODEL": DEFAULT_LLM_SYNOD_GLM_MODEL, "SYNDERESIS_LLM_SYNOD_OPENROUTER_BASE_URL": ( DEFAULT_LLM_SYNOD_OPENROUTER_BASE_URL ), } RESEARCH_LLM_SYNOD_ROUTE_VARIABLES = { "SYNDERESIS_LLM_SYNOD_MODEL_ID": DEFAULT_RESEARCH_LLM_SYNOD_MODEL_ID, "SYNDERESIS_RESEARCH_OPENAI_MODEL": DEFAULT_RESEARCH_OPENAI_MODEL, "SYNDERESIS_RESEARCH_ANTHROPIC_MODEL": DEFAULT_RESEARCH_ANTHROPIC_MODEL, "SYNDERESIS_RESEARCH_OPENAI_BASE_URL": DEFAULT_RESEARCH_OPENAI_BASE_URL, "SYNDERESIS_RESEARCH_ANTHROPIC_BASE_URL": ( DEFAULT_RESEARCH_ANTHROPIC_BASE_URL ), } LEGACY_LLM_SYNOD_ROUTE_VARIABLE_NAMES = ( "SYNDERESIS_FUSION_MODEL_ID", "SYNDERESIS_FUSION_QWEN_MODEL", "SYNDERESIS_FUSION_GROK_MODEL", "SYNDERESIS_FUSION_MINIMAX_MODEL", "SYNDERESIS_FUSION_DEEPSEEK_MODEL", "SYNDERESIS_FUSION_GLM_MODEL", "SYNDERESIS_FUSION_OPENROUTER_BASE_URL", "SYNDERESIS_FUSION_OPENROUTER_REFERER", "SYNDERESIS_FUSION_OPENROUTER_TITLE", ) REMOVED_MODEL_ROUTE_VARIABLE_NAMES = ( "SYNDERESIS_LLM_SYNOD_OPENROUTER_REFERER", "SYNDERESIS_LLM_SYNOD_OPENROUTER_TITLE", "SYNDERESIS_ALLOWED_MODELS", "SYNDERESIS_HF_MODEL_ID", "HF_MODEL_ID", "HF_INFERENCE_BASE_URL", "HF_INFERENCE_ENDPOINT_URL", "SYNDERESIS_TINKER_MODEL_PATH", "TINKER_MODEL_PATH", ) ALL_KNOWN_MODEL_ROUTE_VARIABLE_NAMES = tuple( dict.fromkeys( ( *CATHOLIC_LLM_SYNOD_ROUTE_VARIABLES, *RESEARCH_LLM_SYNOD_ROUTE_VARIABLES, *LEGACY_LLM_SYNOD_ROUTE_VARIABLE_NAMES, *REMOVED_MODEL_ROUTE_VARIABLE_NAMES, ) ) ) MANAGED_ROUTE_INPUT_VARIABLE_NAMES = tuple( dict.fromkeys( ( *CATHOLIC_LLM_SYNOD_ROUTE_VARIABLES, *RESEARCH_LLM_SYNOD_ROUTE_VARIABLES, *LEGACY_LLM_SYNOD_ROUTE_VARIABLE_NAMES, *REMOVED_MODEL_ROUTE_VARIABLE_NAMES, ) ) ) class GenerationRouteContractError(ValueError): """Raised when a deployment or runtime route is not canonical.""" def _nonempty(environment: Mapping[str, str], name: str) -> str: return str(environment.get(name, "") or "").strip() def validate_authoritative_route_values( values: Mapping[str, str] | None = None, ) -> None: """Detect accidental drift in checked-in generation route constants.""" supplied = values or {} expected = { **CATHOLIC_LLM_SYNOD_ROUTE_VARIABLES, **RESEARCH_LLM_SYNOD_ROUTE_VARIABLES, "SYNDERESIS_PRO_MODEL": PRO_MODEL_ID, } family_prefixes = { "SYNDERESIS_LLM_SYNOD_QWEN_MODEL": "qwen/", "SYNDERESIS_LLM_SYNOD_GROK_MODEL": "x-ai/", "SYNDERESIS_LLM_SYNOD_MINIMAX_MODEL": "minimax/", "SYNDERESIS_LLM_SYNOD_DEEPSEEK_MODEL": "deepseek/", "SYNDERESIS_LLM_SYNOD_GLM_MODEL": "z-ai/", "SYNDERESIS_PRO_MODEL": "moonshotai/", "SYNDERESIS_RESEARCH_OPENAI_MODEL": "gpt-", "SYNDERESIS_RESEARCH_ANTHROPIC_MODEL": "claude-", } for name, canonical in expected.items(): value = str(supplied.get(name, canonical)) if value != canonical: prefix = family_prefixes.get(name) if prefix and not value.startswith(prefix): raise GenerationRouteContractError( f"{name} is configured for the wrong model family" ) if name.endswith("_BASE_URL"): raise GenerationRouteContractError( f"{name} must use its canonical HTTPS API origin" ) raise GenerationRouteContractError( f"{name} must use the current canonical value" ) def deployment_route_wiring( *, backend: str, website_variant: str, model_id: str, environment: Mapping[str, str] | None = None, ) -> tuple[dict[str, str], tuple[str, ...]]: """Return canonical Space variables and stale variables to remove.""" validate_authoritative_route_values() if website_variant not in WEBSITE_VARIANTS: raise GenerationRouteContractError( "website variant must be catholic or research" ) if ( website_variant == RESEARCH_WEBSITE_VARIANT and backend != LLM_SYNOD_BACKEND ): raise GenerationRouteContractError( "the research website variant requires llm-synod" ) supplied = environment or {} if backend != LLM_SYNOD_BACKEND: variables: dict[str, str] = {} elif website_variant == CATHOLIC_WEBSITE_VARIANT: if model_id != DEFAULT_LLM_SYNOD_MODEL_ID: raise GenerationRouteContractError( "Catholic deployment requires the canonical LLM Synod model ID" ) variables = dict(CATHOLIC_LLM_SYNOD_ROUTE_VARIABLES) else: if model_id not in { DEFAULT_LLM_SYNOD_MODEL_ID, DEFAULT_RESEARCH_LLM_SYNOD_MODEL_ID, }: raise GenerationRouteContractError( "Research deployment requires the canonical research LLM Synod model ID" ) variables = dict(RESEARCH_LLM_SYNOD_ROUTE_VARIABLES) if backend == LLM_SYNOD_BACKEND: validate_runtime_route( backend=backend, website_variant=website_variant, model_id=variables["SYNDERESIS_LLM_SYNOD_MODEL_ID"], qwen_model=variables.get( "SYNDERESIS_LLM_SYNOD_QWEN_MODEL", DEFAULT_LLM_SYNOD_QWEN_MODEL, ), grok_model=variables.get( "SYNDERESIS_LLM_SYNOD_GROK_MODEL", DEFAULT_LLM_SYNOD_GROK_MODEL, ), minimax_model=variables.get( "SYNDERESIS_LLM_SYNOD_MINIMAX_MODEL", DEFAULT_LLM_SYNOD_MINIMAX_MODEL, ), deepseek_model=variables.get( "SYNDERESIS_LLM_SYNOD_DEEPSEEK_MODEL", DEFAULT_LLM_SYNOD_DEEPSEEK_MODEL, ), glm_model=variables.get( "SYNDERESIS_LLM_SYNOD_GLM_MODEL", DEFAULT_LLM_SYNOD_GLM_MODEL, ), openrouter_base_url=variables.get( "SYNDERESIS_LLM_SYNOD_OPENROUTER_BASE_URL", DEFAULT_LLM_SYNOD_OPENROUTER_BASE_URL, ), research_openai_model=variables.get( "SYNDERESIS_RESEARCH_OPENAI_MODEL", DEFAULT_RESEARCH_OPENAI_MODEL, ), research_anthropic_model=variables.get( "SYNDERESIS_RESEARCH_ANTHROPIC_MODEL", DEFAULT_RESEARCH_ANTHROPIC_MODEL, ), research_openai_base_url=variables.get( "SYNDERESIS_RESEARCH_OPENAI_BASE_URL", DEFAULT_RESEARCH_OPENAI_BASE_URL, ), research_anthropic_base_url=variables.get( "SYNDERESIS_RESEARCH_ANTHROPIC_BASE_URL", DEFAULT_RESEARCH_ANTHROPIC_BASE_URL, ), environment=supplied, ) clear_variable_names = tuple( name for name in ALL_KNOWN_MODEL_ROUTE_VARIABLE_NAMES if name not in variables ) return variables, clear_variable_names def validate_runtime_route( *, backend: str, website_variant: str, model_id: str, qwen_model: str, grok_model: str, minimax_model: str, deepseek_model: str, glm_model: str, openrouter_base_url: str, research_openai_model: str, research_anthropic_model: str, research_openai_base_url: str, research_anthropic_base_url: str, environment: Mapping[str, str] | None = None, ) -> None: """Fail closed unless the active runtime route is exactly canonical.""" if backend != LLM_SYNOD_BACKEND: return if website_variant not in WEBSITE_VARIANTS: raise GenerationRouteContractError( "website variant must be catholic or research" ) supplied = environment or {} for name in ( *LEGACY_LLM_SYNOD_ROUTE_VARIABLE_NAMES, *REMOVED_MODEL_ROUTE_VARIABLE_NAMES, ): if _nonempty(supplied, name): raise GenerationRouteContractError( f"{name} is a legacy or unsupported generation-route override" ) if website_variant == CATHOLIC_WEBSITE_VARIANT: for name in RESEARCH_LLM_SYNOD_ROUTE_VARIABLES: if ( name != "SYNDERESIS_LLM_SYNOD_MODEL_ID" and _nonempty(supplied, name) ): raise GenerationRouteContractError( f"{name} is a research route and cannot configure a Catholic deployment" ) actual = { "SYNDERESIS_LLM_SYNOD_MODEL_ID": model_id, "SYNDERESIS_LLM_SYNOD_QWEN_MODEL": qwen_model, "SYNDERESIS_LLM_SYNOD_GROK_MODEL": grok_model, "SYNDERESIS_LLM_SYNOD_MINIMAX_MODEL": minimax_model, "SYNDERESIS_LLM_SYNOD_DEEPSEEK_MODEL": deepseek_model, "SYNDERESIS_LLM_SYNOD_GLM_MODEL": glm_model, "SYNDERESIS_LLM_SYNOD_OPENROUTER_BASE_URL": openrouter_base_url, } expected = CATHOLIC_LLM_SYNOD_ROUTE_VARIABLES else: for name in CATHOLIC_LLM_SYNOD_ROUTE_VARIABLES: if ( name != "SYNDERESIS_LLM_SYNOD_MODEL_ID" and _nonempty(supplied, name) ): raise GenerationRouteContractError( f"{name} is a Catholic route and cannot configure a research deployment" ) actual = { "SYNDERESIS_LLM_SYNOD_MODEL_ID": model_id, "SYNDERESIS_RESEARCH_OPENAI_MODEL": research_openai_model, "SYNDERESIS_RESEARCH_ANTHROPIC_MODEL": research_anthropic_model, "SYNDERESIS_RESEARCH_OPENAI_BASE_URL": research_openai_base_url, "SYNDERESIS_RESEARCH_ANTHROPIC_BASE_URL": ( research_anthropic_base_url ), } expected = RESEARCH_LLM_SYNOD_ROUTE_VARIABLES for name, canonical in expected.items(): configured = _nonempty(supplied, name) if actual[name] != canonical or ( configured and configured != canonical ): if name.endswith("_BASE_URL"): raise GenerationRouteContractError( f"{name} must use its canonical HTTPS API origin" ) raise GenerationRouteContractError( f"{name} must use the current canonical value" )