Orieileen/Canvex

专为电商卖家和美工打造的无限画布 AI 工具。生图、改图、抠图、换视角、做视频、AI Skills / An infinite canvas AI tool built for e-commerce sellers and designers — generate, edit, cut out, change perspectives, create videos, and use AI Skills.

TypeScript

24

277 commits

updated Sep 14, 2026

See the code

README

Canvex

Canvex 是一个具有对话、skills、生成和编辑图像和视频能力的无限画布 LLM Agent。这是一款专门为电商卖家/美工做的工具。通过场景管理,可以将多个画布用于不同的项目。

Frontend Backend Database Queue Agent

语言:English

功能

  • 聊天即创作 —— 在画布底部聊天框输入提示词,llm agent 生成一张或多张图片(或一段视频)并落到画布上。chat框(聊天记录)本身就是画布上的一个框,能像别的元素一样拖动、缩放、滚动。
  • 任意图片上的 AI 工具栏 —— 选中图片即弹出基于excalidraw原生选框和箭头工作的浮动工具栏:
    • 编辑 —— 用提示词改风格/改内容。
    • 抠图 —— 一键去背景抠出主体。
    • 拆分 —— 从一张图产出上下两张:抠出主体图 + 去掉主体的干净背景图。
    • 换视角 —— 通过改变摄像头位置,从新机位重新渲染不同角度的图片(fal.ai LoRA)。
    • 视频 —— 把静图变成一段动画。时长、比例、画质三个旋钮全部按你选的模型来
    • 样机 —— 借深度把一张设计图贴到另一张图上,带 深度 / 蒙版 / 不透明度 控制。
    • 合并 / 调整 / 下载 / 发到聊天 —— 本地拍平选区、Lightroom 风格调色面板、导出画布、或把图作为LLM Agent参考附件。
  • 多图合成 —— 一次框选最多 8 张图,「图像」页签会变成「合成 N 张图…」,这些图会一起发给api供应商。其余工具是单图操作,多选时置灰。
  • 框 & 箭头标注 —— 精细化编辑图片:在图上画框/箭头/文字来指向要改的区域。
  • Skills(技能) —— agent 会自己判断该不该用skill(如Canvex预置的 image-prompt-sop 把模糊需求改写成高质量单图提示词、amazon-listing-pack-sop 一键生成协调的 7 张亚马逊套图)。在侧栏「技能库」可以装自己的skill:拖一个 SKILL.md 进去(或者直接在浏览器里写),下一条消息 agent 就能使用,不需要重启应用
  • 场景 —— 侧栏里多个独立画布:新建、重命名、删除、快速切换;编辑自动保存。置顶是浏览器本地的偏好(localStorage),不跨设备同步。
  • 素材库 —— 保存你生成过的所有图片/视频,按画布分组;点缩略图即可重新插回当前画布。

架构概览

flowchart LR
  subgraph FE["前端 — React + Excalidraw"]
    Chat["聊天框"]
    Bar["AI 工具栏"]
  end
  subgraph BE["后端 — Django + DRF"]
    Agent["deepagents agent<br/>(skills + tools)"]
    API["job 端点"]
  end
  Q[["Celery 队列<br/>canvas · canvas_cpu"]]
  Prov["图像 / 视频 / fal.ai 供应商"]

  Chat -->|"POST /chat/ (SSE)"| Agent
  Agent -->|"generate_image · generate_video"| Q
  Bar -->|"编辑 · 抠图 · 拆分 · 换视角 · 视频"| API --> Q
  Q --> Prov --> Q
  Q -->|"轮询 job → 落到画布"| FE
  • 聊天(llm agent)是 deepagentscreate_deep_agent),带两个工具(generate_imagegenerate_video)、一份按场景隔离的 memory 文件、以及按需展开的 SKILL.md 技能。每轮对话历史从数据库回放(不需要独立的记忆存储)。
  • 每次生成都是异步 job:API 建一条 QUEUED 记录、提交后入 Celery 队列;前端轮询 job 直到结果就绪再落到画布。抠图是两段链(LLM 出白底 → CPU rembg 出 alpha)。

部署

1)克隆

