#!/usr/bin/env python3 """Start the Synderesis proxy inside a Hugging Face Docker Space.""" from __future__ import annotations import json import os import sys from decimal import InvalidOperation, Decimal from pathlib import Path from huggingface_hub import hf_hub_download DEFAULT_TINKER_MODEL_PATH = ( "tinker://69e3ecac-e413-51b3-b3ff-2eb20d22c878:train:1/" "sampler_weights/synderesis-nemotron-super-catholic-v2-style-20260616T170321Z" ) DEFAULT_BACKEND = "llm-synod" DEPLOYMENT_METADATA_PATH = Path("synderesis_deployment.json") PERSISTENT_STATE_DIR = Path("/data") FALLBACK_RUNTIME_DIR = Path("/tmp/synderesis") REQUIRE_PERSISTENT_STATE_ENV = "SYNDERESIS_REQUIRE_PERSISTENT_STATE" def load_deployment_metadata() -> dict[str, object]: """Read build metadata generated by the deploy script.""" if not DEPLOYMENT_METADATA_PATH.exists(): return {} try: payload = json.loads(DEPLOYMENT_METADATA_PATH.read_text(encoding="utf-8")) except Exception as exc: print(f"Could not read deployment metadata: {exc}", file=sys.stderr, flush=True) return {} return payload if isinstance(payload, dict) else {} def configure_deployment_env(metadata: dict[str, object]) -> None: """Expose safe deployment metadata to the API process.""" source_db = metadata.get("source_db") if isinstance(metadata.get("source_db"), dict) else {} website_variant = str(metadata.get("website_variant") or "").strip().lower() env_values = { "SYNDERESIS_GIT_COMMIT": metadata.get("git_commit", ""), "SYNDERESIS_GIT_BRANCH": metadata.get("git_branch", ""), "SYNDERESIS_BUILD_GENERATED_AT": metadata.get("generated_at", ""), "SYNDERESIS_SOURCE_DB_SHA256": source_db.get("sha256", "") if isinstance(source_db, dict) else "", "SYNDERESIS_SOURCE_DB_EMBEDDER_SIGNATURE": source_db.get("embedder_signature", "") if isinstance(source_db, dict) else "", } for key, value in env_values.items(): if value: os.environ.setdefault(key, str(value)) # The UI and API mode come from the same uploaded bundle, including upload-only deploys. os.environ["SYNDERESIS_WEBSITE_VARIANT"] = ( website_variant if website_variant in {"catholic", "research"} else "catholic" ) def log_deployment_metadata(metadata: dict[str, object]) -> None: """Emit structured non-secret deployment metadata once at startup.""" if not metadata: print("SYNDERESIS_DEPLOYMENT_INFO {}", file=sys.stderr, flush=True) return print(f"SYNDERESIS_DEPLOYMENT_INFO {json.dumps(metadata, sort_keys=True, separators=(',', ':'))}", file=sys.stderr, flush=True) def persistent_state_required() -> bool: """Return whether this deployment must use the mounted /data volume.""" raw_value = os.getenv(REQUIRE_PERSISTENT_STATE_ENV, "").strip().lower() if not raw_value: return False if raw_value in {"1", "true", "yes", "on"}: return True if raw_value in {"0", "false", "no", "off"}: return False raise RuntimeError( f"{REQUIRE_PERSISTENT_STATE_ENV} must be a boolean flag" ) def writable_runtime_dir() -> Path: """Return a writable directory for SQLite state.""" if ( PERSISTENT_STATE_DIR.is_dir() and os.access(PERSISTENT_STATE_DIR, os.W_OK) ): return PERSISTENT_STATE_DIR if persistent_state_required(): raise RuntimeError( "Persistent API state requires a writable /data volume" ) FALLBACK_RUNTIME_DIR.mkdir(parents=True, exist_ok=True) return FALLBACK_RUNTIME_DIR def configure_source_db() -> None: """Download the retrieval database from a Hub dataset when configured.""" if os.getenv("SYNDERESIS_SOURCE_DB"): return repo_id = os.getenv("SYNDERESIS_SOURCE_DB_REPO", "").strip() if not repo_id: return filename = os.getenv("SYNDERESIS_SOURCE_DB_FILENAME", "official_sources.sqlite3").strip() repo_type = os.getenv("SYNDERESIS_SOURCE_DB_REPO_TYPE", "dataset").strip() token = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACE_HUB_TOKEN") or None downloaded_path = hf_hub_download(repo_id=repo_id, filename=filename, repo_type=repo_type, token=token) os.environ["SYNDERESIS_SOURCE_DB"] = downloaded_path print(f"Using source retrieval DB: {downloaded_path}", file=sys.stderr) def configure_defaults() -> None: """Set Space-safe defaults without overriding explicit variables.""" runtime_dir = writable_runtime_dir() os.environ.setdefault("SYNDERESIS_API_DB", str(runtime_dir / "synderesis_api.sqlite3")) if persistent_state_required(): ledger = Path(os.environ["SYNDERESIS_API_DB"]).resolve() if not ledger.is_relative_to(PERSISTENT_STATE_DIR.resolve()): raise RuntimeError( "Persistent API state requires SQLite under /data" ) os.environ.setdefault("SYNDERESIS_SQLITE_JOURNAL_MODE", "DELETE") os.environ.setdefault("SYNDERESIS_API_HOST", "0.0.0.0") os.environ.setdefault("SYNDERESIS_API_PORT", os.getenv("PORT", "7860")) os.environ.setdefault("SYNDERESIS_BACKEND", os.getenv("SYNDERESIS_MODEL_BACKEND", DEFAULT_BACKEND)) os.environ.setdefault("SYNDERESIS_MODEL_BACKEND", os.getenv("SYNDERESIS_BACKEND", DEFAULT_BACKEND)) if os.environ["SYNDERESIS_MODEL_BACKEND"] == "tinker": os.environ.setdefault( "SYNDERESIS_TINKER_MODEL_PATH", DEFAULT_TINKER_MODEL_PATH, ) os.environ.setdefault("SYNDERESIS_RETRIEVAL_LIMIT", "6") os.environ.setdefault("SYNDERESIS_CHAT_CONTEXT_MESSAGE_LIMIT", "12") os.environ.setdefault("SYNDERESIS_CHAT_CONTEXT_CHAR_LIMIT", "12000") os.environ.setdefault("SYNDERESIS_SOURCE_EMBEDDER", "harrier") os.environ.setdefault("NOLEGRAPH_AUTOLOAD_EMBEDDER", "1") stripe_names = ( "SYNDERESIS_STRIPE_SECRET_KEY", "SYNDERESIS_STRIPE_WEBHOOK_SECRET", "SYNDERESIS_STRIPE_FIXED_PRICE_ID", "SYNDERESIS_STRIPE_METERED_PRICE_ID", "SYNDERESIS_STRIPE_METER_EVENT_NAME", "SYNDERESIS_STRIPE_PUBLIC_ORIGIN", "SYNDERESIS_STRIPE_INCLUDED_RETAIL_MICRO_USD", "SYNDERESIS_STRIPE_FIXED_MONTHLY_CENTS", "SYNDERESIS_STRIPE_EXPECTED_LIVEMODE", "SYNDERESIS_STRIPE_REQUIRE_PERSISTENT_LEDGER", ) byok_fee_name = "SYNDERESIS_STRIPE_BYOK_PLATFORM_FEE_RATE" configured_byok_fee = os.getenv(byok_fee_name, "").strip() present = {name for name in stripe_names if os.getenv(name, "").strip()} if not present and configured_byok_fee: raise RuntimeError( "BYOK platform fee configuration requires Stripe billing" ) if present and present != set(stripe_names): raise RuntimeError("Stripe billing configuration is incomplete") if present: byok_fee = configured_byok_fee or "0.25" try: parsed_fee = Decimal(byok_fee) except (InvalidOperation, ValueError) as exc: raise RuntimeError( "Stripe BYOK platform fee rate must be a finite decimal" ) from exc if ( not parsed_fee.is_finite() or parsed_fee <= 0 or parsed_fee >= 1 ): raise RuntimeError( "Stripe BYOK platform fee rate must be greater than zero and less than one" ) os.environ[byok_fee_name] = byok_fee if os.environ["SYNDERESIS_STRIPE_REQUIRE_PERSISTENT_LEDGER"].lower() != "true": raise RuntimeError("Stripe billing requires persistent ledger enforcement") ledger = Path(os.environ["SYNDERESIS_API_DB"]).resolve() if not ledger.is_relative_to(Path("/data")): raise RuntimeError("Stripe billing requires persistent SQLite state under /data") def main() -> None: """Configure the container and replace this process with the API server.""" deployment_metadata = load_deployment_metadata() configure_deployment_env(deployment_metadata) log_deployment_metadata(deployment_metadata) configure_defaults() configure_source_db() port = os.environ["SYNDERESIS_API_PORT"] cmd = [ sys.executable, "scripts/serve_huggingface_api.py", "--host", os.environ["SYNDERESIS_API_HOST"], "--port", port, ] os.execvpe(sys.executable, cmd, os.environ) if __name__ == "__main__": main()