Spaces:
Sleeping
Sleeping
File size: 4,874 Bytes
de0f30b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | """Server-owned runtime policy for hosted SAGE runs.
The local service historically accepted the complete :class:`RunRequest`
surface so developers could select providers, stages, artifacts, and tuning
parameters from scripts. Hosted callers must not have that authority. This
module provides an opt-in boundary that replaces every operational field with
server environment configuration while retaining only the cohort text and the
internal human-confirmation choice.
"""
from __future__ import annotations
import os
from pydantic import BaseModel, Field
from .models import RunRequest
LOCK_RUNTIME_CONFIG_ENV = "SAGE_LOCK_RUNTIME_CONFIG"
HOSTED_MODE_ENV = "SAGE_HOSTED_MODE"
_TRUE_VALUES = frozenset({"1", "true", "yes", "on"})
def env_flag(name: str) -> bool:
"""Return whether an opt-in environment flag is explicitly enabled."""
return str(os.getenv(name) or "").strip().casefold() in _TRUE_VALUES
def hosted_mode_enabled() -> bool:
"""Whether the backend is running without its legacy local HTML surface."""
return env_flag(HOSTED_MODE_ENV)
def runtime_config_locked() -> bool:
"""Whether request-controlled operational configuration is disabled.
Hosted mode implies the lock so a deployment cannot hide the legacy UI
while accidentally leaving its model/path controls available through the
raw API. The dedicated flag also permits locked local proxy testing.
"""
return hosted_mode_enabled() or env_flag(LOCK_RUNTIME_CONFIG_ENV)
class LockedRuntimeConfig(BaseModel):
"""Validated non-secret policy values read only from the backend env."""
max_llm_calls: int = Field(default=1000, ge=1, le=5000)
temperature: float = Field(default=0.0, ge=0.0, le=2.0)
llm_max_retries: int = Field(default=2, ge=0, le=10)
timeout_seconds: float = Field(default=90.0, ge=1.0, le=600.0)
stage2_top_k: int = Field(default=10, ge=1, le=100)
stage2_mode: str = "adopt_suggestions"
stage2_llm_verify_results: bool = False
stage2_llm_filter_noise: bool = False
stage2_llm_retry_limit: int = Field(default=1, ge=0, le=5)
@classmethod
def from_environment(cls) -> "LockedRuntimeConfig":
mapping = {
"max_llm_calls": "SAGE_MAX_LLM_CALLS",
"temperature": "SAGE_LLM_TEMPERATURE",
"llm_max_retries": "SAGE_LLM_MAX_RETRIES",
"timeout_seconds": "SAGE_LLM_TIMEOUT",
"stage2_top_k": "SAGE_STAGE2_TOP_K",
"stage2_mode": "SAGE_STAGE2_MODE",
"stage2_llm_verify_results": "SAGE_STAGE2_LLM_VERIFY_RESULTS",
"stage2_llm_filter_noise": "SAGE_STAGE2_LLM_FILTER_NOISE",
"stage2_llm_retry_limit": "SAGE_STAGE2_LLM_RETRY_LIMIT",
}
values = {
field_name: os.environ[environment_name]
for field_name, environment_name in mapping.items()
if environment_name in os.environ and os.environ[environment_name].strip()
}
return cls.model_validate(values)
def apply_runtime_config_policy(request: RunRequest) -> RunRequest:
"""Return a server-owned request when the hosted runtime lock is enabled.
Provider, model, response-format, endpoint, and credential selection remain
in their existing provider-specific environment variables. Setting their
request fields to ``None`` makes the provider factory consult only that
backend configuration and prevents a raw client from overriding it.
"""
if not runtime_config_locked():
return request
configured = LockedRuntimeConfig.from_environment()
updates = {
# Hosted runs are always end-to-end from inline text. HITL is the one
# safe workflow choice retained for the internal frontend.
"input_path": None,
"case_id": None,
"run_id": None,
"start_stage": 1,
"end_stage": 2 if request.require_human_confirmation else 3,
"ir_json_path": None,
"retrieval_context_path": None,
# Do not load a repository .env file in a container. Provider/model
# values and secrets come from the process environment only.
"llm_dotenv_path": None,
"llm_provider": None,
"llm_model": None,
"llm_response_format": None,
"max_llm_calls": configured.max_llm_calls,
"temperature": configured.temperature,
"llm_max_retries": configured.llm_max_retries,
"timeout_seconds": configured.timeout_seconds,
"stage2_top_k": configured.stage2_top_k,
"stage2_mode": configured.stage2_mode,
"stage2_llm_verify_results": configured.stage2_llm_verify_results,
"stage2_llm_filter_noise": configured.stage2_llm_filter_noise,
"stage2_llm_retry_limit": configured.stage2_llm_retry_limit,
}
return RunRequest.model_validate({**request.model_dump(mode="python"), **updates})
|