git clone https://github.com/Orieileen/Canvex.git
cd Canvex

2)配置

cp .env.example .env

cp .env.example .env后默认值直接可用,不需要更改.env里的任何一行这里不填任何 API key —— api key在应用内配置(第 4 步)。.env 只管基础设施:端口、数据库,以及 PUBLIC_MEDIA_BASE(见下表)。

3)启动(Docker)

前置:Docker + Docker Compose。

docker compose up -d --build

4)在 Canvex 添加通道

由于各家供应商的请求参数各不相同,Canvex 预设了一套 API 供应商格式(不是广告):

  1. 聊天(LLM agent)模型 —— Canvex 预设了 LLM agent 的第三方供应商 兔子 tu-zi:在 tu-zi 注册后拿到 API key,填进 Canvex 的通道配置即可用。必须是支持 OpenAI 风格 tool calling 的 key —— 不支持的填进来,聊天框会回一段文字然后画布上什么都不发生。
  2. 图片生成 · 自定义模板 —— Canvex 预设了生成图片的第三方供应商 API Mart:在 apimart 注册后拿到 API key,填进配置即可用。编辑图片、拆分图片、以及 LLM agent 调用的生图 tool,都用这一把 key。
  3. 视角重渲染 —— 先去 fal.ai 注册拿到 API key,用于「换视角」(改变机位)功能。用的模型是 fal-ai/qwen-image-edit-2511-multiple-angles
  4. 视频生成 · 自定义模板 —— Canvex 预设了生成视频的第三方供应商 API Mart:同样是注册拿到 API key 填进配置即可用。

apikey使用步骤:

快捷配置 —— 打开 http://localhost:5173,点左侧栏的「通道配置」。在「快捷配置」中填入对应供应商的key即可。下面列出Canvex可用预设:

角色预设
聊天兔子 tu-zi、OpenAI、Google、DeepSeek、智谱 GLM
生图API Mart、OpenAI、Google
视频API Mart
换视角fal.ai

以下有三种方法可以使用:

1.聊天/llm agent用兔子、生图和视频用 API Mart,变换视角使用fal.ai是这个项目经过测试得出跑的最稳最便宜的供应商 —— 这里每个功能都是对着它们做出来、验过来的,所以想最快跑通就配这两家。其余几条聊天/llm agent官方供应商通道也可以使用,不过openai等国外供应商一方面比较贵,一方面api并不好买,除了tu-zi也可以使用国内llm厂商,而图片apimart则是便宜+稳定。换视角只有一个选项,因为视角 LoRA 只在 fal.ai 上。

2.从一段 curl 开始配置供应商 —— 如果要使用不在Canvex列表的供应商走这条:把你需要的api供应商文档里的示例 curl 粘进去,Canvex向导会替你自动把通道拼出来,不用写 JSON。(这个功能可能不适配所有供应商)

3.新建自定义供应商配置 —— 完全自己写请求模板。你需要知道供应商所有的请求参数以及值怎么写,并手动拼成json填入自定义供应商

模型行右边的 ⚡ 会真发一次最小生成。它只出现在生图和换视角通道上:聊天通道要验的是它认不认 tools 参数,直接在聊天框里说「生成一张图」最快;视频则太慢,撑不过一次同步测试 —— 对视频来说,画布上第一条真片子就是那次测试。

每条通道名字左边有一个状态点:绿 = 上次调用通了,橙 = 上次失败,空心 = 还没调用过。真实生成也会更新它,不只是 ⚡ —— 一条通道哪天悄悄坏了(key 过期、额度打光、供应商换端点),在这里一眼能看见,而不是变成又一次莫名其妙的生成失败。展开失败的卡片,能看到供应商返回的原文,上面还有一句说这属于哪类问题、该改哪儿:key 过期、余额打光、模型名供应商不认、以及人家自己挂了,在原始报错里长得都差不多,而其中只有一部分是靠改配置能解决的。

每个模型行下面还能挂自己的覆盖项 —— 一条通道底下挂着四十个在时长、比例、键名上互相不一致的模型,而不是建四十条通道,靠的就是它。如果你是手写通道:这些旋钮填错大多不会报错,只是静默失效 —— 比例发到一个供应商不读的键上不是错误,只是那个设置永远不起作用。

