# Comfyui_o1key 开发指南 ## 对话原则 始终使用中文进行对话。 ## 编码规范 ⚠️ 重要 ### 文件编码要求 - **所有文本文件必须使用 UTF-8 编码(无 BOM)** - **行结束符使用 LF(Unix 风格),Windows 批处理文件除外(CRLF)** - 项目已配置 `.gitattributes` 和 `.editorconfig` 来自动处理编码 ### 编辑器配置 确保编辑器设置: - 文件编码:UTF-8(无 BOM) - 行结束符:LF - 自动插入文件末尾空行:开启 ## Git 提交规范 ### Commit Message 规范 - **所有 commit message 必须使用英文**,避免中文编码问题 - 使用 Conventional Commits 格式:`: ` ### 常用类型 - `feat`: 新增功能 - `fix`: 修复问题 - `docs`: 文档更新 - `refactor`: 代码重构 - `style`: 代码格式调整 - `test`: 测试相关 - `chore`: 构建/工具配置 ### 示例 ```bash git commit -m "feat: add new model support" git commit -m "fix: resolve image encoding issue" git commit -m "docs: update README installation guide" ``` ## 配置文件管理 ### 基本原则 `.config` 文件包含敏感信息(API 密钥),已添加到 `.gitignore` 中,**不会被提交到版本控制**。 ### 配置方式 用户通过以下方式创建本地配置: 1. **快捷脚本**(推荐) - Windows: 双击 `设置API密钥(win).bat` - Linux/Mac: 运行 `./设置API密钥(mac).sh` - 脚本会自动创建 `.config` 文件 2. **手动创建** - 参考 `.config.example` 模板 - 在插件根目录创建 `.config` 文件 - 填写 API 密钥 3. **环境变量** - 设置 `O1KEY_API_KEY` 环境变量 - 无需创建配置文件 ### 注意事项 - `.config` 文件仅存在于本地,不会被 Git 追踪 - 开发者无需担心意外提交密钥的问题 - 提交代码时会自动忽略 `.config` 文件 ## 项目概述 这是一个 ComfyUI 自定义节点插件,通过 api.o1key.com 调用 AI 模型进行图像生成。 ### 技术栈 - Python 3.7+ - ComfyUI 框架 - aiohttp (异步 HTTP) - Pillow (图像处理) - PyTorch (张量处理) --- ## 目录结构 ``` Comfyui_o1key/ ├── __init__.py # 节点注册入口 ├── models_config.py # 模型配置中心 ⭐ 管理所有支持的模型 ├── version.txt # 版本号文件 ├── update.bat # Windows 自动更新脚本 ├── update.sh # Linux/Mac 自动更新脚本 ├── nodes/ # 节点模块 │ ├── __init__.py │ ├── nano_banana_pro.py # NanoBananaPro 节点 │ └── batch_nano_banana_pro.py # 批量节点 ├── utils/ # 工具模块 │ ├── __init__.py │ ├── image_utils.py # 图像转换工具 │ ├── config.py # 配置管理 │ └── update_checker.py # 更新检查器 ├── clients/ # API 客户端 │ ├── __init__.py │ ├── base_client.py # 客户端基类 │ └── gemini_client.py # Gemini API 客户端 ├── .config.example # 配置文件模板 ├── requirements.txt # 依赖包 └── README.md # 用户文档 ├── 设置API密钥(win).bat # Windows 配置脚本 └── 设置API密钥(mac).sh # Mac/Linux 配置脚本 注:.config 文件在本地自动创建,不提交到版本控制 ``` --- ## 模型管理系统 ### 概述 所有 Nano Banana Pro 支持的模型都在 `models_config.py` 中统一管理。要添加新模型或临时关闭某个模型,只需编辑这个文件即可。 ### 模型配置文件 (models_config.py) #### 配置结构 ```python GEMINI_MODELS = [ { "id": "gemini-3-pro-image-preview-url", "description": "URL 模式,根据分辨率自动选择端点 (1K/2K/4K)", "enabled": True, "endpoint_type": "dynamic", "endpoint": None # 动态端点,由代码根据分辨率选择 }, { "id": "gemini-3-pro-image-preview", "description": "标准模式,固定端点", "enabled": True, "endpoint_type": "standard", "endpoint": "/v1beta/models/gemini-3-pro-image-preview:generateContent" }, # 更多模型... ] ``` #### 字段说明 | 字段 | 类型 | 必需 | 说明 | |------|------|------|------| | `id` | string | 是 | 模型标识符,用于 API 调用 | | `description` | string | 是 | 模型描述,说明特点和适用场景 | | `enabled` | boolean | 是 | 是否启用该模型(false 则在节点中隐藏) | | `endpoint_type` | string | 是 | 端点类型:"dynamic", "standard", "flatfee" | | `endpoint` | string | 是 | API 端点路径(动态端点设为 None) | #### 端点类型说明 - **dynamic**: 根据分辨率动态选择端点(如 gemini-3-pro-image-preview-url) - **standard**: 使用固定端点(如 gemini-3-pro-image-preview) - **flatfee**: 固定费用模式端点(如 gemini-3-pro-image-preview-flatfee) ### 常见操作 #### 1. 添加新模型 在 `GEMINI_MODELS` 列表末尾添加新模型: ```python GEMINI_MODELS = [ # ... 现有模型 ... { "id": "gemini-新模型名称", "description": "新模型的描述和特点", "enabled": True, "endpoint_type": "standard", # 根据实际情况选择 "endpoint": "/v1beta/models/gemini-新模型名称:generateContent" # 配置端点 } ] ``` **注意**: - **固定端点模型**:直接在 `endpoint` 字段填写完整的端点路径即可,无需修改代码 - **动态端点模型**:如果模型需要根据分辨率动态选择端点,设置 `endpoint_type: "dynamic"` 和 `endpoint: None`,并在 `gemini_client.py` 的 `get_endpoint()` 方法中添加对应逻辑 #### 2. 临时关闭模型 将模型的 `enabled` 字段设为 `False`: ```python { "id": "gemini-3-pro-image-preview-url", "description": "URL 模式", "enabled": False, # 临时关闭 "endpoint_type": "dynamic" } ``` 关闭后,该模型将不会出现在 ComfyUI 节点的下拉列表中。 #### 3. 重新启用模型 将 `enabled` 改回 `True`: ```python { "id": "gemini-3-pro-image-preview-url", "enabled": True, # 重新启用 # ... } ``` #### 4. 修改模型描述 直接编辑 `description` 字段: ```python { "id": "gemini-3-pro-image-preview", "description": "标准模式,固定端点,适用于常规图像生成", # 更新描述 # ... } ``` ### 工具函数 `models_config.py` 提供了一些工具函数,可在代码中使用: ```python from ..models_config import ( get_enabled_models, # 获取启用的模型列表 get_all_models, # 获取所有模型(包括禁用的) get_model_config, # 获取指定模型的完整配置 is_model_enabled, # 检查模型是否启用 get_model_description, # 获取模型描述 get_endpoint_type, # 获取端点类型 get_model_endpoint # 获取模型端点 ) # 示例:获取启用的模型 enabled = get_enabled_models() # ['gemini-3-pro-image-preview-url', 'gemini-3-pro-image-preview', ...] # 示例:获取模型配置 config = get_model_config("gemini-3-pro-image-preview-url") # {'id': '...', 'description': '...', 'enabled': True, 'endpoint_type': 'dynamic', 'endpoint': None} # 示例:获取模型端点 endpoint = get_model_endpoint("gemini-3-pro-image-preview") # '/v1beta/models/gemini-3-pro-image-preview:generateContent' ``` ### 节点集成 所有使用模型列表的节点都会自动从 `models_config.py` 加载: ```python from ..models_config import get_enabled_models class NanoBananaPro: @classmethod def INPUT_TYPES(cls): # 自动从配置加载启用的模型 enabled_models = get_enabled_models() return { "required": { "模型": (enabled_models, { "default": enabled_models[0] }), # ... } } ``` ### 配置验证 `models_config.py` 在加载时会自动验证配置: - 检查每个模型是否有必需字段(id, description, enabled, endpoint_type, endpoint) - 检查 `endpoint_type` 是否合法(dynamic, standard, flatfee) - 检查非动态端点模型必须配置有效的 `endpoint` - 检查端点格式是否正确(应以 `/v1beta/models/` 开头) - 确保至少有一个模型是启用的 如果配置不合法,会在终端打印警告信息。 ### 最佳实践 1. **添加新模型前**: - 确认模型使用 Gemini 原生接口格式 - 确认端点规则(dynamic/standard/flatfee) - 编写清晰的描述说明 2. **临时测试**: - 关闭其他模型,只启用测试模型 - 验证功能后再重新启用其他模型 3. **版本控制**: - `models_config.py` 应纳入版本控制 - 重大模型变更应记录在 `CHANGELOG.md` 中 4. **文档更新**: - 添加新模型后,更新 `README.md` 中的模型列表 - 如有特殊使用说明,添加到文档中 --- ## 开发新节点流程 ### 1. 创建节点文件 在 `nodes/` 目录下创建新的 Python 文件: ```python # nodes/my_new_node.py from typing import Optional, Tuple import torch from ..utils.image_utils import tensor_to_pil, pil_to_tensor from ..clients.gemini_client import GeminiAPIClient class MyNewNode: """节点描述""" def __init__(self): self.client = None @classmethod def INPUT_TYPES(cls): return { "required": { "prompt": ("STRING", {"default": "", "multiline": True}), # 更多参数... }, "optional": { "images": ("IMAGE",) } } RETURN_TYPES = ("IMAGE",) RETURN_NAMES = ("images",) FUNCTION = "execute" CATEGORY = "image/generation" def execute(self, prompt: str, images: Optional[torch.Tensor] = None) -> Tuple[torch.Tensor]: # 实现逻辑 pass ``` ### 2. 注册节点 在 `nodes/__init__.py` 中添加导出: ```python from .my_new_node import MyNewNode __all__ = ['NanoBananaPro', 'MyNewNode'] ``` 在根 `__init__.py` 中注册: ```python from .nodes import NanoBananaPro, MyNewNode NODE_CLASS_MAPPINGS = { "NanoBananaPro": NanoBananaPro, "MyNewNode": MyNewNode } NODE_DISPLAY_NAME_MAPPINGS = { "NanoBananaPro": "Nano Banana Pro", "MyNewNode": "My New Node" } ``` ### 3. 更新 CHANGELOG.md 记录新增功能。 --- ## ComfyUI 节点规范 ### INPUT_TYPES 参数类型 | 类型 | 格式 | 示例 | |------|------|------| | 字符串 | `("STRING", {...})` | `("STRING", {"default": "", "multiline": True})` | | 整数 | `("INT", {...})` | `("INT", {"default": 1, "min": 1, "max": 100})` | | 浮点数 | `("FLOAT", {...})` | `("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.1})` | | 下拉选项 | `([...], {...})` | `(["option1", "option2"], {"default": "option1"})` | | 图像 | `("IMAGE",)` | 放在 optional 中 | ### 返回值规范 ```python RETURN_TYPES = ("IMAGE", "MASK", "STRING") # 类型元组 RETURN_NAMES = ("images", "mask", "text") # 名称元组 ``` ### 必须的类属性 ```python FUNCTION = "execute" # 执行函数名 CATEGORY = "image/generation" # 节点分类路径 ``` --- ## 工具模块使用 ### 图像转换 (utils/image_utils.py) ```python from ..utils.image_utils import tensor_to_pil, pil_to_tensor # ComfyUI Tensor → PIL Image 列表 pil_images = tensor_to_pil(tensor) # tensor: [B, H, W, C], range [0, 1] # PIL Image 列表 → ComfyUI Tensor tensor = pil_to_tensor(pil_images) # 返回 [B, H, W, C], range [0, 1] # PIL → Base64 from ..utils.image_utils import encode_image_to_base64 b64_str = encode_image_to_base64(pil_image) # Base64 → PIL from ..utils.image_utils import decode_base64_to_pil pil_image = decode_base64_to_pil(b64_str) ``` ### 配置管理 (utils/config.py) ```python from ..utils.config import get_api_key, get_api_key_or_raise, load_config, get_api_base_url # 获取 API 密钥(返回 None 如果未找到) api_key = get_api_key("O1KEY_API_KEY") # 获取 API 密钥(抛出异常如果未找到) api_key = get_api_key_or_raise("O1KEY_API_KEY") # 获取 API 基础 URL(统一配置) base_url = get_api_base_url() # 默认: https://vip.o1key.com # 加载完整配置 config = load_config() ``` ### API 基础 URL 配置 所有 API 客户端都使用统一的基础 URL 配置,默认为 `https://vip.o1key.com`。 #### 配置优先级 1. **环境变量** `O1KEY_API_BASE_URL`(优先级最高) 2. **.config 文件**中的 `O1KEY_API_BASE_URL` 配置项 3. **默认值** `https://vip.o1key.com`(在 `utils/config.py` 中定义) #### 修改 API 地址 **方法 1:修改默认值(影响所有用户)** 编辑 `utils/config.py`: ```python # 修改此常量 DEFAULT_API_BASE_URL = "https://your-api-domain.com" ``` **方法 2:使用环境变量(推荐,不影响代码)** 在系统环境变量中设置: ```bash # Windows set O1KEY_API_BASE_URL=https://your-api-domain.com # Linux/Mac export O1KEY_API_BASE_URL=https://your-api-domain.com ``` **方法 3:在 .config 文件中配置** 在插件根目录的 `.config` 文件中添加: ``` O1KEY_API_BASE_URL=https://your-api-domain.com ``` #### 使用示例 所有客户端会自动使用统一配置: ```python from ..utils.config import get_api_base_url # 获取当前配置的 API 地址 base_url = get_api_base_url() print(f"当前 API 地址: {base_url}") ``` --- ## API 客户端使用 ### 使用 GeminiAPIClient ```python from ..clients.gemini_client import GeminiAPIClient # 初始化(自动读取配置) client = GeminiAPIClient() # 同步生成(用于 ComfyUI 节点) images = client.generate_sync( prompt="描述文字", model="gemini-3-pro-image-preview-url", resolution="2K", aspect_ratio="1:1", batch_size=1, images=None, # 可选:输入图像列表 progress_callback=None ) ``` ### 创建新的 API 客户端 继承 `BaseAPIClient` 并实现抽象方法: ```python from ..clients.base_client import BaseAPIClient class MyAPIClient(BaseAPIClient): def __init__(self): super().__init__( base_url="https://api.example.com", api_key=get_api_key_or_raise("MY_API_KEY"), max_request_size=20 * 1024 * 1024 ) def get_endpoint(self, **kwargs) -> str: return "/v1/generate" def build_request_body(self, **kwargs) -> dict: return {"prompt": kwargs.get("prompt", "")} def parse_response(self, response: dict) -> Any: return response.get("result") ``` --- ## API 端点说明 ### Gemini 模型端点 **gemini-3-pro-image-preview-url** (根据分辨率动态选择): - 1K: `/v1beta/models/gemini-3-pro-image-preview-url:generateContent` - 2K: `/v1beta/models/gemini-3-pro-image-preview-2k-url:generateContent` - 4K: `/v1beta/models/gemini-3-pro-image-preview-4k-url:generateContent` **gemini-3-pro-image-preview** (固定端点): - `/v1beta/models/gemini-3-pro-image-preview:generateContent` **gemini-3-pro-image-preview-flatfee** (固定端点): - `/v1beta/models/gemini-3-pro-image-preview-flatfee:generateContent` ### 请求格式 ```json { "contents": [{ "role": "user", "parts": [ {"text": "提示词"}, {"inline_data": {"mime_type": "image/png", "data": "base64..."}} ] }], "generationConfig": { "responseModalities": ["TEXT", "IMAGE"], "imageConfig": { "aspectRatio": "1:1", "imageSize": "2K" } } } ``` --- ## 代码规范 ### 命名约定 - 类名:PascalCase(如 `NanoBananaPro`) - 函数/方法:snake_case(如 `tensor_to_pil`) - 常量:UPPER_CASE(如 `API_BASE_URL`) - 私有方法:前缀下划线(如 `_load_config`) ### 类型注解 所有公开函数必须有类型注解: ```python def function_name(param1: str, param2: Optional[int] = None) -> List[Image.Image]: pass ``` ### 文档字符串 使用 Google 风格的 docstring: ```python def function_name(param1: str, param2: int) -> bool: """ 函数简短描述 Args: param1: 参数1说明 param2: 参数2说明 Returns: 返回值说明 Raises: ValueError: 异常情况说明 Example: >>> result = function_name("test", 42) >>> print(result) True """ pass ``` ### 错误处理 ```python try: # 业务逻辑 pass except ValueError as e: # 用户输入错误 print(f"节点名: 输入错误 - {str(e)}") raise except RuntimeError as e: # API 或网络错误 print(f"节点名: API 错误 - {str(e)}") raise except Exception as e: # 未知错误 print(f"节点名: 未知错误 - {str(e)}") raise ``` --- ## 限制与约束 | 限制项 | 值 | 说明 | |--------|-----|------| | 请求体大小 | 20MB | 超过会报错 | | 输入图像数量 | 14张 | 图生图模式限制 | | 批次大小 | 1-1000 | 并发生成数量 | | 支持的分辨率 | 1K/2K/4K | API 限制 | --- ## 测试检查清单 新节点开发完成后,验证以下场景: - [ ] 文生图基础功能 - [ ] 图生图功能(如支持) - [ ] 不同分辨率(1K/2K/4K) - [ ] 不同宽高比 - [ ] 批量生成 - [ ] 错误处理(无 API 密钥、网络错误等) - [ ] 边界条件(最大图像数、最大批次) --- ## 更新日志 修改代码后,更新 `CHANGELOG.md` 记录变更。 格式: ```markdown ## [版本号] - 日期 ### Added - 新增功能 ### Changed - 变更内容 ### Fixed - 修复问题 ``` --- ## 版本发布流程 ### 1. 准备发布 发布新版本前确认以下事项: - [ ] 所有功能测试通过 - [ ] 更新 `CHANGELOG.md`(记录本次变更) - [ ] 更新 `version.txt`(更新版本号) - [ ] 更新 `README.md`(如有新功能需要说明) ### 2. 版本号规范 遵循语义化版本 (Semantic Versioning): - **主版本号** (Major): 重大架构变更、不兼容的 API 修改 - **次版本号** (Minor): 新增功能、向后兼容 - **修订号** (Patch): Bug 修复、小改进 示例:`v1.10.2` → Major.Minor.Patch ### 3. 发布步骤 ```bash # 1. 更新版本号 echo "v1.11.0" > version.txt # 2. 提交变更 git add . git commit -m "Release v1.11.0: 添加新功能描述" # 3. 创建标签 git tag v1.11.0 # 4. 推送到远程 git push origin main --tags ``` ### 4. 用户更新 用户运行更新脚本即可获取最新版本: - **Windows**: 双击 `update.bat` - **Linux/Mac**: 运行 `./update.sh` 更新脚本会自动: - 检查远程更新 - 备份配置文件 - 拉取最新代码 - 更新依赖包 - 显示更新日志 ---