""" EPEEKit 集中配置。 所有可调参数从环境变量读取,每次调用实时读取(不缓存),确保 load_dotenv() 后生效。 """ import os from typing import Any def get_llm_max_iterations() -> int: return int(os.getenv("LLM_MAX_ITERATIONS", "5")) # ─── LLM 模型注册表 ───────────────────────────────────── # # 每个模型的配置说明: # id — 前端/API 使用的短 ID # name — 显示名称 # provider — API 提供者(对应 _get_client 的分发键) # model_id — 传给 OpenAI SDK 的 model 参数 # description — 前端下拉列表说明文字 # vision — 是否支持多模态图片输入 LLM_MODELS: dict[str, dict[str, Any]] = { "gpt-5.4": { "id": "gpt-5.4", "name": "GPT-5.4", "provider": "vectorengine", "model_id": "gpt-5.4", "description": "知识工作与计算机操控最强,1M 上下文", "vision": True, }, "claude-sonnet-4-6": { "id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6", "provider": "vectorengine", "model_id": "claude-sonnet-4-6", "description": "高性价比编码与日常任务", "vision": True, }, "claude-opus-4-6": { "id": "claude-opus-4-6", "name": "Claude Opus 4.6", "provider": "vectorengine", "model_id": "claude-opus-4-6", "description": "编码与专家级推理最强,128K 输出", "vision": True, }, "gemini-3.1-pro-preview": { "id": "gemini-3.1-pro-preview", "name": "Gemini 3.1 Pro", "provider": "vectorengine", "model_id": "gemini-3.1-pro-preview", "description": "推理最强、价格最低,2M 上下文", "vision": True, }, "glm-4.7": { "id": "glm-4.7", "name": "GLM-4.7", "provider": "vectorengine", "model_id": "glm-4.7", "description": "智谱 AI,中文能力突出,免费额度", "vision": False, }, "gpt-4o-mini": { "id": "gpt-4o-mini", "name": "GPT-4o Mini", "provider": "vectorengine", "model_id": "gpt-4o-mini", "description": "轻量快速、高性价比", "vision": True, }, "deepseek-chat": { "id": "deepseek-chat", "name": "DeepSeek Chat", "provider": "deepseek", "model_id": "deepseek-chat", "description": "中文对话优化(直连)", "vision": False, }, } def get_default_llm_model_id() -> str: """返回 .env 中配置的默认 LLM 短 ID,不在注册表中则回退到 gpt-5.4。""" env_model = os.getenv("LLM_MODEL", "gpt-5.4") if env_model in LLM_MODELS: return env_model return "gpt-5.4" def get_llm_model_config(model_id: str | None = None) -> dict[str, Any]: """根据短 ID 获取 LLM 模型配置,未指定或不存在则使用默认模型。""" if model_id and model_id in LLM_MODELS: return LLM_MODELS[model_id] return LLM_MODELS[get_default_llm_model_id()] def get_llm_models_list() -> list[dict]: """返回前端下拉列表所需的 LLM 模型摘要信息。""" return [ { "id": cfg["id"], "name": cfg["name"], "description": cfg["description"], "vision": cfg.get("vision", False), } for cfg in LLM_MODELS.values() ] # ─── 图像模型注册表 ───────────────────────────────────── # # 每个模型的配置说明: # id — 前端/API 使用的短 ID # name — 显示名称 # provider — 生成服务提供者(对应 image_gen.py 中的 Provider) # model_id — Replicate 上的完整模型 ID # description — 前端下拉列表中的说明文字 # supports_ref_image — 是否原生支持参考图输入(IP-Adapter 等) # ref_image_param — 传给 Replicate 的参考图参数名(模型间可能不同) # num_images_param — 批量生成参数名(Flux 用 num_outputs,Kolors 用 number_of_images) # default_params — 默认推理参数 IMAGE_MODELS: dict[str, dict[str, Any]] = { "gpt-image-1.5": { "id": "gpt-image-1.5", "name": "GPT Image 1.5", "provider": "openai", "model_id": "gpt-image-1.5", "description": "OpenAI 最强生图,文字渲染与 prompt 理解最佳,支持多图参考", "supports_ref_image": True, "max_ref_images": 16, "default_params": { "size": "1024x1024", "quality": "high", }, }, "gemini-3.1-flash-image": { "id": "gemini-3.1-flash-image", "name": "Gemini 3.1 Flash Image", "provider": "gemini_native", "model_id": "gemini-3.1-flash-image-preview", "description": "Google 原生生图,速度快、价格低,支持多图参考(最多 14 张)", "supports_ref_image": True, "max_ref_images": 14, "default_params": {}, }, "flux-schnell": { "id": "flux-schnell", "name": "Flux Schnell", "provider": "replicate", "model_id": "black-forest-labs/flux-schnell", "description": "快速生成,适合快速迭代", "supports_ref_image": False, "num_images_param": "num_outputs", "default_params": { "aspect_ratio": "1:1", "output_format": "png", }, }, "flux-dev": { "id": "flux-dev", "name": "Flux Dev", "provider": "replicate", "model_id": "black-forest-labs/flux-dev", "description": "高质量生成,细节更好", "supports_ref_image": False, "num_images_param": "num_outputs", "default_params": { "aspect_ratio": "1:1", "output_format": "png", }, }, "sdxl": { "id": "sdxl", "name": "Stable Diffusion XL", "provider": "replicate", "model_id": "stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b", "description": "经典 SDXL,支持 negative prompt", "supports_ref_image": False, "num_images_param": "num_outputs", "default_params": { "width": 1024, "height": 1024, "num_inference_steps": 50, "guidance_scale": 7.5, }, }, "instant-style": { "id": "instant-style", "name": "InstantStyle", "provider": "replicate", "model_id": "jyoung105/instant-style:c6f01e12f31cb99f9ee774a78992a71294f630a6f433d9aecfdc33b816fc4baa", "description": "强风格迁移,画风还原度高(较慢)", "supports_ref_image": True, "ref_image_param": "style_image", "num_images_param": "num_outputs", "default_params": { "width": 1024, "height": 1024, "num_inference_steps": 30, "guidance_scale": 5, "style_strength": 1.0, "block_mode": "style-only", "adapter_mode": "original", }, }, "kolors-ipadapter": { "id": "kolors-ipadapter", "name": "Kolors IP-Adapter", "provider": "replicate", "model_id": "fofr/kolors-with-ipadapter:5a1a92b2c0f81813225d48ed8e411813da41aa84e7582fb705d1af46eea36eed", "description": "风格参考生成,上传参考图效果最佳", "supports_ref_image": True, "ref_image_param": "image", "num_images_param": "number_of_images", "default_params": { "width": 1024, "height": 1024, "steps": 25, "cfg": 4, "ip_adapter_weight": 0.8, "ip_adapter_weight_type": "style transfer precise", "output_format": "png", }, }, # Mesh 管道专用的内部模型(internal=True 不展示到前端下拉): # 输入两张图:image=原图做 IP-Adapter 风格参考;controlnet_input=normal/depth 结构参考。 # 用途:在 Stage 3 对 Trellis 抽出来的 normal 帧做二次重绘,还原原画风。 "ip-adapter-controlnet-depth": { "id": "ip-adapter-controlnet-depth", "name": "IP-Adapter + ControlNet Depth (内部)", "provider": "replicate", "model_id": "chigozienri/ip_adapter-sdxl-controlnet-depth:0436c8702ef52616be5c30948551b3af6a86c821cca9b01f11ac297624fff14c", "description": "Mesh 管道 Stage 3 专用:IP-Adapter 保风格 + ControlNet 保几何", "internal": True, "supports_ref_image": True, "ref_image_param": "image", # IP-Adapter 参考图字段 "default_params": { "scale": 0.75, # IP-Adapter 权重(建议 0.7-0.8) "controlnet_conditioning_scale": 0.8, # 结构约束(建议 0.7-0.9) "prompt": "same building, same art style, consistent with reference", "negative_prompt": "blurry, distorted, different style, realistic photo, photograph", "num_outputs": 1, "num_inference_steps": 30, }, }, } def get_style_restore_model_id() -> str: """Mesh 管道 Stage 3 使用的内部模型短 ID。""" return "ip-adapter-controlnet-depth" # ─── 视角变换模型注册表 ───────────────────────────────── # # 视角变换模型不用于生图,而是将已有图片转换为不同视角。 # 输入通常只需一张图,输出为多个固定视角的图片。 VIEW_TRANSFORM_MODELS: dict[str, dict[str, Any]] = { "zero123plus": { "id": "zero123plus", "name": "Zero123++", "provider": "replicate", "model_id": "jd7h/zero123plusplus:c69c6559a29011b576f1ff0371b3bc1add2856480c60520c7e9ce0b40a6e9052", "description": "单图生成 6 个固定视角,适合建筑/物体的多角度预览", "pipeline": "grid", "output_views": [ {"azimuth": 30, "elevation": 30}, {"azimuth": 90, "elevation": -20}, {"azimuth": 150, "elevation": 30}, {"azimuth": 210, "elevation": -20}, {"azimuth": 270, "elevation": 30}, {"azimuth": 330, "elevation": -20}, ], "grid_layout": {"cols": 2, "rows": 3}, "enabled": True, }, "trellis": { "id": "trellis", "name": "Trellis (Mesh Pipeline)", "provider": "replicate", "model_id": "firtoz/trellis:e8f6c45206993f297372f5436b90350817bd9b4a0d52d2a76df50c1c8afa2b3c", "description": "3D 重建 + 任意视角 + 风格还原,建筑友好(~30s 重建 + 72s/张重绘)", "pipeline": "mesh", "default_params": { "texture_size": 1024, "mesh_simplify": 0.95, "generate_color": True, "generate_normal": True, "generate_model": True, "save_gaussian_ply": False, "ss_sampling_steps": 12, "slat_sampling_steps": 12, "ss_guidance_strength": 7.5, "slat_guidance_strength": 3.0, }, "default_azimuths": [0, 60, 120, 180, 240, 300], "default_elevations": [0, 0, 0, 0, 0, 0], "enabled": True, }, "hunyuan3d": { "id": "hunyuan3d", "name": "Hunyuan3D-2 (高质量)", "provider": "replicate", "model_id": "tencent/hunyuan3d-2:b1b9449a1277e10402781c5d41eb30c0a0683504fb23fab591ca9dfc2aabe1cb", "description": "几何质量最佳(~127s),需自定义渲染器(二期启用)", "pipeline": "mesh", "default_params": { "steps": 50, "guidance_scale": 5.5, "octree_resolution": 256, "remove_background": True, }, "enabled": False, }, } def get_view_transform_model_config(model_id: str | None = None) -> dict[str, Any]: """获取视角变换模型配置,默认返回 zero123plus。""" if model_id and model_id in VIEW_TRANSFORM_MODELS: return VIEW_TRANSFORM_MODELS[model_id] return VIEW_TRANSFORM_MODELS["zero123plus"] def get_view_transform_models_list() -> list[dict]: """返回前端下拉列表所需的视角变换模型摘要信息(只包含 enabled=True 的)。""" return [ { "id": cfg["id"], "name": cfg["name"], "description": cfg["description"], "pipeline": cfg.get("pipeline", "grid"), } for cfg in VIEW_TRANSFORM_MODELS.values() if cfg.get("enabled", True) ] def get_default_image_model_id() -> str: """返回 .env 中配置的默认模型短 ID,若不在注册表中则回退到 flux-schnell。""" env_model = os.getenv("IMAGE_MODEL", "flux-schnell") for mid, cfg in IMAGE_MODELS.items(): if cfg["model_id"] == env_model or mid == env_model: return mid return "flux-schnell" def get_image_model_config(model_id: str | None = None) -> dict[str, Any]: """根据短 ID 获取模型配置,未指定或不存在则使用默认模型。""" if model_id and model_id in IMAGE_MODELS: return IMAGE_MODELS[model_id] return IMAGE_MODELS[get_default_image_model_id()] def get_ref_image_model_id() -> str | None: """返回有参考图时推荐使用的模型 ID(第一个 supports_ref_image=True 的模型)。""" for mid, cfg in IMAGE_MODELS.items(): if cfg.get("supports_ref_image"): return mid return None def get_image_models_list() -> list[dict]: """返回前端下拉列表所需的模型摘要信息(过滤掉 internal=True 的内部模型)。""" return [ { "id": cfg["id"], "name": cfg["name"], "description": cfg["description"], "supports_ref_image": cfg.get("supports_ref_image", False), } for cfg in IMAGE_MODELS.values() if not cfg.get("internal", False) ] # ─── 记忆系统配置 ───────────────────────────────────── def get_deepseek_api_key() -> str: return os.getenv("DEEPSEEK_API_KEY", "") def get_ollama_base_url() -> str: return os.getenv("OLLAMA_BASE_URL", "http://localhost:11434") def get_mem0_embedding_model() -> str: return os.getenv("MEM0_EMBEDDING_MODEL", "nomic-embed-text") def get_max_recent_turns() -> int: return int(os.getenv("MAX_RECENT_TURNS", "20")) # ─── 图像输出配置 ───────────────────────────────────── def get_image_aspect_ratio() -> str: return os.getenv("IMAGE_ASPECT_RATIO", "1:1") def get_image_output_format() -> str: return os.getenv("IMAGE_OUTPUT_FORMAT", "png")