环境变量

最少需要这些就能跑起来(完整列表 + 调优旋钮见 .env.example)(Canvex已经配置好了环境变量,所以以下只做展示说明,不需要任何更改):

变量必填说明
PUBLIC_MEDIA_BASE你的浏览器访问这个后端的地址(默认 http://localhost:28000)。只用在「发到聊天」附件那几个绝对 URL 上;从别的机器/域名打开这个应用时才需要改。供应商永远不会来拉它 —— 源图要么内联成 base64、要么由我们主动推给供应商,所以自托管不需要任何隧道、CDN 或公网地址。
CANVAS_AGENT_STORE_BACKENDmemory(默认,进程内)或 postgres(持久化 agent 记忆;同时要设 CANVAS_AGENT_STORE_DSN,并装上 langgraph-checkpoint-postgres)。
POSTGRES_DB / _USER / _PASSWORD默认都是 canvex
BACKEND_PORT / FRONTEND_PORT宿主端口,默认 28000 / 5173
VITE_API_URL前端调用的后端地址。Docker Compose 会传 http://localhost:28000;不走 Compose 直接跑 dev server 又不设它的话,代码回落到 :8000,连不上。

API

所有路由在 /api/v1/canvas/ 下。

用途端点
场景(CRUD)GET/POST /scenes/GET/PATCH/DELETE /scenes/{id}/
聊天(SSE 流)POST /scenes/{id}/chat/
图像编辑 / 生成POST /scenes/{id}/image-edit/GET /image-edit-jobs/{job_id}/
拆分(主体 + 背景)POST /scenes/{id}/split/ → 返回两个 job,都在 /image-edit-jobs/{job_id}/ 轮询
视频POST /scenes/{id}/video/GET /video-jobs/{job_id}/
换视角(fal.ai)POST /scenes/{id}/angle/GET /angle-jobs/{job_id}/
进行中的 job(恢复轮询)GET /scenes/{id}/active-jobs/
每个场景的 job 历史GET /scenes/{id}/image-edit-jobs//video-jobs//angle-jobs/
发到聊天的上传POST /scenes/{id}/upload-attachment/
素材库GET /media-library/folders/GET /media-library/folders/{scene_id}/items/
Agent 当前看得见的技能GET /skills/
装 / 卸技能GET / POST /skill-library/PATCH / DELETE /skill-library/{id}/
通道(CRUD + 嵌套的模型)GET/POST /image-providers/GET/PATCH/PUT/DELETE /image-providers/{id}/
⚡ 测一条通道POST /image-providers/{id}/test/ —— 失败也返 200,带原始报文 + 诊断码
表单字段表 + 一键预设GET /image-providers/schema/
curl 向导POST /image-providers/wizard/parse/(只解析不发送)、POST /image-providers/wizard/probe/(拿还没保存的通道真发一次生成)
工具栏选择器读的模型列表GET /image-models/(回包里不含 base URL / key)

聊天端点走 SSEtext/event-stream,每个事件的帧格式是 data: <json>\n\n)。事件类型:user_createdassistant_delta(逐 token 的文字流,实际量最大的就是它)、tool_calltool_resultcanvas_asset{url},agent 本轮产出、客户端要落到画布的图)、assistant_finalassistanterrordone

后端

技术栈:Django + DRF + Celery + Redis + PostgreSQL + deepagents(底层是 LangChain / LangGraph)。

