""" Configuration constants for the Geminicli2api proxy server. Centralizes all configuration to avoid duplication across modules. """ import os from typing import Any, Optional # Client Configuration # 需要自动封禁的错误码 (默认值,可通过环境变量或配置覆盖) AUTO_BAN_ERROR_CODES = [401, 403] # Default Safety Settings for Google API DEFAULT_SAFETY_SETTINGS = [ {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}, {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"}, {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"}, {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"}, {"category": "HARM_CATEGORY_CIVIC_INTEGRITY", "threshold": "BLOCK_NONE"} ] # Helper function to get base model name from any variant def get_base_model_name(model_name): """Convert variant model name to base model name.""" # Remove all possible suffixes in order suffixes = ["-maxthinking", "-nothinking", "-search"] for suffix in suffixes: if model_name.endswith(suffix): return model_name[:-len(suffix)] return model_name # Helper function to check if model uses search grounding def is_search_model(model_name): """Check if model name indicates search grounding should be enabled.""" return "-search" in model_name # Helper function to check if model uses no thinking def is_nothinking_model(model_name): """Check if model name indicates thinking should be disabled.""" return "-nothinking" in model_name # Helper function to check if model uses max thinking def is_maxthinking_model(model_name): """Check if model name indicates maximum thinking budget should be used.""" return "-maxthinking" in model_name # Helper function to get thinking budget for a model def get_thinking_budget(model_name): """Get the appropriate thinking budget for a model based on its name and variant.""" if is_nothinking_model(model_name): return 128 # Limited thinking for pro elif is_maxthinking_model(model_name): return 32768 else: # Default thinking budget for regular models return -1 # Default for all models # Helper function to check if thinking should be included in output def should_include_thoughts(model_name): """Check if thoughts should be included in the response.""" if is_nothinking_model(model_name): # For nothinking mode, still include thoughts if it's a pro model base_model = get_base_model_name(model_name) return "gemini-2.5-pro" in base_model else: # For all other modes, include thoughts return True # Dynamic Configuration System - Optimized for memory efficiency async def get_config_value(key: str, default: Any = None, env_var: Optional[str] = None) -> Any: """Get configuration value with priority: ENV > Storage > default.""" # Priority 1: Environment variable if env_var and os.getenv(env_var): return os.getenv(env_var) # Priority 2: Storage system try: from src.storage_adapter import get_storage_adapter storage_adapter = await get_storage_adapter() value = await storage_adapter.get_config(key) # 检查值是否存在(不是None),允许空字符串、0、False等有效值 if value is not None: return value except Exception as e: # Debug: print import/storage errors # print(f"Config storage error for key {key}: {e}") pass return default # Configuration getters - all async async def get_proxy_config(): """Get proxy configuration.""" proxy_url = await get_config_value("proxy", env_var="PROXY") return proxy_url if proxy_url else None async def get_calls_per_rotation() -> int: """Get calls per rotation setting.""" env_value = os.getenv("CALLS_PER_ROTATION") if env_value: try: return int(env_value) except ValueError: pass return int(await get_config_value("calls_per_rotation", 100)) async def get_auto_ban_enabled() -> bool: """Get auto ban enabled setting.""" env_value = os.getenv("AUTO_BAN") if env_value: return env_value.lower() in ("true", "1", "yes", "on") return bool(await get_config_value("auto_ban_enabled", False)) async def get_auto_ban_error_codes() -> list: """ Get auto ban error codes. Environment variable: AUTO_BAN_ERROR_CODES (comma-separated, e.g., "400,403") TOML config key: auto_ban_error_codes Default: [400, 403] """ env_value = os.getenv("AUTO_BAN_ERROR_CODES") if env_value: try: return [int(code.strip()) for code in env_value.split(",") if code.strip()] except ValueError: pass codes = await get_config_value("auto_ban_error_codes") if codes and isinstance(codes, list): return codes return AUTO_BAN_ERROR_CODES async def get_retry_429_max_retries() -> int: """Get max retries for 429 errors.""" env_value = os.getenv("RETRY_429_MAX_RETRIES") if env_value: try: return int(env_value) except ValueError: pass return int(await get_config_value("retry_429_max_retries", 5)) async def get_retry_429_enabled() -> bool: """Get 429 retry enabled setting.""" env_value = os.getenv("RETRY_429_ENABLED") if env_value: return env_value.lower() in ("true", "1", "yes", "on") return bool(await get_config_value("retry_429_enabled", True)) async def get_retry_429_interval() -> float: """Get 429 retry interval in seconds.""" env_value = os.getenv("RETRY_429_INTERVAL") if env_value: try: return float(env_value) except ValueError: pass return float(await get_config_value("retry_429_interval", 1)) # Model name lists for different features BASE_MODELS = [ "gemini-2.5-pro-preview-06-05", "gemini-2.5-pro", "gemini-2.5-pro-preview-05-06", "gemini-2.5-flash", ] def get_available_models(router_type="openai"): """ Get available models with feature prefixes. Args: router_type: "openai" or "gemini" Returns: List of model names with feature prefixes """ models = [] for base_model in BASE_MODELS: # 基础模型 models.append(base_model) # 假流式模型 (前缀格式) models.append(f"假流式/{base_model}") # 流式抗截断模型 (仅在流式传输时有效,前缀格式) models.append(f"流式抗截断/{base_model}") # 支持thinking模式后缀与功能前缀组合 for thinking_suffix in ["-maxthinking", "-nothinking", "-search"]: # 基础模型 + thinking后缀 models.append(f"{base_model}{thinking_suffix}") # 假流式 + thinking后缀 models.append(f"假流式/{base_model}{thinking_suffix}") # 流式抗截断 + thinking后缀 models.append(f"流式抗截断/{base_model}{thinking_suffix}") return models def is_fake_streaming_model(model_name: str) -> bool: """Check if model name indicates fake streaming should be used.""" return model_name.startswith("假流式/") def is_anti_truncation_model(model_name: str) -> bool: """Check if model name indicates anti-truncation should be used.""" return model_name.startswith("流式抗截断/") def get_base_model_from_feature_model(model_name: str) -> str: """Get base model name from feature model name.""" # Remove feature prefixes for prefix in ["假流式/", "流式抗截断/"]: if model_name.startswith(prefix): return model_name[len(prefix):] return model_name async def get_anti_truncation_max_attempts() -> int: """ Get maximum attempts for anti-truncation continuation. Environment variable: ANTI_TRUNCATION_MAX_ATTEMPTS TOML config key: anti_truncation_max_attempts Default: 3 """ env_value = os.getenv("ANTI_TRUNCATION_MAX_ATTEMPTS") if env_value: try: return int(env_value) except ValueError: pass return int(await get_config_value("anti_truncation_max_attempts", 3)) # Server Configuration async def get_server_host() -> str: """ Get server host setting. Environment variable: HOST TOML config key: host Default: 0.0.0.0 """ return str(await get_config_value("host", "0.0.0.0", "HOST")) async def get_server_port() -> int: """ Get server port setting. Environment variable: PORT TOML config key: port Default: 7861 """ env_value = os.getenv("PORT") if env_value: try: return int(env_value) except ValueError: pass return int(await get_config_value("port", 7861)) async def get_api_password() -> str: """ Get API password setting for chat endpoints. Environment variable: API_PASSWORD TOML config key: api_password Default: Uses PASSWORD env var for compatibility, otherwise 'pwd' """ # 优先使用 API_PASSWORD,如果没有则使用通用 PASSWORD 保证兼容性 api_password = await get_config_value("api_password", None, "API_PASSWORD") if api_password is not None: return str(api_password) # 兼容性:使用通用密码 return str(await get_config_value("password", "pwd", "PASSWORD")) async def get_panel_password() -> str: """ Get panel password setting for web interface. Environment variable: PANEL_PASSWORD TOML config key: panel_password Default: Uses PASSWORD env var for compatibility, otherwise 'pwd' """ # 优先使用 PANEL_PASSWORD,如果没有则使用通用 PASSWORD 保证兼容性 panel_password = await get_config_value("panel_password", None, "PANEL_PASSWORD") if panel_password is not None: return str(panel_password) # 兼容性:使用通用密码 return str(await get_config_value("password", "pwd", "PASSWORD")) async def get_server_password() -> str: """ Get server password setting (deprecated, use get_api_password or get_panel_password). Environment variable: PASSWORD TOML config key: password Default: pwd """ return str(await get_config_value("password", "pwd", "PASSWORD")) async def get_credentials_dir() -> str: """ Get credentials directory setting. Environment variable: CREDENTIALS_DIR TOML config key: credentials_dir Default: ./creds """ return str(await get_config_value("credentials_dir", "./creds", "CREDENTIALS_DIR")) async def get_code_assist_endpoint() -> str: """ Get Code Assist endpoint setting. Environment variable: CODE_ASSIST_ENDPOINT TOML config key: code_assist_endpoint Default: https://cloudcode-pa.googleapis.com """ return str(await get_config_value("code_assist_endpoint", "https://cloudcode-pa.googleapis.com", "CODE_ASSIST_ENDPOINT")) async def get_auto_load_env_creds() -> bool: """ Get auto load environment credentials setting. Environment variable: AUTO_LOAD_ENV_CREDS TOML config key: auto_load_env_creds Default: False """ env_value = os.getenv("AUTO_LOAD_ENV_CREDS") if env_value: return env_value.lower() in ("true", "1", "yes", "on") return bool(await get_config_value("auto_load_env_creds", False)) async def get_compatibility_mode_enabled() -> bool: """ Get compatibility mode setting. 兼容性模式:启用后所有system消息全部转换成user,停用system_instructions。 该选项可能会降低模型理解能力,但是能避免流式空回的情况。 Environment variable: COMPATIBILITY_MODE TOML config key: compatibility_mode_enabled Default: True """ env_value = os.getenv("COMPATIBILITY_MODE") if env_value: return env_value.lower() in ("true", "1", "yes", "on") return bool(await get_config_value("compatibility_mode_enabled", True)) async def get_oauth_proxy_url() -> str: """ Get OAuth proxy URL setting. 用于Google OAuth2认证的代理URL。 Environment variable: OAUTH_PROXY_URL TOML config key: oauth_proxy_url Default: https://oauth2.googleapis.com """ return str(await get_config_value("oauth_proxy_url", "https://oauth2.googleapis.com", "OAUTH_PROXY_URL")) async def get_googleapis_proxy_url() -> str: """ Get Google APIs proxy URL setting. 用于Google APIs调用的代理URL。 Environment variable: GOOGLEAPIS_PROXY_URL TOML config key: googleapis_proxy_url Default: https://www.googleapis.com """ return str(await get_config_value("googleapis_proxy_url", "https://www.googleapis.com", "GOOGLEAPIS_PROXY_URL")) async def get_resource_manager_api_url() -> str: """ Get Google Cloud Resource Manager API URL setting. 用于Google Cloud Resource Manager API的URL。 Environment variable: RESOURCE_MANAGER_API_URL TOML config key: resource_manager_api_url Default: https://cloudresourcemanager.googleapis.com """ return str(await get_config_value("resource_manager_api_url", "https://cloudresourcemanager.googleapis.com", "RESOURCE_MANAGER_API_URL")) async def get_service_usage_api_url() -> str: """ Get Google Cloud Service Usage API URL setting. 用于Google Cloud Service Usage API的URL。 Environment variable: SERVICE_USAGE_API_URL TOML config key: service_usage_api_url Default: https://serviceusage.googleapis.com """ return str(await get_config_value("service_usage_api_url", "https://serviceusage.googleapis.com", "SERVICE_USAGE_API_URL")) # MongoDB Configuration async def get_mongodb_uri() -> str: """ Get MongoDB connection URI setting. MongoDB连接URI,用于分布式部署时的数据存储。 设置此项后将不再使用本地/creds和TOML文件。 Environment variable: MONGODB_URI TOML config key: mongodb_uri Default: None (使用本地文件存储) 示例格式: - mongodb://username:password@localhost:27017/database - mongodb+srv://username:password@cluster.mongodb.net/database """ return str(await get_config_value("mongodb_uri", "", "MONGODB_URI")) async def get_mongodb_database() -> str: """ Get MongoDB database name setting. MongoDB数据库名称。 Environment variable: MONGODB_DATABASE TOML config key: mongodb_database Default: gcli2api """ return str(await get_config_value("mongodb_database", "gcli2api", "MONGODB_DATABASE")) async def is_mongodb_mode() -> bool: """ Check if MongoDB mode is enabled. 检查是否启用了MongoDB模式。 如果配置了MongoDB URI,则启用MongoDB模式,不再使用本地文件。 Returns: bool: True if MongoDB mode is enabled, False otherwise """ mongodb_uri = await get_mongodb_uri() return bool(mongodb_uri and mongodb_uri.strip())