Spaces:
Sleeping
Sleeping
File size: 6,846 Bytes
1ebb69b b9345ee 1ebb69b b9345ee 1ebb69b b9345ee 1ebb69b b9345ee 1ebb69b b9345ee 1ebb69b | 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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | """
Nancy HF Space β Configuration Module.
All settings are loaded from environment variables with sensible defaults.
Provider configuration can be supplied as a JSON string via PROVIDERS_CONFIG.
"""
from __future__ import annotations
import json
import logging
from typing import Any
from dotenv import load_dotenv
from pydantic import Field, field_validator
from pydantic_settings import BaseSettings
# Load environment variables from .env file
load_dotenv()
logger = logging.getLogger("nancy.config")
class Settings(BaseSettings):
"""Application settings sourced from environment variables."""
# ββ Auth ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
nancy_api_key: str = Field(
default="nancy-dev-key",
description="Bearer token required for /v1/* API endpoints.",
)
nancy_ext_secret: str = Field(
default="nancy-ext-dev-secret",
description="Bearer token required for /ext/* extension endpoints.",
)
# ββ Upstash Redis (optional) ββββββββββββββββββββββββββββββββββββββ
upstash_redis_rest_url: str = Field(
default="",
description="Upstash Redis REST URL. Leave empty to use in-memory fallback.",
)
upstash_redis_rest_token: str = Field(
default="",
description="Upstash Redis REST bearer token.",
)
# ββ Official Paid APIs / Hybrid Keys (optional) βββββββββββββββββββ
mistral_api_key: str = Field(default="", description="Official Mistral API Key.")
nvidia_nim_api_key: str = Field(default="", description="Official NVIDIA NIM API Key.")
deepseek_api_key: str = Field(default="", description="Official DeepSeek API Key.")
anthropic_api_key: str = Field(default="", description="Official Anthropic/Claude API Key.")
z_ai_api_key: str = Field(default="", description="Official Z.ai API Key.")
# ββ Provider Routing ββββββββββββββββββββββββββββββββββββββββββββββ
default_provider: str = Field(
default="chatgpt",
description="Default provider when the model name is not recognized.",
)
fallback_chain: Any = Field(
default=["chatgpt", "gemini", "deepseek", "kimi", "claude", "nim", "zai"],
description="Ordered list of providers to try on failure.",
)
providers_config: dict[str, Any] = Field(
default_factory=lambda: {
"chatgpt": {"rpm": 10, "tpm": 40000, "url_pattern": "https://chatgpt.com"},
"gemini": {"rpm": 15, "tpm": 60000, "url_pattern": "https://gemini.google.com"},
"deepseek": {"rpm": 10, "tpm": 40000, "url_pattern": "https://chat.deepseek.com"},
"kimi": {"rpm": 10, "tpm": 40000, "url_pattern": "https://kimi.moonshot.cn"},
"claude": {"rpm": 5, "tpm": 30000, "url_pattern": "https://claude.ai"},
"nim": {"rpm": 5, "tpm": 20000, "url_pattern": "https://build.nvidia.com/nim"},
"zai": {"rpm": 5, "tpm": 20000, "url_pattern": "https://chat.z.ai"},
},
description="Per-provider configuration. Supply as JSON string via env var.",
)
# ββ Circuit Breaker βββββββββββββββββββββββββββββββββββββββββββββββ
cb_failure_threshold: int = Field(
default=3,
description="Consecutive failures before tripping the circuit breaker.",
)
cb_cooldown_seconds: float = Field(
default=60.0,
description="Seconds to wait before retrying a tripped provider.",
)
# ββ Task Queue ββββββββββββββββββββββββββββββββββββββββββββββββββββ
task_timeout_seconds: float = Field(
default=240.0,
description="Max seconds to wait for extension to complete a task (fast-type + AI generation + relay).",
)
task_queue_max_size: int = Field(
default=100,
description="Maximum number of pending tasks in the queue.",
)
# ββ Extension βββββββββββββββββββββββββββββββββββββββββββββββββββββ
ext_heartbeat_timeout_seconds: float = Field(
default=30.0,
description="Seconds after last heartbeat before extension is considered offline.",
)
ext_sse_keepalive_seconds: float = Field(
default=15.0,
description="Interval for SSE keepalive pings to the extension.",
)
# ββ Server ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
log_level: str = Field(default="INFO", description="Logging level.")
cors_origins: Any = Field(
default=["*"],
description="Allowed CORS origins.",
)
# ββ Validators ββββββββββββββββββββββββββββββββββββββββββββββββββββ
@field_validator("providers_config", mode="before")
@classmethod
def parse_providers_json(cls, v: Any) -> dict[str, Any]:
"""Accept a JSON string or dict for providers_config."""
if isinstance(v, str):
try:
return json.loads(v)
except json.JSONDecodeError as exc:
logger.error("Invalid PROVIDERS_CONFIG JSON: %s", exc)
raise ValueError(f"PROVIDERS_CONFIG is not valid JSON: {exc}") from exc
return v
@field_validator("fallback_chain", mode="before")
@classmethod
def parse_fallback_chain(cls, v: Any) -> list[str]:
"""Accept a comma-separated string or list."""
if isinstance(v, str):
return [s.strip() for s in v.split(",") if s.strip()]
return v
@field_validator("cors_origins", mode="before")
@classmethod
def parse_cors_origins(cls, v: Any) -> list[str]:
"""Accept a comma-separated string or list."""
if isinstance(v, str):
return [s.strip() for s in v.split(",") if s.strip()]
return v
@property
def redis_enabled(self) -> bool:
"""Return True if Upstash Redis is configured."""
return bool(self.upstash_redis_rest_url and self.upstash_redis_rest_token)
model_config = {"env_prefix": "", "case_sensitive": False}
# Module-level singleton
settings = Settings()
|