backend/
├── config/                      # Django 工程 (settings, celery, urls, wsgi/asgi)
└── studio/                      # 主 app,挂在 /api/v1/canvas/
    ├── models.py                # Scene, ChatMessage, ImageEditJob/Result, VideoJob,
    │                            #   AngleJob/Result, DataFolder/DataAsset,
    │                            #   ImageProvider/ImageModel (通道), Skill
    ├── views.py  serializers.py  urls.py
    ├── tasks.py                 # Celery: canvas.image_edit_job / image_edit_cutout_job
    │                            #   / video_job / angle_job / cutout_llm_step
    ├── tests/                    # 预设与端点的契约、curl 导入、比例、通道诊断、
    │                             #   聊天协议、请求模板
    └── services/
        ├── image.py video.py                 # **只**建 job —— 真正调供应商的在
        │                                     #   agent/tools/ 里, 见下
        ├── angle.py                          # 建 job + 调 fal.ai
        ├── image_client.py                   # OpenAI 兼容图像客户端, 由一个 ImageChannel
        │                                     #   构造 (库是唯一配置来源)
        ├── image_channels.py                 # 库里那两级行 → 每个调用点消费的那一个
        │                                     #   ImageChannel; 通道类型规则、预设、表单
        ├── template_client.py                # 真正跑一条用户写的请求模板: 发送、轮询、
        │                                     #   从回包里把结果挖出来
        ├── request_template.py               # 模板格式本身 (占位符放哪儿)
        ├── curl_import.py                    # 供应商的示例 curl → 那份模板
        ├── channel_health.py                 # 每次真实往返之后写那个状态点
        ├── channel_diagnosis.py              # 供应商报错 → 「这属于哪类问题」
        ├── attachments.py scenes.py billing.py (空操作) http_retry.py listings_utils.py
        └── agent/
            ├── builder.py        # create_deep_agent (model, tools, skills, memory, store)
            ├── skills.py  context.py
            ├── skill_md.py       # 解析 + 准入检查上传的 SKILL.md
            ├── tools/            # **不只是 agent 的工具** —— 全产品每一个图/视频 job
                                  #   都在这儿真正执行, 包括工具栏发起的
                                  #   (common.py, image.py, video.py)
            └── skills/           # 只是出厂种子 —— 迁移 0018 把它导进库, 运行时以库为准
                                  #   (改这些文件不生效)

异步 job 流水线

一个生成请求会在事务里建 QUEUED job、提交后入 Celery 队列(返回 202 + {job_id, status} —— 拆分返回两个,两条 leg 各一个)。任务跑在专用队列上:

队列(worker)任务
canvasworker_canvasgeventimage_edit_jobvideo_jobangle_jobcutout_llm_step
canvas_cpuworker_canvas_cpupreforkimage_edit_cutout_job(rembg alpha,CPU 密集)
excalidrawworkerprefork默认队列

抠图/拆分是两段链:第一段(LLM,跑 canvas)出白底图,第二段(rembg,跑 canvas_cpu)把白底转透明 alpha。前端轮询 job 端点(或 /active-jobs/),就绪后落到画布。聊天 agent 调用的图像/视频工具建的是同样的 job —— agent 返回一句"已入队",不阻塞等渲染。

FAQ

  • 查日志(某个 job 失败时)—— 要带上三个 worker:

    docker compose logs -f backend worker worker_canvas worker_canvas_cpu
    
  • 图像结果不对或报错 —— 在侧栏「通道配置」里核对 base URL、key 和模型名,用 ⚡ 测一下。视频通道也在同一个面板里配,但没有 ⚡(见第 4 步):对它来说,画布上第一条真片子就是那次测试。

  • 视频好像卡住了 —— 多半没有。视频供应商本来就要几分钟;模板通道的轮询预算给到了约 50 分钟,那个数是照 APIMart 一次真实出片实测定的。整个等待期间画布上都留着那个占位框,刷新页面也还在。

  • 某次生成栽在源图上 —— 它会直说:job 翻成 FAILED,带着供应商自己的报错原文,画布会把它显示在占位卡片上。这不是 PUBLIC_MEDIA_BASE 的问题 —— Canvex 要么把源图内联成 base64,要么主动推给供应商,从不指望谁来访问你的机器。唯一还需要「公网可达」的,是你自己粘进来的外部 URL。图生视频那一类:有些视频供应商既不收 base64、又要求图片地址公网可达,对这种,通道上的 upload_path 指向供应商自己的上传端点,Canvex 在生成之前先把字节推过去。API Mart 的视频预设自带这一项;手写的视频通道要自己填。

  • 前端请求被 CORS 拦 —— 保持 CORS_ALLOW_ALL_ORIGINS=true(默认),或把你的来源加进 CORS_ALLOWED_ORIGINS

Contributors

Orieileen

277 commits

Orieileen/Canvex

