Skip to content

feat: Multi-User 隔离 + v2.0.x 多版本构建体系#91

Open
jackzhao8886 wants to merge 37 commits into
capfrom
multi-user
Open

feat: Multi-User 隔离 + v2.0.x 多版本构建体系#91
jackzhao8886 wants to merge 37 commits into
capfrom
multi-user

Conversation

@jackzhao8886
Copy link
Copy Markdown
Collaborator

Summary

  • 多用户目录隔离 (Multi-User):通过 FunArt-ComfyUI-Multi-User 自研插件实现 per-user output/input/temp 目录隔离,支持 X-FunArt-Comfy-UserId Header 注入
  • 多版本构建体系 (v2.0.x):支持 v0.3.77 / v0.16.4 两个 ComfyUI 版本并行构建,构建参数全面参数化(CAP_SYSTEM_VERSION / COMFYUI_VERSION / VP 版本等)
  • VisionPlaid 内置 + 版本分离:v0.16.4 默认内置 VisionPlaid 1.2.2 (bitforge 0.2.4),自研插件按版本目录覆盖(v0.16.4/custom_nodes/ 覆盖共享 custom_nodes/
  • 执行历史持久化:HistoryManager 增量 flush 到 NAS,重启后保留执行历史
  • 媒体资产修复:新增 /api/jobs 处理器,修复 CPU 模式下媒体资产面板空白

Changes

核心功能

  • FunArt-ComfyUI-Multi-User 插件:folder_paths patch + execution patch + server patch + DynamicPathProxy
  • Agent 版本感知:根据 COMFYUI_VERSION 加载对应版本配置
  • upload-base 只上传自研插件(社区插件由用户按需安装)

构建系统

  • Makefile 参数化:CAP_SYSTEM_VERSION、COMFYUI_VERSION、VP/bitforge 版本均可按版本覆盖
  • Dockerfile.template 支持多版本 custom_nodes 覆盖(先 COPY 共享 → 再 COPY 版本目录)
  • 内置依赖指纹策略:BUILTIN_DEPENDENCY_VERSION = CAP_SYSTEM_VERSION(裸版本号,不扰动存量)

Bug Fixes

  • 修复 DynamicPathProxy JSON 序列化错误
  • 修复执行线程用户上下文丢失导致图片预览失败
  • 修复 NFS lock contention(移除 SQLite 持久化)
  • 修复 flashinfer 与 VP 的安装冲突
  • 修复 v0.16.4 前端 /api/prompt 路径兼容

Test Plan

  • v0.16.4 新建项目 → ComfyUI 正常启动,VisionPlaid 节点可用
  • v0.3.77 新建项目 → 不受影响,行为与改造前一致
  • Multi-User 隔离 → 不同 UserId 的 output 目录互不可见
  • 存量项目升级 → 指纹未变不触发重装,冷启动时间无退化
  • 全量构建 + OSS upload-base → 自研插件正确上传

🤖 Generated with Claude Code

luling.zqj and others added 30 commits May 19, 2026 11:52
支持多 ComfyUI 版本并行构建和运行时按版本隔离快照路径,
为 v0.16.4 上线做好基础设施准备,同时保证默认行为零变化。

P1 构建基础设施:
- Dockerfile 参数化 (ARG COMFYUI_VERSION/CUSTOM_NODES_JSON)
- 拆分 shared/Makefile.common + 版本子目录 (v0.3.77/, v0.16.4/)
- 顶层 Makefile 新增版本矩阵目标 (build-comfyui-v0.16.4 等)

P2 Agent 运行时:
- constants.py 新增 COMFYUI_VERSION 探测和版本前缀 SNAPSHOT_DIR
- snapshot_manager.py 新路径优先 + legacy fallback (存量快照零迁移)
- entrypoint.bash 版本 banner + 版本感知快照检查
- s.yaml 模板注入 COMFYUI_VERSION 环境变量
- _PROTECTED_ARGS 新增 --multi-user 防冲突

Change-Id: Iec4e8d7ea6654861be6e01a800e5a64c1d3be7b7
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Agent Dockerfile: 确保 built-in/custom_nodes 目录存在,拆分模型下载层
- Dockerfile.template: 修正 build context 相对路径 (code/→去除前缀)
- 版本 Makefile: 修正 CUSTOM_NODES_JSON 路径

Change-Id: I99e3aa74e66f4fd3a66707949556fd2d3476d30e
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
insightface 和 pyworld 需要编译 C++ 扩展,之前 dependencies 阶段
只安装了 git 导致构建失败。

Change-Id: I14d26e8acc98982f4a0ee1b3a8727023ad93eaf7
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TeaCache 依赖 comfy.ldm.lightricks.model.precompute_freqs_cis,该接口在 v0.16.4 中已移除
- ComfyUI v0.16.4 需要 user/ 目录存放 sqlite 数据库文件

Change-Id: I7cc6542c39056a523867715912e0d9dc91ac752d
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
get_object_info 端点序列化节点信息时,被 patch 的 folder_paths 函数返回
DynamicPathProxy 对象,json.dumps 无法识别导致 TypeError。通过 patch
json.JSONEncoder.default 自动将 DynamicPathProxy 转为字符串解决。

Change-Id: I9e73c8229b9d04874d78aa4f08202ec7d6cf9895
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Agent proxy 转发 POST /prompt 请求时,user_id 仅在 HTTP header 中传递,
未注入到 body 的 extra_data 中。ComfyUI 的 post_prompt handler 不会将
header 复制到 extra_data,导致执行线程通过 execution_patch 读取 extra_data
时 user_id 为 default,图片保存到默认目录而非用户目录,/view 预览 404。

修复:在 proxy 中对 POST /prompt 请求,将 user_id 注入 extra_data,
与 API 模式(serverless_api_service.py)的处理方式保持一致。

同时添加 BUG-001(DynamicPathProxy JSON 序列化)和 BUG-002(本次修复)
的调查报告文档。

Change-Id: I5959d4fd09028b1c0cfbab44598597ffcbac8a64
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
v0.16.4 前端通过 /api/prompt(而非 /prompt)提交工作流,
导致 user_id 注入逻辑未触发,执行线程仍使用 default 用户。

Change-Id: Ifd3281aa1a9ca53d53316086a31b8939c78e88d9
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
根因:ComfyUIDevSnapshotLoader 将 custom_nodes 替换为 NAS 软链接,
如果 NAS 上的旧 snapshot 不含 FunArt-ComfyUI-Multi-User 插件,
则 ComfyUI 启动时无法加载该插件,server_patch 不生效,
/view 请求没有用户上下文,导致去默认目录找文件 → 404。

修复:
1. Dockerfile.template: 将自研插件备份到 /root/built-in/custom_nodes/
2. snapshot_loader.py: 创建 NAS 软链接后自动恢复自研插件
3. server_patch.py: 改用 aiohttp middleware(更可靠,兼容所有版本)

Change-Id: I1caed0ecf509d16c1a963a1692c47fd880d82713
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Dockerfile: 备份全部 custom_nodes 到 /root/built-in/(已含新版本兼容插件+自研插件)
- snapshot_loader: 移除 _restore_builtin_plugins(),不再直接复制到 NAS
- builtin_custom_nodes: 移除 INSTALLED_VERSION_FILE 前置检查,始终创建 delta 目录和 yaml

Change-Id: I963d9da5ecd6f835a6966f8656dcbacb8ee01f2b
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change-Id: I90cb60f36dce4a6802c34f03274dd43e9d46ee54
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 移除多行续行中的注释(导致 shell syntax error)
- 移除已过时的 v0.3.77-alpha input 拷贝
- dependency_version.txt 写入 COMFYUI_VERSION 而非硬编码 1.6.5

Change-Id: I6bb8c5380fc17474ed3aed27edf1d2ea58a4478a
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Agent Dockerfile: source image funart-comfyui:v1.6.5 → v1.6.6, version.txt 1.6.5 → 1.6.6
- Makefile.common: 本地产物镜像名 v1.6.5 → v1.6.6, dependency_version.txt 改为 1.6.6
- Dockerfile.template: built-in version.txt 改为 1.6.6

Change-Id: Ie8b4cc9c5222adaf422048fa49f760ce720e34ab
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
上一个 commit 误用 1.6.6,应与目标模板版本 1.6.7 一致。

Change-Id: I901cde42cfedf032bfe6a6205e3fde570cc68595
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ComfyUI v0.16.4 前端使用 /api/jobs 接口展示媒体资产,但 CPU 函数的
GatewayRoutes 未处理此路由,请求穿透到本地 ComfyUI(从不执行 prompt),
导致 history 永远为空。新增 JobsHandler 从 TaskManager 的 HistoryManager
读取数据并返回 v0.16.4 jobs 格式。

Change-Id: I3612fb4e98fe71a628c0e9a6a8e40f17186368e8
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ComfyUI v0.16.4 的 sqlite 数据库默认路径为 /root/user/comfyui.db,
Agent 镜像未创建该目录导致 init_db() 失败,Session 为 None。

Change-Id: I13072f6ad0781bb95940632fccae0de94e1fd9c0
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
数据库路径由 cli_args.py 计算为 comfyui 源码根目录下的 user/comfyui.db,
即 /root/comfyui/user/,而非 /root/user/。
修复位置改为 management_service.py 中 ComfyUI 启动前(快照加载后),
确保目录在正确时机创建。

Change-Id: I8512318c077e6ea18d2b658830fdb84b25acd24a
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 启动时从 ${MNT_DIR}/output/.history.json 加载已完成的历史记录
- 任务完成/删除时去抖写盘(3s 合并高频写入)
- 最多持久化 500 条最近记录,避免文件无限增长
- 原子写入(tmp + rename),不影响并发读取

Change-Id: I67509a6977a372a99d54f10e7362e9e5a28d4e58
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 只持久化展示所需字段(outputs/status/meta/user_id/create_time)
- 丢弃完整 workflow prompt 数据,单条从几十 KB 降至几百字节
- 上限从 500 增至 1000 条(精简后总文件约 500KB)
- jobs_handler 兼容从磁盘加载的精简记录格式

Change-Id: I8119b2902e29cfb0fccde4c62d315e8ece59efe6
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- JSON 文件替换为 SQLite(${MNT_DIR}/output/.history.db)
- DB 不淘汰旧记录,历史永久保留
- 启动时只加载最近 2000 条到内存(API 从内存读取)
- 单条 INSERT OR REPLACE 代替全量重写,性能更好
- NFS 单写者场景下 SQLite 可靠无并发风险

Change-Id: I4fa3fc9d3143e0be2031a7546a135a98611a39dd
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 只写入新完成的脏记录(_dirty_prompt_ids),不全量重写
- 删除操作同步到 DB(_deleted_prompt_ids)
- 去掉 DELETE 淘汰逻辑,DB 保留全部历史
- 启动时只加载最近 2000 条到内存

Change-Id: Id2c36d33a13a989de7eed62312c348b4725e8788
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change-Id: Ibf92da172ed40c59c6ad6c1bb78142cff731546b
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- agent/Dockerfile: COMFYUI_IMAGE 默认 tag v1.6.7 → v2.0.0,内置插件 version.txt 1.6.7 → 2.0.0
- comfyui/shared/Dockerfile.template: 内置插件 version.txt 1.6.7 → 2.0.0
- comfyui/shared/Makefile.common: 中间镜像 tag (普通 + deepgpu) v1.6.7 → v2.0.0,
  以及 .funart/dependency_version.txt 1.6.7 → 2.0.0

DeepGPU 流的独立版本号 1.6.5 (Dockerfile.deepgpu / Makefile.common deepgpu 段) 保持不变。
multi-user 分支改造 Makefile 时为"提高速度"用 --exclude 排除了
comfyui/custom_nodes,改为 mkdir -p 创建空目录上传。但 OSS 不存储空目录,
导致 NAS 下载后没有 custom_nodes/ 文件夹,agent 启动时 symlink 失败:
  Source path does not exist: /mnt/.../custom_nodes

修复:去掉 --exclude,从容器完整拷出 custom_nodes,用 mv 移到 tmp 根
目录后上传(跟 v0.3.77 时代老 Makefile 的行为一致)。

影响:upload-base 构建时间会增加(需拷出插件文件),但保证 OSS 数据完整。
改动:
1. Makefile.common upload-base: custom_nodes 从本地 src/code/comfyui/custom_nodes/
   拷贝(只有 3 个自研插件),不再从容器里提取全部 94 个插件。
   OSS 上 custom_nodes/ 只包含自研插件,新建项目 NAS 只下载这 3 个。

2. Dockerfile.template built-in 备份: 从 cp -r 全部 custom_nodes 改为
   只 cp 3 个自研插件目录(Multi-User / APIs / VisionPlaid)。
   delta 恢复机制只恢复自研插件,不恢复社区插件。

自研插件清单:
- FunArt-ComfyUI-Multi-User (多用户目录隔离)
- FunArt-ComfyUI-APIs (平台 API 节点)
- Comfy-VisionPlaid (加速推理)

用户可通过 ComfyUI-Manager 按需安装社区插件。
容器内仍安装全部 91 个社区插件(用于 pip 依赖安装阶段),
但不上传到 OSS、不备份到 built-in。
Change-Id: If0bb4363c4c723ced667bb295b1de65f8a583ace
避免存量 v0.16.4 实例因指纹变为 2.0.0-vp1.2.1 而在冷启动时
触发 install_all,扰动用户环境。镜像仍内置 VisionPlaid 1.2.1,
仅指纹回退;刷新存量需手动 bump CAP_SYSTEM_VERSION。

Change-Id: I77f3c51860b5444c6780df34bbd8b50ba9f1d4ff
Co-developed-by: Cursor <noreply@cursor.com>
问题 1: VisionPlaid 插件手维护的 __init__.py 跟 pip 包版本不匹配
  - 1.0.3 模块路径 comfy.v1.xxx,1.2.1 改为 comfy.xxx
  - 手写 import 路径猜错导致 ImportError / cannot import name

解决: 删掉手维护的 Comfy-VisionPlaid 源码(共享 + v0.16.4),
改为在 Dockerfile base stage 从 pip 包自带的插件代码拷贝:
  cp -r ${VENV_DIR}/.../vision_plaid/Comfy-VisionPlaid ${COMFYUI_DIR}/custom_nodes/
upload-base 也从容器的 pip 包提取 VisionPlaid 上传到 OSS。
这样插件代码的 __init__.py 始终跟 SDK 版本自动匹配。

问题 2: v0.3.77 构建 COPY break
  - Dockerfile.template 有 COPY comfyui/${COMFYUI_VERSION}/custom_nodes/
  - v0.3.77 下无 custom_nodes/ 目录,Docker COPY 源路径不存在报错退出

解决: v0.3.77 和 v0.16.4 各加 custom_nodes/.gitkeep,确保目录存在。

改动清单:
- 删除 src/code/comfyui/custom_nodes/Comfy-VisionPlaid/ (3 文件)
- 删除 src/code/comfyui/v0.16.4/custom_nodes/Comfy-VisionPlaid/ (3 文件)
- 新增 src/code/comfyui/v0.3.77/custom_nodes/.gitkeep
- 新增 src/code/comfyui/v0.16.4/custom_nodes/.gitkeep
- Dockerfile.template: base stage 加 cp 从 venv 拷 VisionPlaid
- Makefile.common: upload-base 加 docker run tar 提取 VisionPlaid
v0.16.4 前端 fetchJobs.ts 校验 execution_error 必须含 traceback(array)
和 exception_type(string),但 ComfyUI 后端在某些错误场景下不返回这些字段,
导致 schema 校验失败 → /api/jobs 请求报错 → 媒体资产面板显示空白。

修复:在 jobs_handler.py 用 setdefault 补上默认值(traceback=[] / exception_type='')。

不影响 v0.3.77(/api/jobs 是 v0.16.4 新增 API,v0.3.77 前端不调此接口)。
修改 5 处:
- agent/Dockerfile: COMFYUI_IMAGE tag v2.0.0 → v2.0.1 + BUILTIN_DEPENDENCY_VERSION
- v0.16.4/Makefile: CAP_SYSTEM_VERSION
- Makefile.common: CAP_SYSTEM_VERSION 默认值
- Dockerfile.template: BUILTIN_DEPENDENCY_VERSION 默认值
- 根 Makefile: v0.16.4 条件分支 CAP_SYSTEM_VERSION

配合 jobs_handler bugfix (52d21d1) 一起发布,避免 ACR 上 v2.0.0 tag 被覆盖。
Change-Id: Ic39e3a646b720d8ae418b72a306ad3c5bcb593bf
Co-developed-by: Claude <noreply@anthropic.com>
luling.zqj and others added 7 commits June 2, 2026 11:59
Change-Id: I5d5df7f4bff68950006b392c1667a001760175c2
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 新增 v0.16.4/workflows/VisionPlaid/ 下 6 个工作流 JSON
  (FLUX.1-dev, FLUX.2-klein, Qwen-Image-Edit 2511 int4/fp16, 原版, Wan2.2)
- Makefile.common upload-base 加自动上传 workflows/ 到 OSS
- 旧版 v0.3.77 不受影响(无 workflows/ 目录,跳过)
Root Makefile had 2.0.2 while v0.16.4/Makefile has 2.0.1, causing tag
mismatch during make build-comfyui.

Change-Id: I78b367c0b9eda3e2fdc8c030aad8eb7cecfab015
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…NFS lock contention

HistoryManager previously used SQLite on NAS for history persistence,
causing lock contention and cascading instance failures during cold-start
storms. Now purely in-memory — history is lost on restart, which is
acceptable for this use case.

Change-Id: Idfb88231ea9197aca1e4ff9780db3588190416eb
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…elease

CAP_SYSTEM_VERSION 2.0.1 → 2.0.2 to pick up VP 1.2.2 built-in plugins
in the gateway agent image.

Change-Id: I93092d8b48a3d6348a401691c95472a2b7876d7a
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change-Id: I4a425cf908e38cea45155a6a3c68b2e8989ab491
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change-Id: I79a3b411ba4405e49bff2b9013aeb80a31f1742e
Co-developed-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant