新增 Cloudflare Canvas Service
- `canvas-service-worker/` 用 Hono + Hyperdrive。
- `POST /canvas/insert-media-batch` 负责插入媒体。
- 输出格式保持旧 `canvases_v2.data` 兼容。
- `canvas_insertion_jobs` 记录排序、重试和审计。
这次验收不是查询工具。完整跑法已经重跑一条新实例:Ignis 先生成 `EOvrHs.png` 和 `MinIPa.mp4`;Mai Agent 用 `video-edit` 发自然语言任务;Tobatsu 把封面和视频拼成 `TAxB0r.mp4`;Mai 收回 artifact,再通过 Canvas Service 写进同一张画布。
这次改动把 canvas 写入、Tobatsu 异步任务、Agent 技能入口拆成清楚的几层。 test 验收覆盖的是这些层一起跑,不是单点 mock。
这条主线只讲一个实例,而且在同一个 Ignis session / canvas 里完成。 先生成图片和视频,再发 `video-edit`,最后把 Tobatsu 返回的 mp4 写回同一张画布。
turn_d6abb6fa-495f-4dcb-9749-fe6ff5b469d5session_1cb401e8-7836-4a71-a1c0-677a2ae2f3a56a9f6da5-dea3-4f48-98d6-57d3ee81dee8generate_imageEOvrHs.png2048×1152 · image/pngcjob_5730135a-e0dd-4685-8dff-6cbe0e01ae33tool calls: in_context_update_preference generate_image CDN: https://cdn-asia.funplus-marketing.ai/ultra/EOvrHs.png
turn_394ab5cb-5101-4129-8666-164e0e8eab90session_1cb401e8-7836-4a71-a1c0-677a2ae2f3a56a9f6da5-dea3-4f48-98d6-57d3ee81dee8create_videosMinIPa.mp41280×720 · video/mp4cjob_0ea76681-e809-4888-9130-7a9b5711382aCDN: https://cdn-asia.funplus-marketing.ai/ultra/MinIPa.mp4
{
"action": "new_task",
"task_type": "video_edit",
"payload": {
"instruction": "用 EOvrHs.png 做 2 秒开场封面,然后接上 MinIPa.mp4,输出一个 mp4。"
}
}
turn_f01b7bc4-fe6e-40a2-8c6a-13e7502813bfgw_aac81389-205a-45f0-8a79-074ea9ec13a8gwr_0678e6b5-1cf4-4e82-b71c-bb0a067015ba55ba4485-f185-4ddb-8e3e-76060ee41d8f8ae0b2e4mai.delivery_result · HTTP 2005d349316-f970-423d-92e5-9596b5a74a5d21fd2825-cc08-464d-b34a-60df2503e70cTAxB0r.mp41280×720 · 7.062s · 835046 bytesRendi primary · no local fallbacksummary: 已用 EOvrHs.png 生成 2 秒静音开场封面, 并接上 MinIPa.mp4 输出 1 个 mp4。
turn_878fac5d-0276-4a4a-ab21-c98b8cc3d337session_1cb401e8-7836-4a71-a1c0-677a2ae2f3a56a9f6da5-dea3-4f48-98d6-57d3ee81dee8skill_toolcall_curl get_taskTAxB0r.mp4cjob_68046521-19c0-4ad2-bc8d-37d7d74f0cc5get_task 不是主业务动作。 它把已完成的 Gateway 结果带回 Agent, 再由 batch_result 写进当前 canvas。
33 succeeded / attempts=1 eachEOvrHs.png · MinIPa.mp4 · TAxB0r.mp4video · 1280×720 · 7.062stobatsu_gateway{
"canvas_id": "6a9f6da5-dea3-4f48-98d6-57d3ee81dee8",
"last_session_id": "session_1cb401e8-7836-4a71-a1c0-677a2ae2f3a5",
"files": ["EOvrHs.png", "MinIPa.mp4", "TAxB0r.mp4"],
"element_file_ids": ["EOvrHs.png", "MinIPa.mp4", "TAxB0r.mp4"]
}
TAxB0r.mp42 秒封面开场拼接视频1280×7207.062s200 · video/mp4 · 835046 bytes{
"file_id": "TAxB0r.mp4",
"media_type": "video",
"mime_type": "video/mp4",
"width": 1280,
"height": 720,
"duration_ms": 7062,
"dataURL": "https://cdn-asia.funplus-marketing.ai/ultra/TAxB0r.mp4"
}
用户要求的顺序已经按 test 环境走完:先生成素材,再调视频编辑,再把返回结果塞进 canvas。
同一 session 生成 `EOvrHs.png` 和 `MinIPa.mp4`。
自然语言 instruction 里带 `file_id`,不传 refs 或 URL。
服务端补 callback envelope、token、canvas/session context。
实际视频处理在 Tobatsu workflow,Rendi primary path 成功。
top-level `task_id`、summary、outputs、media metadata 都到位。
最终产物是 `TAxB0r.mp4`,URL 在 Mai `ultra/` CDN。
这里不是发起视频编辑,只是读取 Gateway 已保存的业务结果。
同一 session 的 `get_task` 把已完成结果带回 `StreamingExecutor`。
第 3 个 `canvas_insertion_jobs` 成功,canvas revision 变成 3。
这是覆盖面的补充 case,证明多视频拼接可用。它不是上面那条写入 canvas 的主线,所以不拿它回答 session/canvas。
6Cmrut.mp48AGTDG.mp4ZEU2xg.mp4这 3 个 file_id 都由 Ignis 在 E0 里生成。
{
"action": "new_task",
"task_type": "video_edit",
"payload": {
"instruction": "把 6Cmrut.mp4、8AGTDG.mp4、ZEU2xg.mp4 按这个顺序拼接成一个 mp4。"
}
}
mai-test-concat-3-videos-v3-20260524-01gw_769fb6eb-bf6a-43b2-ae4a-575731caff64gwr_fd2fd025-becc-4494-bad7-0831b2e68e8f59735841-cb30-40e4-857c-081a521c6765ocUxEH.mp4720×1280 · 15.186s这个 matrix case 没有 session_id / canvas_id。 它只证明三视频拼接和 metadata 回传。 主线 canvas 证明请看 TAxB0r.mp4: session_1cb401e8-7836-4a71-a1c0-677a2ae2f3a5 canvas 6a9f6da5-dea3-4f48-98d6-57d3ee81dee8
下面列的是验收动作和原始证据摘要。token 和 secret 没有展示。
https://mai-2-tmp-1-n370.onrender.comsession_1cb401e8-7836-4a71-a1c0-677a2ae2f3a56a9f6da5-dea3-4f48-98d6-57d3ee81dee8IGNIS_BASE_URL=https://mai-2-tmp-1-n370.onrender.com \ IGNIS_CLI_TOKEN=<redacted> \ ignis ask --new \ --prompt-file /tmp/mai-r3-step1-image-prompt.txt \ --timeout-ms 300000 \ --json ignis ask --session session_1cb401e8... \ --prompt-file /tmp/mai-r3-step2-video-prompt.txt ignis ask --session session_1cb401e8... \ --prompt-file /tmp/mai-r3-step3-video-edit-new-prompt.txt ignis ask --session session_1cb401e8... \ --prompt-file /tmp/mai-r3-step4-get-task-prompt.txt
{
"ok": true,
"status": "completed",
"turn_id": "turn_878fac5d-0276-4a4a-ab21-c98b8cc3d337",
"session_id": "session_1cb401e8-7836-4a71-a1c0-677a2ae2f3a5",
"canvas_id": "6a9f6da5-dea3-4f48-98d6-57d3ee81dee8",
"contains": "TAxB0r.mp4"
}
cjob_68046521-19c0-4ad2-bc8d-37d7d74f0cc513TAxB0r.mp4type=video · 1280×720 · duration=7.062{
"job_count": 3,
"jobs": ["generate_image", "create_videos", "tobatsu_gateway"],
"canvas_revision": 3,
"files": ["EOvrHs.png", "MinIPa.mp4", "TAxB0r.mp4"],
"dataURL": "https://cdn-asia.funplus-marketing.ai/ultra/TAxB0r.mp4"
}
pytest ...test_canvas_service_client.py \
...test_streaming_executor_canvas_source.py
→ 5 passed
cd canvas-service-worker
npm test -- --run
→ 7 passed
cd tools-backend
npm run test -- tobatsu-gateway-*.test.ts
→ 8 passed
cd tobatsu-gateway
npm test -- --run
→ 3 passed
E0 输入素材都由 Ignis 生成。矩阵不是复用手写静态 file_id。
| 素材 | file_id | 类型 | 用途 | 验收状态 |
|---|---|---|---|---|
| V_A | 8AGTDG.mp4 | video | 基础视频 | generated |
| V_B | 6Cmrut.mp4 | video | 拼接视频 | generated |
| V_C | ZEU2xg.mp4 | video | 第三段视频 | generated |
| I_COVER | PI8t8U.png | image | 封面图 | generated |
| I_SLIDE | KCOhw2.png | image | slideshow | generated |
| A_MUSIC | iSW3Fx.wav | audio | 配乐 / 替换音轨 | generated |
CC 和 Tobatsu 都有独立读回。下面是对话里的关键验收点,不是我单方总结。
Slice 1 review PASS。 Vitest 4/4,和 Python `canvas_media_manager` 的 12 条格式不变量对齐。
Slice 2 fixes PASS。 IDOR 已关;failed-continue active_run rollback 修好;4 套测试 16/16。
r3 same-session PASS。 task `55ba4485...` done,run `8ae0b2e4` success,输出 `EOvrHs-cover-plus-MinIPa.mp4`,business callback HTTP 200。
r3 Canvas readback PASS。 同一 canvas revision=3,`files/elements` 都包含 `EOvrHs.png`、`MinIPa.mp4`、`TAxB0r.mp4`。
Continue PASS。 child task `8df509cd...` done,workflow v2,callback HTTP 200,Rendi primary 720p output。
failure smoke PASS。 缺失 file_id 返回 `status=failed`、`error.code=input_unavailable`、`outputs=[]`,callback HTTP 200。
Final matrix PASS。 11/11 任务、run、result、event、artifact 对齐;v3 元数据重跑补齐尺寸和时长。
Test Canvas gate CLOSED。 旧 smoke 证明环境已通;新 r3 实例补齐了同 session 生成、编辑、回写的完整流程。
这些都是本轮验收留下的本机证据。可以直接打开看原始 JSON 或 review verdict。
| 文件 | 内容 | 结论 |
|---|---|---|
/tmp/mai-r3-same-session-e2e-report.md | 同 session 完整实例报告 | primary |
/tmp/mai-r3-step1-image-turn.json | 生成 `EOvrHs.png` 的 Ignis turn | completed |
/tmp/mai-r3-step2-video-turn.json | 生成 `MinIPa.mp4` 的 Ignis turn | completed |
/tmp/mai-r3-step3-video-edit-new-turn.json | 发起 `video-edit` New task | accepted |
/tmp/mai-r3-step4-get-task-turn.json | 同 session 拉回 `TAxB0r.mp4` | completed |
/tmp/mai-r3-canvas-jobs.json | 3 个 canvas job 顺序读回 | succeeded |
/tmp/mai-r3-canvas-full.json | canvas revision=3 + files/elements | succeeded |
/tmp/mai-v106-test-completion-report.md | test-only 完成报告 | source report |
/tmp/mai-cc-review-test-canvas-env-rerun-verdict.md | CC 复核 test canvas 原始 JSON | gate closed |
/tmp/mai-test-ignis-get-task-after-env-turn.json | Ignis CLI turn 输出 | completed |
/tmp/mai-test-ignis-get-task-after-env-canvas-summary-parsed.json | Canvas job + canvas data 摘要 | succeeded |
/tmp/mai-cc-review-v106-final-matrix-verify.md | 11-case 视频编辑矩阵 review | pass |
/tmp/mai-cc-review-failure-smoke-v106-verify.md | 失败路径 review | pass |
/tmp/mai-prod-cut-audit-plan.md | prod cut 预检计划 | paused |
本报告只声明 test 通过。prod 方向已按用户要求暂停。