专为电商卖家和美工打造的无限画布 AI 工具。生图、改图、抠图、换视角、做视频、AI Skills / An infinite canvas AI tool built for e-commerce sellers and designers — generate, edit, cut out, change perspectives, create videos, and use AI Skills.

TypeScript

24

277 commits

updated Sep 14, 2026

See the code

README

Canvex

Canvex 是一个具有对话、skills、生成和编辑图像和视频能力的无限画布 LLM Agent。这是一款专门为电商卖家/美工做的工具。通过场景管理,可以将多个画布用于不同的项目。

Frontend Backend Database Queue Agent

语言:English

功能

  • 聊天即创作 —— 在画布底部聊天框输入提示词,llm agent 生成一张或多张图片(或一段视频)并落到画布上。chat框(聊天记录)本身就是画布上的一个框,能像别的元素一样拖动、缩放、滚动。
  • 任意图片上的 AI 工具栏 —— 选中图片即弹出基于excalidraw原生选框和箭头工作的浮动工具栏:
    • 编辑 —— 用提示词改风格/改内容。
    • 抠图 —— 一键去背景抠出主体。
    • 拆分 —— 从一张图产出上下两张:抠出主体图 + 去掉主体的干净背景图。
    • 换视角 —— 通过改变摄像头位置,从新机位重新渲染不同角度的图片(fal.ai LoRA)。
    • 视频 —— 把静图变成一段动画。时长、比例、画质三个旋钮全部按你选的模型来
    • 样机 —— 借深度把一张设计图贴到另一张图上,带 深度 / 蒙版 / 不透明度 控制。
    • 合并 / 调整 / 下载 / 发到聊天 —— 本地拍平选区、Lightroom 风格调色面板、导出画布、或把图作为LLM Agent参考附件。
  • 多图合成 —— 一次框选最多 8 张图,「图像」页签会变成「合成 N 张图…」,这些图会一起发给api供应商。其余工具是单图操作,多选时置灰。
  • 框 & 箭头标注 —— 精细化编辑图片:在图上画框/箭头/文字来指向要改的区域。
  • Skills(技能) —— agent 会自己判断该不该用skill(如Canvex预置的 image-prompt-sop 把模糊需求改写成高质量单图提示词、amazon-listing-pack-sop 一键生成协调的 7 张亚马逊套图)。在侧栏「技能库」可以装自己的skill:拖一个 SKILL.md 进去(或者直接在浏览器里写),下一条消息 agent 就能使用,不需要重启应用
  • 场景 —— 侧栏里多个独立画布:新建、重命名、删除、快速切换;编辑自动保存。置顶是浏览器本地的偏好(localStorage),不跨设备同步。
  • 素材库 —— 保存你生成过的所有图片/视频,按画布分组;点缩略图即可重新插回当前画布。

架构概览

flowchart LR
  subgraph FE["前端 — React + Excalidraw"]
    Chat["聊天框"]
    Bar["AI 工具栏"]
  end
  subgraph BE["后端 — Django + DRF"]
    Agent["deepagents agent<br/>(skills + tools)"]
    API["job 端点"]
  end
  Q[["Celery 队列<br/>canvas · canvas_cpu"]]
  Prov["图像 / 视频 / fal.ai 供应商"]

  Chat -->|"POST /chat/ (SSE)"| Agent
  Agent -->|"generate_image · generate_video"| Q
  Bar -->|"编辑 · 抠图 · 拆分 · 换视角 · 视频"| API --> Q
  Q --> Prov --> Q
  Q -->|"轮询 job → 落到画布"| FE
  • 聊天(llm agent)是 deepagentscreate_deep_agent),带两个工具(generate_imagegenerate_video)、一份按场景隔离的 memory 文件、以及按需展开的 SKILL.md 技能。每轮对话历史从数据库回放(不需要独立的记忆存储)。
  • 每次生成都是异步 job:API 建一条 QUEUED 记录、提交后入 Celery 队列;前端轮询 job 直到结果就绪再落到画布。抠图是两段链(LLM 出白底 → CPU rembg 出 alpha)。

部署

1)克隆

git clone https://github.com/Orieileen/Canvex.git
cd Canvex

