""" 模型配置中心 用于集中管理所有支持的 Gemini 模型 使用方式: 1. 添加新模型: 在对应的模型列表中添加新的模型字典 2. 临时关闭模型: 将模型的 enabled 字段设为 False 3. 重新启用模型: 将模型的 enabled 字段改回 True 模型类型: - GEMINI_MODELS: Nano Banana 图像生成模型 - GEMINI_FLASH_MODELS: Google Gemini Flash 文本生成模型 示例: 添加新模型: { "id": "gemini-新模型名称", "description": "模型说明和特点", "enabled": True, "endpoint_type": "standard", "endpoint": "/v1beta/models/gemini-新模型名称:generateContent", "thinking_config": { "不思考": None, "低": "low", "中": None, "高": "high" } } 临时关闭模型: 将对应模型的 "enabled": True 改为 "enabled": False """ from typing import List, Dict, Optional, Tuple # ============================================================ # 模型配置列表 # ============================================================ # ============================================================ # Nano Banana 图像生成模型 # ============================================================ GEMINI_MODELS = [ { "id": "nano-banana-pro-次卡", "description": "Nano Banana Pro 次卡,根据分辨率自动选择端点 (1K/2K/4K),高性能图像生成模型", "enabled": True, "provider": "gemini_async", "endpoint_type": "dynamic", "endpoint": None, # 动态端点,由代码根据分辨率选择 "supported_aspect_ratios": [ "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9" ], "supported_resolutions": ["1K", "2K", "4K"] }, { "id": "nano-banana-pro-官方计费", "description": "Nano Banana Pro 官方计费,按分辨率路由 (1K/2K/4K),使用官方计费通道", "enabled": True, "provider": "gemini_async", "endpoint_type": "dynamic", "endpoint": None, # 动态端点,由代码根据分辨率选择 "supported_aspect_ratios": [ "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9" ], "supported_resolutions": ["1K", "2K", "4K"] }, { "id": "nano-banana-2-次卡", "description": "Nano Banana 2 次卡,根据分辨率自动选择端点 (512px/1K/2K/4K),图像生成模型", "enabled": True, "provider": "gemini_async", "endpoint_type": "dynamic", "endpoint": None, # 动态端点,由代码根据分辨率选择 "supported_aspect_ratios": [ "1:1", "1:4", "1:8", "2:3", "3:2", "3:4", "4:1", "4:3", "4:5", "5:4", "8:1", "9:16", "16:9", "21:9" ], "supported_resolutions": ["512px", "1K", "2K", "4K"] }, { "id": "nano-banana-2-官方计费", "description": "Nano Banana 2 官方计费,按分辨率路由 (512/1K/2K/4K),使用官方计费通道", "enabled": True, "provider": "gemini_async", "endpoint_type": "dynamic", "endpoint": None, # 动态端点,由代码根据分辨率选择 "supported_aspect_ratios": [ "1:1", "1:4", "1:8", "2:3", "3:2", "3:4", "4:1", "4:3", "4:5", "5:4", "8:1", "9:16", "16:9", "21:9" ], "supported_resolutions": ["512px", "1K", "2K", "4K"] }, ] # ============================================================ # Google Gemini Flash 文本生成模型 # ============================================================ GEMINI_FLASH_MODELS = [ { "id": "gemini-3-flash-preview", "description": "Gemini 3 Flash,快速多模态文本生成,通过 thinkingConfig 控制思考等级", "enabled": True, "endpoint_type": "standard", "endpoint": "/v1beta/models/gemini-3-flash-preview:generateContent", "thinking_config": { "低": "low", "中": "medium", "高": "high" } }, { "id": "gemini-3.1-pro-preview", "description": "Gemini 3.1 Pro,高性能多模态文本生成,通过 thinkingConfig 控制思考等级", "enabled": True, "endpoint_type": "standard", "endpoint": "/v1beta/models/gemini-3.1-pro-preview:generateContent", "thinking_config": { "低": "low", "中": "high" } }, { "id": "gemini-3.1-flash-lite-preview", "description": "Gemini 3.1 Flash Lite,轻量级多模态文本生成,通过 thinkingConfig 控制思考等级", "enabled": True, "endpoint_type": "standard", "endpoint": "/v1beta/models/gemini-3.1-flash-lite-preview:generateContent", "thinking_config": { "低": "low", "中": "medium", "高": "high" } } ] # ============================================================ # 工具函数 # ============================================================ def get_enabled_models() -> List[str]: """ 获取所有启用的模型 ID 列表 Returns: 启用的模型 ID 列表 Example: >>> get_enabled_models() ['gemini-3-pro-image-preview-url', 'gemini-3-pro-image-preview', ...] """ return [model["id"] for model in GEMINI_MODELS if model.get("enabled", False)] def get_all_models() -> List[str]: """ 获取所有模型 ID 列表(包括已禁用的) Returns: 所有模型 ID 列表 Example: >>> get_all_models() ['gemini-3-pro-image-preview-url', 'gemini-3-pro-image-preview', ...] """ return [model["id"] for model in GEMINI_MODELS] def get_model_config(model_id: str) -> Optional[Dict]: """ 根据模型 ID 获取完整的模型配置 Args: model_id: 模型 ID Returns: 模型配置字典,如果未找到则返回 None Example: >>> config = get_model_config("gemini-3-pro-image-preview-url") >>> print(config["description"]) URL 模式,根据分辨率自动选择端点 (1K/2K/4K) """ for model in GEMINI_MODELS: if model["id"] == model_id: return model return None def is_model_enabled(model_id: str) -> bool: """ 检查指定模型是否启用 Args: model_id: 模型 ID Returns: True 如果模型启用,False 如果禁用或不存在 Example: >>> is_model_enabled("gemini-3-pro-image-preview-url") True """ config = get_model_config(model_id) if config is None: return False return config.get("enabled", False) def get_model_description(model_id: str) -> str: """ 获取模型的描述信息 Args: model_id: 模型 ID Returns: 模型描述,如果未找到则返回空字符串 Example: >>> get_model_description("gemini-3-pro-image-preview") '标准模式,固定端点,适用于常规图像生成' """ config = get_model_config(model_id) if config is None: return "" return config.get("description", "") def get_model_supported_aspect_ratios(model_id: str) -> List[str]: """ 获取模型支持的宽高比列表 Args: model_id: 模型 ID Returns: 支持的宽高比字符串列表,如果未配置则返回空列表 Example: >>> get_model_supported_aspect_ratios("gemini-3-pro-image-preview") ['1:1', '2:3', '3:2', ...] """ config = get_model_config(model_id) if config is None: return [] return config.get("supported_aspect_ratios", []) def get_all_supported_aspect_ratios() -> List[str]: """ 获取所有启用模型支持的宽高比(去重合并) Returns: 所有启用模型支持的宽高比列表(保持顺序、去重) Example: >>> get_all_supported_aspect_ratios() ['1:1', '4:3', '3:4', '16:9', '9:16', '2:3', '3:2', '4:5', '5:4', '21:9', '1:4', '4:1', '1:8', '8:1'] """ seen = set() result = [] for model in GEMINI_MODELS: if not model.get("enabled", False): continue for ratio in model.get("supported_aspect_ratios", []): if ratio not in seen: seen.add(ratio) result.append(ratio) return result def get_model_supported_resolutions(model_id: str) -> List[str]: """ 获取模型支持的分辨率列表 Args: model_id: 模型 ID Returns: 支持的分辨率字符串列表,如果未配置则返回空列表 Example: >>> get_model_supported_resolutions("gemini-3.1-flash-image-preview") ['512', '1K', '2K', '4K'] >>> get_model_supported_resolutions("gemini-3-pro-image-preview") ['1K', '2K', '4K'] """ config = get_model_config(model_id) if config is None: return [] return config.get("supported_resolutions", []) def get_all_supported_resolutions() -> List[str]: """ 获取所有启用模型支持的分辨率(去重合并,按从小到大固定顺序排列) Returns: 所有启用模型支持的分辨率列表(按 512 → 1K → 2K → 4K 顺序) Example: >>> get_all_supported_resolutions() ['512', '1K', '2K', '4K'] """ _ORDER = ["512px", "1K", "2K", "4K"] seen = set() for model in GEMINI_MODELS: if not model.get("enabled", False): continue for res in model.get("supported_resolutions", []): seen.add(res) return [res for res in _ORDER if res in seen] def get_model_provider(model_id: str) -> Optional[str]: """ 获取模型的异步 Provider 名称 Args: model_id: 模型 ID Returns: Provider 名称(如 "gemini_async"),如果模型未配置 provider 则返回 None """ config = get_model_config(model_id) if config is None: return None return config.get("provider") def get_enabled_async_models() -> List[str]: """ 获取所有启用的、支持异步模式的模型 ID 列表 Returns: 模型 ID 列表(仅包含配置了 provider 且 enabled 的模型) """ return [ model["id"] for model in GEMINI_MODELS if model.get("enabled", False) and model.get("provider") ] def get_endpoint_type(model_id: str) -> Optional[str]: """ 获取模型的端点类型 Args: model_id: 模型 ID Returns: 端点类型 ("dynamic", "standard", "flatfee"),如果未找到则返回 None Example: >>> get_endpoint_type("gemini-3-pro-image-preview-url") 'dynamic' """ config = get_model_config(model_id) if config is None: return None return config.get("endpoint_type") def get_model_endpoint(model_id: str) -> Optional[str]: """ 获取模型的 API 端点 Args: model_id: 模型 ID Returns: API 端点路径,如果未找到或为动态端点则返回 None Example: >>> get_model_endpoint("gemini-3-pro-image-preview") '/v1beta/models/gemini-3-pro-image-preview:generateContent' >>> get_model_endpoint("gemini-3-pro-image-preview-url") None # 动态端点 """ config = get_model_config(model_id) if config is None: return None return config.get("endpoint") # ============================================================ # Gemini Flash 模型工具函数 # ============================================================ # ============================================================ # Sora 视频生成模型 # ============================================================ SORA_MODELS = [ { "id": "sora-2", "description": "Sora 2 官方模型,支持标准时长和分辨率", "enabled": True, "supported_seconds": [4, 8, 10, 12, 15], "supported_sizes": ["720x1280", "1280x720"], "seconds_category": "官方", # 用于界面显示标签 }, { "id": "sora-2-pro", "description": "Sora 2 Pro 增强模型,支持扩展时长和竖屏/横屏高清分辨率", "enabled": True, "supported_seconds": [4, 8, 12, 15, 25], "supported_sizes": ["720x1280", "1280x720", "1024x1792", "1792x1024"], "seconds_category": "扩展", # Pro 模型支持全部时长 }, ] # 秒数显示标签配置(用于界面下拉菜单) # key: 实际秒数, value: 显示文本 SECONDS_DISPLAY_MAP = { 4: "4", 8: "8", 12: "12", 10: "10", 15: "15", 25: "25(pro)", } # 分辨率显示标签配置 # key: 实际分辨率, value: (显示P数, 显示方向) RESOLUTION_DISPLAY_MAP = { "720x1280": ("720P", "竖屏"), "1280x720": ("720P", "横屏"), "1024x1792": ("1080P", "竖屏"), "1792x1024": ("1080P", "横屏"), } # ============================================================ # Sora 模型工具函数 # ============================================================ def get_enabled_sora_models() -> List[str]: """获取所有启用的 Sora 模型 ID 列表""" return [model["id"] for model in SORA_MODELS if model.get("enabled", False)] def get_sora_model_config(model_id: str) -> Optional[Dict]: """根据模型 ID 获取 Sora 模型的完整配置""" for model in SORA_MODELS: if model["id"] == model_id: return model return None def get_sora_supported_seconds(model_id: str) -> List[int]: """获取 Sora 模型支持的视频时长列表(秒)""" config = get_sora_model_config(model_id) if config is None: return [] return config.get("supported_seconds", []) def get_sora_supported_sizes(model_id: str) -> List[str]: """获取 Sora 模型支持的分辨率列表""" config = get_sora_model_config(model_id) if config is None: return [] return config.get("supported_sizes", []) def get_all_sora_seconds() -> List[int]: """获取所有启用 Sora 模型支持的时长(去重、升序)""" seen = set() for model in SORA_MODELS: if not model.get("enabled", False): continue for s in model.get("supported_seconds", []): seen.add(s) return sorted(seen) def get_all_sora_sizes() -> List[str]: """获取所有启用 Sora 模型支持的分辨率(去重、保持顺序)""" seen = set() result = [] for model in SORA_MODELS: if not model.get("enabled", False): continue for size in model.get("supported_sizes", []): if size not in seen: seen.add(size) result.append(size) return result def get_sora_seconds_with_labels(model_id: str) -> List[Tuple[str, int]]: """ 获取指定模型支持的秒数列表(带标签显示) Returns: 列表项为 (显示文本, 实际秒数),如 [("4(官方)", 4), ("10(特殊)", 10)] """ config = get_sora_model_config(model_id) if config is None: return [] seconds_list = config.get("supported_seconds", []) result = [] for s in seconds_list: category = SECONDS_CATEGORIES.get(s, "") label = f"{s}({category})" if category else str(s) result.append((label, s)) return result def get_sora_sizes_with_labels(model_id: str) -> List[Tuple[str, str]]: """ 获取指定模型支持的分辨率列表(带独占标识) Returns: 列表项为 (显示文本, 实际分辨率),如 [("720P 9:16 (720x1280)", "720x1280")] """ from math import gcd config = get_sora_model_config(model_id) if config is None: return [] sizes = config.get("supported_sizes", []) result = [] # 检查哪些分辨率是独占的(仅该模型支持) all_sizes_count = {} for m in SORA_MODELS: if not m.get("enabled", False): continue for size in m.get("supported_sizes", []): all_sizes_count[size] = all_sizes_count.get(size, 0) + 1 for size in sizes: # 解析分辨率 parts = size.lower().split("x") w, h = int(parts[0]), int(parts[1]) short_side = min(w, h) # 分辨率等级 if short_side >= 1792: res = "2K+" elif short_side >= 1080: res = "1K+" elif short_side >= 720: res = "720P" else: res = f"{short_side}P" # 比例 g = gcd(w, h) ratio = f"{w // g}:{h // g}" # 检查是否独占 exclusive = all_sizes_count.get(size, 0) == 1 exclusive_tag = " [Pro独占]" if exclusive else "" # 方向 orientation = "竖屏" if h > w else "横屏" if w > h else "方形" label = f"{res} {ratio} {orientation}{exclusive_tag} ({size})" result.append((label, size)) return result # ============================================================ # Google Veo 视频生成模型 # ============================================================ VEO_MODELS = [ { "id": "Veo3.1", "description": "Google Veo 3.1 视频生成模型,支持文生视频和图生视频", "enabled": True, }, ] # Veo 分辨率映射表 # key: "分辨率_宽高比", value: 实际分辨率字符串 VEO_RESOLUTION_MAP = { # 720p "720p_9:16": "720x1280", "720p_16:9": "1280x720", # 1080p "1080p_9:16": "1080x1920", "1080p_16:9": "1920x1080", # 4K "4K_9:16": "2160x3840", "4K_16:9": "3840x2160", } # ============================================================ # Veo 模型工具函数 # ============================================================ def get_enabled_veo_models() -> List[str]: """获取所有启用的 Veo 模型 ID 列表""" return [model["id"] for model in VEO_MODELS if model.get("enabled", False)] def get_veo_model_config(model_id: str) -> Optional[Dict]: """根据模型 ID 获取 Veo 模型的完整配置""" for model in VEO_MODELS: if model["id"] == model_id: return model return None # ============================================================ # Gemini Flash 模型工具函数 # ============================================================ def get_enabled_flash_models() -> List[str]: """ 获取所有启用的 Flash 模型 ID 列表 Returns: 启用的 Flash 模型 ID 列表 Example: >>> get_enabled_flash_models() ['gemini-3-flash-preview'] """ return [model["id"] for model in GEMINI_FLASH_MODELS if model.get("enabled", False)] def get_all_flash_models() -> List[str]: """ 获取所有 Flash 模型 ID 列表(包括已禁用的) Returns: 所有 Flash 模型 ID 列表 """ return [model["id"] for model in GEMINI_FLASH_MODELS] def get_flash_model_config(model_id: str) -> Optional[Dict]: """ 根据模型 ID 获取 Flash 模型的完整配置 Args: model_id: 模型 ID Returns: 模型配置字典,如果未找到则返回 None Example: >>> config = get_flash_model_config("gemini-3-flash-preview") >>> print(config["description"]) 'Gemini 3 Flash,快速多模态文本生成,支持图片和视频输入' """ for model in GEMINI_FLASH_MODELS: if model["id"] == model_id: return model return None def is_flash_model_enabled(model_id: str) -> bool: """ 检查指定 Flash 模型是否启用 Args: model_id: 模型 ID Returns: True 如果模型启用,False 如果禁用或不存在 """ config = get_flash_model_config(model_id) if config is None: return False return config.get("enabled", False) def get_flash_model_endpoint(model_id: str) -> Optional[str]: """ 获取 Flash 模型的 API 端点 Args: model_id: 模型 ID Returns: API 端点路径,如果未找到则返回 None Example: >>> get_flash_model_endpoint("gemini-3-flash-preview") '/v1beta/models/gemini-3-flash-preview:generateContent' """ config = get_flash_model_config(model_id) if config is None: return None return config.get("endpoint") def get_flash_model_description(model_id: str) -> str: """ 获取 Flash 模型的描述信息 Args: model_id: 模型 ID Returns: 模型描述,如果未找到则返回空字符串 """ config = get_flash_model_config(model_id) if config is None: return "" return config.get("description", "") def get_flash_model_thinking_level_value(model_id: str, thinking_level: str) -> Optional[str]: """ 获取指定模型在给定思考等级下应传入请求体的 thinkingLevel 值。 仅对 endpoint_type="standard" 且配置了 thinking_config 的模型有效。 返回 None 表示该等级不受支持,请求体中不应包含 thinkingConfig。 Args: model_id: 模型 ID thinking_level: 思考等级中文名(不思考/低/中/高) Returns: API thinkingLevel 值(如 "low"/"medium"/"high"),或 None(不传参) Example: >>> get_flash_model_thinking_level_value("gemini-3-pro-preview", "低") 'low' >>> get_flash_model_thinking_level_value("gemini-3-pro-preview", "中") None # 不受支持,省略 thinkingConfig """ config = get_flash_model_config(model_id) if config is None: return None thinking_config = config.get("thinking_config") if not thinking_config: return None return thinking_config.get(thinking_level) # 已弃用:动态端点模式下不再需要这些函数 # def get_flash_model_thinking_levels(model_id: str) -> List[str]: # """ # 获取 Flash 模型支持的思考等级列表 # # Args: # model_id: 模型 ID # # Returns: # 思考等级列表(中文),如果未找到则返回空列表 # # Example: # >>> get_flash_model_thinking_levels("gemini-3-flash-preview") # ['默认', '最低', '低', '中', '高'] # """ # config = get_flash_model_config(model_id) # if config is None: # return [] # # thinking_levels = config.get("thinking_levels", {}) # return list(thinking_levels.keys()) # def get_thinking_level_value(model_id: str, thinking_level: str) -> Optional[str]: # """ # 获取思考等级对应的 API 参数值 # # Args: # model_id: 模型 ID # thinking_level: 思考等级(中文) # # Returns: # API 参数值(英文),如果未找到则返回 None # # Example: # >>> get_thinking_level_value("gemini-3-flash-preview", "默认") # 'high' # >>> get_thinking_level_value("gemini-3-flash-preview", "最低") # 'minimal' # """ # config = get_flash_model_config(model_id) # if config is None: # return None # # thinking_levels = config.get("thinking_levels", {}) # return thinking_levels.get(thinking_level) # ============================================================ # 向后兼容性检查 # ============================================================ def validate_models_config() -> None: """ 验证模型配置的完整性 检查: - 每个模型必须有 id, description, enabled, endpoint_type, endpoint 字段 - 非动态端点模型必须配置有效的 endpoint - 至少有一个模型是启用的 Raises: ValueError: 如果配置不合法 """ if not GEMINI_MODELS: raise ValueError("GEMINI_MODELS 列表不能为空") required_fields = ["id", "description", "enabled", "endpoint_type", "endpoint"] valid_endpoint_types = ["dynamic", "standard", "flatfee"] for i, model in enumerate(GEMINI_MODELS): # 检查必需字段 for field in required_fields: if field not in model: raise ValueError(f"模型 #{i} 缺少必需字段: {field}") # 检查 endpoint_type 是否合法 if model["endpoint_type"] not in valid_endpoint_types: raise ValueError( f"模型 {model['id']} 的 endpoint_type '{model['endpoint_type']}' 不合法。" f"必须是: {', '.join(valid_endpoint_types)}" ) # 检查非动态端点模型必须有有效的 endpoint if model["endpoint_type"] != "dynamic" and not model.get("endpoint"): raise ValueError( f"模型 {model['id']} 的 endpoint_type 为 '{model['endpoint_type']}'," f"但未配置有效的 endpoint 字段" ) # 检查端点格式(如果配置了) endpoint = model.get("endpoint") if endpoint and not endpoint.startswith("/v1beta/models/"): raise ValueError( f"模型 {model['id']} 的 endpoint '{endpoint}' 格式不正确。" f"应以 '/v1beta/models/' 开头" ) # 检查至少有一个启用的模型 if not get_enabled_models(): raise ValueError("至少需要启用一个模型") def validate_flash_models_config() -> None: """ 验证 Flash 模型配置的完整性 检查: - 每个模型必须有 id, description, enabled 字段 - 每个模型必须有 endpoint 字段且格式正确 - 至少有一个模型是启用的 Raises: ValueError: 如果配置不合法 """ if not GEMINI_FLASH_MODELS: raise ValueError("GEMINI_FLASH_MODELS 列表不能为空") required_fields = ["id", "description", "enabled"] for i, model in enumerate(GEMINI_FLASH_MODELS): # 检查必需字段 for field in required_fields: if field not in model: raise ValueError(f"Flash 模型 #{i} 缺少必需字段: {field}") # 检查端点配置 if "endpoint" not in model: raise ValueError(f"Flash 模型 {model['id']} 缺少 'endpoint' 字段") endpoint = model.get("endpoint", "") if not endpoint or not endpoint.startswith("/v1beta/models/"): raise ValueError( f"Flash 模型 {model['id']} 的 endpoint '{endpoint}' 格式不正确。" f"应以 '/v1beta/models/' 开头" ) # 检查至少有一个启用的模型 if not get_enabled_flash_models(): raise ValueError("至少需要启用一个 Flash 模型") # 在模块加载时验证配置 try: validate_models_config() except ValueError as e: print(f"⚠️ 图像模型配置验证失败: {str(e)}") print(f"⚠️ 请检查 models_config.py 文件") try: validate_flash_models_config() except ValueError as e: print(f"⚠️ Flash 模型配置验证失败: {str(e)}") print(f"⚠️ 请检查 models_config.py 文件")