2)配置

cp .env.example .env

cp .env.example .env后默认值直接可用,不需要更改.env里的任何一行这里不填任何 API key —— api key在应用内配置(第 4 步)。.env 只管基础设施:端口、数据库,以及 PUBLIC_MEDIA_BASE(见下表)。

3)启动(Docker)

前置:Docker + Docker Compose。

docker compose up -d --build

4)在 Canvex 添加通道

由于各家供应商的请求参数各不相同,Canvex 预设了一套 API 供应商格式(不是广告):

  1. 聊天(LLM agent)模型 —— Canvex 预设了 LLM agent 的第三方供应商 兔子 tu-zi:在 tu-zi 注册后拿到 API key,填进 Canvex 的通道配置即可用。必须是支持 OpenAI 风格 tool calling 的 key —— 不支持的填进来,聊天框会回一段文字然后画布上什么都不发生。
  2. 图片生成 · 自定义模板 —— Canvex 预设了生成图片的第三方供应商 API Mart:在 apimart 注册后拿到 API key,填进配置即可用。编辑图片、拆分图片、以及 LLM agent 调用的生图 tool,都用这一把 key。
  3. 视角重渲染 —— 先去 fal.ai 注册拿到 API key,用于「换视角」(改变机位)功能。用的模型是 fal-ai/qwen-image-edit-2511-multiple-angles
  4. 视频生成 · 自定义模板 —— Canvex 预设了生成视频的第三方供应商 API Mart:同样是注册拿到 API key 填进配置即可用。

apikey使用步骤:

快捷配置 —— 打开 http://localhost:5173,点左侧栏的「通道配置」。在「快捷配置」中填入对应供应商的key即可。下面列出Canvex可用预设:

角色预设
聊天兔子 tu-zi、OpenAI、Google、DeepSeek、智谱 GLM
生图API Mart、OpenAI、Google
视频API Mart
换视角fal.ai

以下有三种方法可以使用:

1.聊天/llm agent用兔子、生图和视频用 API Mart,变换视角使用fal.ai是这个项目经过测试得出跑的最稳最便宜的供应商 —— 这里每个功能都是对着它们做出来、验过来的,所以想最快跑通就配这两家。其余几条聊天/llm agent官方供应商通道也可以使用,不过openai等国外供应商一方面比较贵,一方面api并不好买,除了tu-zi也可以使用国内llm厂商,而图片apimart则是便宜+稳定。换视角只有一个选项,因为视角 LoRA 只在 fal.ai 上。

2.从一段 curl 开始配置供应商 —— 如果要使用不在Canvex列表的供应商走这条:把你需要的api供应商文档里的示例 curl 粘进去,Canvex向导会替你自动把通道拼出来,不用写 JSON。(这个功能可能不适配所有供应商)

3.新建自定义供应商配置 —— 完全自己写请求模板。你需要知道供应商所有的请求参数以及值怎么写,并手动拼成json填入自定义供应商

模型行右边的 ⚡ 会真发一次最小生成。它只出现在生图和换视角通道上:聊天通道要验的是它认不认 tools 参数,直接在聊天框里说「生成一张图」最快;视频则太慢,撑不过一次同步测试 —— 对视频来说,画布上第一条真片子就是那次测试。

每条通道名字左边有一个状态点:绿 = 上次调用通了,橙 = 上次失败,空心 = 还没调用过。真实生成也会更新它,不只是 ⚡ —— 一条通道哪天悄悄坏了(key 过期、额度打光、供应商换端点),在这里一眼能看见,而不是变成又一次莫名其妙的生成失败。展开失败的卡片,能看到供应商返回的原文,上面还有一句说这属于哪类问题、该改哪儿:key 过期、余额打光、模型名供应商不认、以及人家自己挂了,在原始报错里长得都差不多,而其中只有一部分是靠改配置能解决的。

每个模型行下面还能挂自己的覆盖项 —— 一条通道底下挂着四十个在时长、比例、键名上互相不一致的模型,而不是建四十条通道,靠的就是它。如果你是手写通道:这些旋钮填错大多不会报错,只是静默失效 —— 比例发到一个供应商不读的键上不是错误,只是那个设置永远不起作用。

环境变量

最少需要这些就能跑起来(完整列表 + 调优旋钮见 .env.example)(Canvex已经配置好了环境变量,所以以下只做展示说明,不需要任何更改):

变量必填说明
PUBLIC_MEDIA_BASE你的浏览器访问这个后端的地址(默认 http://localhost:28000)。只用在「发到聊天」附件那几个绝对 URL 上;从别的机器/域名打开这个应用时才需要改。供应商永远不会来拉它 —— 源图要么内联成 base64、要么由我们主动推给供应商,所以自托管不需要任何隧道、CDN 或公网地址。
CANVAS_AGENT_STORE_BACKENDmemory(默认,进程内)或 postgres(持久化 agent 记忆;同时要设 CANVAS_AGENT_STORE_DSN,并装上 langgraph-checkpoint-postgres)。
POSTGRES_DB / _USER / _PASSWORD默认都是 canvex
BACKEND_PORT / FRONTEND_PORT宿主端口,默认 28000 / 5173
VITE_API_URL前端调用的后端地址。Docker Compose 会传 http://localhost:28000;不走 Compose 直接跑 dev server 又不设它的话,代码回落到 :8000,连不上。

API

所有路由在 /api/v1/canvas/ 下。

用途端点
场景(CRUD)GET/POST /scenes/GET/PATCH/DELETE /scenes/{id}/
聊天(SSE 流)POST /scenes/{id}/chat/
图像编辑 / 生成POST /scenes/{id}/image-edit/GET /image-edit-jobs/{job_id}/
拆分(主体 + 背景)POST /scenes/{id}/split/ → 返回两个 job,都在 /image-edit-jobs/{job_id}/ 轮询
视频POST /scenes/{id}/video/GET /video-jobs/{job_id}/
换视角(fal.ai)POST /scenes/{id}/angle/GET /angle-jobs/{job_id}/
进行中的 job(恢复轮询)GET /scenes/{id}/active-jobs/
每个场景的 job 历史GET /scenes/{id}/image-edit-jobs//video-jobs//angle-jobs/
发到聊天的上传POST /scenes/{id}/upload-attachment/
素材库GET /media-library/folders/GET /media-library/folders/{scene_id}/items/
Agent 当前看得见的技能GET /skills/
装 / 卸技能GET / POST /skill-library/PATCH / DELETE /skill-library/{id}/
通道(CRUD + 嵌套的模型)GET/POST /image-providers/GET/PATCH/PUT/DELETE /image-providers/{id}/
⚡ 测一条通道POST /image-providers/{id}/test/ —— 失败也返 200,带原始报文 + 诊断码
表单字段表 + 一键预设GET /image-providers/schema/
curl 向导POST /image-providers/wizard/parse/(只解析不发送)、POST /image-providers/wizard/probe/(拿还没保存的通道真发一次生成)
工具栏选择器读的模型列表GET /image-models/(回包里不含 base URL / key)

聊天端点走 SSEtext/event-stream,每个事件的帧格式是 data: <json>\n\n)。事件类型:user_createdassistant_delta(逐 token 的文字流,实际量最大的就是它)、tool_calltool_resultcanvas_asset{url},agent 本轮产出、客户端要落到画布的图)、assistant_finalassistanterrordone

后端

技术栈:Django + DRF + Celery + Redis + PostgreSQL + deepagents(底层是 LangChain / LangGraph)。

backend/
├── config/                      # Django 工程 (settings, celery, urls, wsgi/asgi)
└── studio/                      # 主 app,挂在 /api/v1/canvas/
    ├── models.py                # Scene, ChatMessage, ImageEditJob/Result, VideoJob,
    │                            #   AngleJob/Result, DataFolder/DataAsset,
    │                            #   ImageProvider/ImageModel (通道), Skill
    ├── views.py  serializers.py  urls.py
    ├── tasks.py                 # Celery: canvas.image_edit_job / image_edit_cutout_job
    │                            #   / video_job / angle_job / cutout_llm_step
    ├── tests/                    # 预设与端点的契约、curl 导入、比例、通道诊断、
    │                             #   聊天协议、请求模板
    └── services/
        ├── image.py video.py                 # **只**建 job —— 真正调供应商的在
        │                                     #   agent/tools/ 里, 见下
        ├── angle.py                          # 建 job + 调 fal.ai
        ├── image_client.py                   # OpenAI 兼容图像客户端, 由一个 ImageChannel
        │                                     #   构造 (库是唯一配置来源)
        ├── image_channels.py                 # 库里那两级行 → 每个调用点消费的那一个
        │                                     #   ImageChannel; 通道类型规则、预设、表单
        ├── template_client.py                # 真正跑一条用户写的请求模板: 发送、轮询、
        │                                     #   从回包里把结果挖出来
        ├── request_template.py               # 模板格式本身 (占位符放哪儿)
        ├── curl_import.py                    # 供应商的示例 curl → 那份模板
        ├── channel_health.py                 # 每次真实往返之后写那个状态点
        ├── channel_diagnosis.py              # 供应商报错 → 「这属于哪类问题」
        ├── attachments.py scenes.py billing.py (空操作) http_retry.py listings_utils.py
        └── agent/
            ├── builder.py        # create_deep_agent (model, tools, skills, memory, store)
            ├── skills.py  context.py
            ├── skill_md.py       # 解析 + 准入检查上传的 SKILL.md
            ├── tools/            # **不只是 agent 的工具** —— 全产品每一个图/视频 job
                                  #   都在这儿真正执行, 包括工具栏发起的
                                  #   (common.py, image.py, video.py)
            └── skills/           # 只是出厂种子 —— 迁移 0018 把它导进库, 运行时以库为准
                                  #   (改这些文件不生效)

异步 job 流水线

一个生成请求会在事务里建 QUEUED job、提交后入 Celery 队列(返回 202 + {job_id, status} —— 拆分返回两个,两条 leg 各一个)。任务跑在专用队列上:

队列(worker)任务
canvasworker_canvasgeventimage_edit_jobvideo_jobangle_jobcutout_llm_step
canvas_cpuworker_canvas_cpupreforkimage_edit_cutout_job(rembg alpha,CPU 密集)
excalidrawworkerprefork默认队列

抠图/拆分是两段链:第一段(LLM,跑 canvas)出白底图,第二段(rembg,跑 canvas_cpu)把白底转透明 alpha。前端轮询 job 端点(或 /active-jobs/),就绪后落到画布。聊天 agent 调用的图像/视频工具建的是同样的 job —— agent 返回一句"已入队",不阻塞等渲染。

FAQ

  • 查日志(某个 job 失败时)—— 要带上三个 worker:

    docker compose logs -f backend worker worker_canvas worker_canvas_cpu
    
  • 图像结果不对或报错 —— 在侧栏「通道配置」里核对 base URL、key 和模型名,用 ⚡ 测一下。视频通道也在同一个面板里配,但没有 ⚡(见第 4 步):对它来说,画布上第一条真片子就是那次测试。

  • 视频好像卡住了 —— 多半没有。视频供应商本来就要几分钟;模板通道的轮询预算给到了约 50 分钟,那个数是照 APIMart 一次真实出片实测定的。整个等待期间画布上都留着那个占位框,刷新页面也还在。

  • 某次生成栽在源图上 —— 它会直说:job 翻成 FAILED,带着供应商自己的报错原文,画布会把它显示在占位卡片上。这不是 PUBLIC_MEDIA_BASE 的问题 —— Canvex 要么把源图内联成 base64,要么主动推给供应商,从不指望谁来访问你的机器。唯一还需要「公网可达」的,是你自己粘进来的外部 URL。图生视频那一类:有些视频供应商既不收 base64、又要求图片地址公网可达,对这种,通道上的 upload_path 指向供应商自己的上传端点,Canvex 在生成之前先把字节推过去。API Mart 的视频预设自带这一项;手写的视频通道要自己填。

  • 前端请求被 CORS 拦 —— 保持 CORS_ALLOW_ALL_ORIGINS=true(默认),或把你的来源加进 CORS_ALLOWED_ORIGINS

Contributors

Orieileen

277 commits

Languages

TypeScript

55.9%

Python

42.6%