@mokuyoaxis/dsh-iris 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/README.en.md +245 -0
  3. package/README.md +55 -6
  4. package/cordis.patch.yml +1 -1
  5. package/docs/ROADMAP.md +22 -5
  6. package/docs/assets/diagrams/iris-architecture.drawio +1 -0
  7. package/docs/assets/diagrams/iris-architecture.png +0 -0
  8. package/docs/assets/diagrams/iris-architecture.svg +63 -0
  9. package/docs/assets/diagrams/iris-task-lifecycle.drawio +1 -0
  10. package/docs/assets/diagrams/iris-task-lifecycle.png +0 -0
  11. package/docs/assets/diagrams/iris-task-lifecycle.svg +64 -0
  12. package/docs/assets/diagrams/iris-workflow-compose.drawio +1 -0
  13. package/docs/assets/diagrams/iris-workflow-compose.png +0 -0
  14. package/docs/assets/diagrams/iris-workflow-compose.svg +41 -0
  15. package/docs/assets/examples/iris-flower-generated.webp +0 -0
  16. package/docs/assets/screenshots/image-task-succeeded.webp +0 -0
  17. package/docs/assets/screenshots/prompt-general-after.webp +0 -0
  18. package/docs/assets/screenshots/prompt-general-before.webp +0 -0
  19. package/docs/assets/screenshots/prompt-general-preview.webp +0 -0
  20. package/docs/assets/screenshots/prompt-image-before.webp +0 -0
  21. package/docs/assets/screenshots/prompt-image-preview.webp +0 -0
  22. package/docs/releases/0.1.2.md +26 -0
  23. package/docs/screenshots.md +34 -0
  24. package/lib/api.js +42 -11
  25. package/lib/bundled-skills.js +62 -0
  26. package/lib/client.js +352 -9
  27. package/lib/index.js +11 -1
  28. package/lib/prompt-optimizer-config.js +170 -0
  29. package/lib/prompt-optimizer.js +244 -0
  30. package/package.json +7 -3
  31. package/screenshots.json +6 -0
  32. package/user_guide.md +64 -3
@@ -0,0 +1,63 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1180" height="800" viewBox="0 0 1180 800" role="img" aria-label="Iris 架构总览(DSH 插件形态)">
2
+ <defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#6b7686"/></marker></defs>
3
+ <rect width="1180" height="800" fill="#ffffff"/>
4
+ <text x="48" y="52" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="21" font-weight="700" fill="#1f2328">Iris 架构总览(DSH 插件形态)</text>
5
+ <text x="48" y="78" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="13" fill="#5b6472">三个入口共用同一套动作实现;凭据与数据只留在宿主侧,插件不额外监听端口</text>
6
+ <rect x="36" y="96" width="1108" height="560" rx="10" fill="none" stroke="#b9c2ce" stroke-width="1.4"/>
7
+ <text x="50" y="120" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12.5" fill="#5b6472">DeepSeek Harness 宿主进程 · Iris 插件(复用宿主工具 / 路由 / 生命周期服务)</text>
8
+ <path d="M216,210 L216,239 L308,239 L308,268" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
9
+ <path d="M590,210 L590,239 L470,239 L470,268" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
10
+ <path d="M848,210 L848,239 L740,239 L740,268" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
11
+ <path d="M362,342 L362,369 L281,369 L281,396" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
12
+ <rect x="321" y="346" width="82" height="19" rx="4" fill="#ffffff"/>
13
+ <text x="362" y="359.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">选择候选模型</text>
14
+ <path d="M578,342 L578,369 L765,369 L765,396" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
15
+ <path d="M496,433 L550,433" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
16
+ <path d="M524,342 L524,480 L236,480 L236,524" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
17
+ <rect x="353" y="488.5" width="58" height="19" rx="4" fill="#ffffff"/>
18
+ <text x="382" y="502" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">创建任务</text>
19
+ <path d="M406,561 L430,561" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
20
+ <path d="M236,598 L236,640 L935,640 L935,598" fill="none" stroke="#6b7686" stroke-width="1.6" stroke-dasharray="5 4" marker-end="url(#arr)"/>
21
+ <rect x="351" y="620.5" width="58" height="19" rx="4" fill="#ffffff"/>
22
+ <text x="380" y="634" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">状态事件</text>
23
+ <path d="M935,210 L935,268" fill="none" stroke="#6b7686" stroke-width="1.6" stroke-dasharray="5 4" marker-end="url(#arr)"/>
24
+ <path d="M765,470 L765,676 L860,676 L860,706" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
25
+ <rect x="790.2" y="666.5" width="43.6" height="19" rx="4" fill="#ffffff"/>
26
+ <text x="812" y="680" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">HTTPS</text>
27
+ <rect x="66" y="138" width="300" height="72" rx="9" fill="#f5f6f8" stroke="#b9c2ce" stroke-width="1.6"/>
28
+ <text x="216" y="170" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">Agent 工具(14 个)</text>
29
+ <text x="216" y="190.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">iris_draw_image · iris_generate_video · …</text>
30
+ <rect x="440" y="138" width="300" height="72" rx="9" fill="#f5f6f8" stroke="#b9c2ce" stroke-width="1.6"/>
31
+ <text x="590" y="170" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">GUI 动作卡</text>
32
+ <text x="590" y="190.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">会话内的 Iris 操作入口</text>
33
+ <rect x="790" y="138" width="290" height="72" rx="9" fill="#f5f6f8" stroke="#b9c2ce" stroke-width="1.6"/>
34
+ <text x="935" y="170" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">Iris 工作台(三座位)</text>
35
+ <text x="935" y="190.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">设置 · 进度 · 悬浮泡泡</text>
36
+ <rect x="200" y="268" width="540" height="74" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
37
+ <text x="470" y="301" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">统一动作层 · lib/actions.js</text>
38
+ <text x="470" y="321.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">参数校验 · 输入解析(上传 / 会话附件 / 宿主路径)· failover 编排</text>
39
+ <rect x="66" y="396" width="430" height="74" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
40
+ <text x="281" y="429" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">模型池 · lib/models.js + capability.js</text>
41
+ <text x="281" y="449.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">providerId::modelId 复合身份 · 能力分配 · 有序候选</text>
42
+ <rect x="550" y="396" width="430" height="74" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
43
+ <text x="765" y="429" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">Provider 适配 · lib/adapters.js</text>
44
+ <text x="765" y="449.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">DashScope 媒体协议 · OpenAI Images 兼容</text>
45
+ <rect x="66" y="524" width="340" height="74" rx="9" fill="#eaf7ef" stroke="#2e9e5b" stroke-width="1.6"/>
46
+ <text x="236" y="557" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">任务系统 · lib/tasks.js</text>
47
+ <text x="236" y="577.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">异步轮询 · 重启恢复 · 取消传播</text>
48
+ <rect x="430" y="524" width="300" height="74" rx="9" fill="#eaf7ef" stroke="#2e9e5b" stroke-width="1.6"/>
49
+ <text x="580" y="557" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">文件与媒体 · lib/media.js</text>
50
+ <text x="580" y="577.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">uploads/ outputs/ · 令牌媒体链接 · Range 流式</text>
51
+ <rect x="790" y="524" width="290" height="74" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
52
+ <text x="935" y="557" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">Web 客户端 · lib/client.js</text>
53
+ <text x="935" y="577.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">三座位 UI · 共享状态订阅 + SSE</text>
54
+ <rect x="790" y="268" width="290" height="74" rx="9" fill="#f5f6f8" stroke="#b9c2ce" stroke-width="1.6"/>
55
+ <text x="935" y="301" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">Skill registry</text>
56
+ <text x="935" y="321.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">随包注册 iris-verify-ui / iris-compose-media</text>
57
+ <rect x="66" y="690" width="470" height="56" rx="8" fill="#ffffff" stroke="#b9c2ce" stroke-width="1.3" stroke-dasharray="5 4"/>
58
+ <text x="301" y="714" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">本地数据 · $DSH_HOME/iris/v1/</text>
59
+ <text x="301" y="734.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">providers.json · tasks.json · uploads/ · outputs/(POSIX 0700 / 0600)</text>
60
+ <rect x="580" y="706" width="560" height="64" rx="9" fill="#f5f6f8" stroke="#b9c2ce" stroke-width="1.6"/>
61
+ <text x="860" y="734" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">媒体 / 视觉服务商</text>
62
+ <text x="860" y="754.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">DashScope 百炼 · OpenAI Images 兼容服务(凭据只发往官方 HTTPS 域名)</text>
63
+ </svg>
@@ -0,0 +1 @@
1
+ <mxfile host="iris-docs"><diagram id="iris-task-lifecycle" name="页 1"><mxGraphModel dx="1000" dy="700" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1240" pageHeight="740" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="start" value="Agent 工具 / GUI 动作卡&lt;br&gt;提交生成请求" style="rounded=1;arcSize=50;whiteSpace=wrap;html=1;fillColor=#f5f6f8;strokeColor=#b9c2ce;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="40" y="300" width="180" height="60" as="geometry"/></mxCell><mxCell id="create" value="创建本地任务&lt;br&gt;status = running" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eef3ff;strokeColor=#4d6bfe;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="268" y="300" width="180" height="60" as="geometry"/></mxCell><mxCell id="attempt" value="提交供应商(Attempt)&lt;br&gt;上传 · 提交 · 同步生成" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff6e5;strokeColor=#c98a1b;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="496" y="298" width="220" height="64" as="geometry"/></mxCell><mxCell id="decide" value="远端受理?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#eef3ff;strokeColor=#4d6bfe;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="764" y="280" width="180" height="100" as="geometry"/></mxCell><mxCell id="accepted" value="已受理&lt;br&gt;remoteTaskId 落盘" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eaf7ef;strokeColor=#2e9e5b;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="996" y="300" width="200" height="60" as="geometry"/></mxCell><mxCell id="failover" value="failover:切换下一候选模型" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fceded;strokeColor=#c94f4f;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="496" y="140" width="220" height="58" as="geometry"/></mxCell><mxCell id="exhausted" value="候选耗尽&lt;br&gt;任务失败" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fceded;strokeColor=#c94f4f;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="240" y="141" width="200" height="56" as="geometry"/></mxCell><mxCell id="watch" value="后台轮询盯守&lt;br&gt;重启后自动接管 running 任务" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eef3ff;strokeColor=#4d6bfe;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="700" y="440" width="240" height="56" as="geometry"/></mxCell><mxCell id="succeeded" value="succeeded&lt;br&gt;产物转存 DSH 附件 + 令牌媒体链接" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eaf7ef;strokeColor=#2e9e5b;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="700" y="560" width="240" height="60" as="geometry"/></mxCell><mxCell id="failed" value="failed&lt;br&gt;保留错误说明" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fceded;strokeColor=#c94f4f;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="460" y="560" width="190" height="60" as="geometry"/></mxCell><mxCell id="canceled" value="canceled&lt;br&gt;取消信号传播" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f6f8;strokeColor=#b9c2ce;fontColor=#1f2328;fontSize=12;" vertex="1" parent="1"><mxGeometry x="230" y="560" width="180" height="60" as="geometry"/></mxCell><mxCell id="note1" value="黄金规则:远端受理后绝不自动重提&lt;br&gt;重提只发生在提交阶段的模型候选切换,避免重复计费" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#b9c2ce;fontColor=#5b6472;fontSize=11;dashed=1;" vertex="1" parent="1"><mxGeometry x="40" y="650" width="560" height="64" as="geometry"/></mxCell><mxCell id="note2" value="启动恢复与取消&lt;br&gt;有 remoteTaskId 的任务继续盯守;孤儿提交清理为 failed;取消传播到本地等待与轮询" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#b9c2ce;fontColor=#5b6472;fontSize=11;dashed=1;" vertex="1" parent="1"><mxGeometry x="640" y="650" width="556" height="64" as="geometry"/></mxCell><mxCell id="c1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=1;exitY=0.5;entryX=0;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="start" target="create"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=1;exitY=0.5;entryX=0;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="create" target="attempt"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=1;exitY=0.5;entryX=0;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="attempt" target="decide"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c4" value="是" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=1;exitY=0.5;entryX=0;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="decide" target="accepted"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c5" value="否" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0.5;exitY=0;entryX=1;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="decide" target="failover"><mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="854" y="169"/><mxPoint x="716" y="169"/></Array></mxGeometry></mxCell><mxCell id="c6" value="重试提交" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0.5;exitY=1;entryX=0.5;entryY=0;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="failover" target="attempt"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0;exitY=0.5;entryX=1;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="failover" target="exhausted"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c8" value="转入盯守" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0.5;exitY=1;entryX=1;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="accepted" target="watch"><mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="1096" y="468"/><mxPoint x="940" y="468"/></Array></mxGeometry></mxCell><mxCell id="c9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0.5;exitY=1;entryX=0.5;entryY=0;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="watch" target="succeeded"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;dashed=1;exitX=0.5;exitY=1;entryX=0.5;entryY=0;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="watch" target="failed"><mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="760" y="534"/><mxPoint x="555" y="534"/></Array></mxGeometry></mxCell><mxCell id="c11" value="轮询耗尽 / 取消 / 错误" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;dashed=1;exitX=0;exitY=0.5;entryX=0.5;entryY=0;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="watch" target="canceled"><mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="660" y="468"/><mxPoint x="660" y="524"/><mxPoint x="320" y="524"/></Array></mxGeometry></mxCell></root></mxGraphModel></diagram></mxfile>
@@ -0,0 +1,64 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1240" height="740" viewBox="0 0 1240 740" role="img" aria-label="生成任务生命周期与受理边界">
2
+ <defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#6b7686"/></marker></defs>
3
+ <rect width="1240" height="740" fill="#ffffff"/>
4
+ <text x="48" y="52" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="21" font-weight="700" fill="#1f2328">生成任务生命周期与受理边界</text>
5
+ <text x="48" y="78" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="13" fill="#5b6472">failover 只发生在上传 / 提交 / 同步生成阶段;远端一旦受理,绝不自动重提</text>
6
+ <path d="M220,330 L268,330" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
7
+ <path d="M448,330 L496,330" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
8
+ <path d="M716,330 L764,330" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
9
+ <path d="M944,330 L996,330" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
10
+ <rect x="959" y="320.5" width="22" height="19" rx="4" fill="#ffffff"/>
11
+ <text x="970" y="334" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">是</text>
12
+ <path d="M854,280 L854,169 L716,169 L716,169" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
13
+ <rect x="843" y="215" width="22" height="19" rx="4" fill="#ffffff"/>
14
+ <text x="854" y="228.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">否</text>
15
+ <path d="M606,198 L606,298" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
16
+ <rect x="577" y="238.5" width="58" height="19" rx="4" fill="#ffffff"/>
17
+ <text x="606" y="252" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">重试提交</text>
18
+ <path d="M496,169 L440,169" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
19
+ <path d="M1096,360 L1096,468 L940,468 L940,468" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
20
+ <rect x="1067" y="404.5" width="58" height="19" rx="4" fill="#ffffff"/>
21
+ <text x="1096" y="418" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">转入盯守</text>
22
+ <path d="M820,496 L820,560" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
23
+ <path d="M760,496 L760,534 L555,534 L555,560" fill="none" stroke="#6b7686" stroke-width="1.6" stroke-dasharray="5 4" marker-end="url(#arr)"/>
24
+ <path d="M700,468 L660,468 L660,524 L320,524 L320,560" fill="none" stroke="#6b7686" stroke-width="1.6" stroke-dasharray="5 4" marker-end="url(#arr)"/>
25
+ <rect x="416.84000000000003" y="508.5" width="146.32" height="19" rx="4" fill="#ffffff"/>
26
+ <text x="490" y="522" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">轮询耗尽 / 取消 / 错误</text>
27
+ <rect x="40" y="300" width="180" height="60" rx="30" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
28
+ <text x="130" y="326" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">Agent 工具 / GUI 动作卡</text>
29
+ <text x="130" y="346.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">提交生成请求</text>
30
+ <rect x="268" y="300" width="180" height="60" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
31
+ <text x="358" y="326" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">创建本地任务</text>
32
+ <text x="358" y="346.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">status = running</text>
33
+ <rect x="496" y="298" width="220" height="64" rx="9" fill="#fff6e5" stroke="#c98a1b" stroke-width="1.6"/>
34
+ <text x="606" y="326" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">提交供应商(Attempt)</text>
35
+ <text x="606" y="346.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">上传 · 提交 · 同步生成</text>
36
+ <path d="M854,280 L944,330 L854,380 L764,330 Z" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
37
+ <text x="854" y="335.25" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">远端受理?</text>
38
+ <rect x="996" y="300" width="200" height="60" rx="9" fill="#eaf7ef" stroke="#2e9e5b" stroke-width="1.6"/>
39
+ <text x="1096" y="326" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">已受理</text>
40
+ <text x="1096" y="346.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">remoteTaskId 落盘</text>
41
+ <rect x="496" y="140" width="220" height="58" rx="9" fill="#fceded" stroke="#c94f4f" stroke-width="1.6"/>
42
+ <text x="606" y="174.25" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">failover:切换下一候选模型</text>
43
+ <rect x="240" y="141" width="200" height="56" rx="9" fill="#fceded" stroke="#c94f4f" stroke-width="1.6"/>
44
+ <text x="340" y="165" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">候选耗尽</text>
45
+ <text x="340" y="185.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">任务失败</text>
46
+ <rect x="700" y="440" width="240" height="56" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
47
+ <text x="820" y="464" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">后台轮询盯守</text>
48
+ <text x="820" y="484.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">重启后自动接管 running 任务</text>
49
+ <rect x="700" y="560" width="240" height="60" rx="9" fill="#eaf7ef" stroke="#2e9e5b" stroke-width="1.6"/>
50
+ <text x="820" y="586" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">succeeded</text>
51
+ <text x="820" y="606.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">产物转存 DSH 附件 + 令牌媒体链接</text>
52
+ <rect x="460" y="560" width="190" height="60" rx="9" fill="#fceded" stroke="#c94f4f" stroke-width="1.6"/>
53
+ <text x="555" y="586" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">failed</text>
54
+ <text x="555" y="606.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">保留错误说明</text>
55
+ <rect x="230" y="560" width="180" height="60" rx="9" fill="#f5f6f8" stroke="#b9c2ce" stroke-width="1.6"/>
56
+ <text x="320" y="586" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">canceled</text>
57
+ <text x="320" y="606.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">取消信号传播</text>
58
+ <rect x="40" y="650" width="560" height="64" rx="8" fill="#ffffff" stroke="#b9c2ce" stroke-width="1.3" stroke-dasharray="5 4"/>
59
+ <text x="320" y="678" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">黄金规则:远端受理后绝不自动重提</text>
60
+ <text x="320" y="698.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">重提只发生在提交阶段的模型候选切换,避免重复计费</text>
61
+ <rect x="640" y="650" width="556" height="64" rx="8" fill="#ffffff" stroke="#b9c2ce" stroke-width="1.3" stroke-dasharray="5 4"/>
62
+ <text x="918" y="678" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">启动恢复与取消</text>
63
+ <text x="918" y="698.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">有 remoteTaskId 的任务继续盯守;孤儿提交清理为 failed;取消传播到本地等待与轮询</text>
64
+ </svg>
@@ -0,0 +1 @@
1
+ <mxfile host="iris-docs"><diagram id="iris-workflow-compose" name="页 1"><mxGraphModel dx="1000" dy="700" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1150" pageHeight="500" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="src" value="源图片&lt;br&gt;宿主路径 / 会话附件" style="rounded=1;arcSize=50;whiteSpace=wrap;html=1;fillColor=#f5f6f8;strokeColor=#b9c2ce;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="40" y="116" width="190" height="66" as="geometry"/></mxCell><mxCell id="look" value="iris_look_at_image&lt;br&gt;结构化观察:主体 · 构图 · 色彩 · 文字" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eef3ff;strokeColor=#4d6bfe;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="280" y="116" width="250" height="66" as="geometry"/></mxCell><mxCell id="prompt" value="生成自包含提示词&lt;br&gt;区分原图事实与本次改动要求" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eef3ff;strokeColor=#4d6bfe;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="580" y="116" width="250" height="66" as="geometry"/></mxCell><mxCell id="draw" value="iris_draw_image&lt;br&gt;付费步骤 · 按产物数计" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff6e5;strokeColor=#c98a1b;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="880" y="116" width="230" height="66" as="geometry"/></mxCell><mxCell id="review" value="iris_relook_attachment&lt;br&gt;对照原始需求逐项复核" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eef3ff;strokeColor=#4d6bfe;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="880" y="286" width="230" height="66" as="geometry"/></mxCell><mxCell id="decide" value="与需求一致?" style="rhombus;whiteSpace=wrap;html=1;fillColor=#eef3ff;strokeColor=#4d6bfe;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="600" y="272" width="180" height="94" as="geometry"/></mxCell><mxCell id="deliver" value="交付产物&lt;br&gt;attachment id + 任务记录 + 媒体链接" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eaf7ef;strokeColor=#2e9e5b;fontColor=#1f2328;fontSize=12;strokeWidth=1.5;" vertex="1" parent="1"><mxGeometry x="320" y="278" width="220" height="82" as="geometry"/></mxCell><mxCell id="note1" value="纪律:只在缺失会改变结果时提问;复核不通过才重绘;生成前明确用途与比例;保留 attachment id 与任务记录供后续步骤复用" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#b9c2ce;fontColor=#5b6472;fontSize=11;dashed=1;" vertex="1" parent="1"><mxGeometry x="40" y="408" width="1070" height="40" as="geometry"/></mxCell><mxCell id="c1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=1;exitY=0.5;entryX=0;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="src" target="look"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=1;exitY=0.5;entryX=0;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="look" target="prompt"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=1;exitY=0.5;entryX=0;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="prompt" target="draw"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c4" value="需要复核" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0.5;exitY=1;entryX=0.5;entryY=0;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="draw" target="review"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0;exitY=0.5;entryX=1;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="review" target="decide"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c6" value="一致" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;exitX=0;exitY=0.5;entryX=1;entryY=0.5;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="decide" target="deliver"><mxGeometry relative="1" as="geometry"/></mxCell><mxCell id="c7" value="不一致 · 仅针对具体偏差重生成" style="edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeColor=#6b7686;strokeWidth=1.5;fontColor=#5b6472;fontSize=11;labelBackgroundColor=#ffffff;dashed=1;exitX=0.5;exitY=0;entryX=0.5;entryY=1;exitDx=0;exitDy=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="decide" target="prompt"><mxGeometry relative="1" as="geometry"><Array as="points"><mxPoint x="690" y="232"/><mxPoint x="705" y="232"/></Array></mxGeometry></mxCell></root></mxGraphModel></diagram></mxfile>
@@ -0,0 +1,41 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1150" height="500" viewBox="0 0 1150 500" role="img" aria-label="组合工作流示例:看图 → 重绘 → 自检">
2
+ <defs><marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#6b7686"/></marker></defs>
3
+ <rect width="1150" height="500" fill="#ffffff"/>
4
+ <text x="48" y="52" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="21" font-weight="700" fill="#1f2328">组合工作流示例:看图 → 重绘 → 自检</text>
5
+ <text x="48" y="78" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="13" fill="#5b6472">来自随包 Skill「iris-compose-media」:先理解,再生成,复核针对具体偏差,重试有界</text>
6
+ <path d="M230,149 L280,149" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
7
+ <path d="M530,149 L580,149" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
8
+ <path d="M830,149 L880,149" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
9
+ <path d="M995,182 L995,286" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
10
+ <rect x="966" y="224.5" width="58" height="19" rx="4" fill="#ffffff"/>
11
+ <text x="995" y="238" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">需要复核</text>
12
+ <path d="M880,319 L780,319" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
13
+ <path d="M600,319 L540,319" fill="none" stroke="#6b7686" stroke-width="1.6" marker-end="url(#arr)"/>
14
+ <rect x="553" y="309.5" width="34" height="19" rx="4" fill="#ffffff"/>
15
+ <text x="570" y="323" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">一致</text>
16
+ <path d="M690,272 L690,232 L705,232 L705,182" fill="none" stroke="#6b7686" stroke-width="1.6" stroke-dasharray="5 4" marker-end="url(#arr)"/>
17
+ <rect x="606.92" y="222.5" width="186.16" height="19" rx="4" fill="#ffffff"/>
18
+ <text x="700" y="236" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" fill="#5b6472">不一致 · 仅针对具体偏差重生成</text>
19
+ <rect x="40" y="116" width="190" height="66" rx="33" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
20
+ <text x="135" y="145" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">源图片</text>
21
+ <text x="135" y="165.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">宿主路径 / 会话附件</text>
22
+ <rect x="280" y="116" width="250" height="66" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
23
+ <text x="405" y="145" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">iris_look_at_image</text>
24
+ <text x="405" y="165.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">结构化观察:主体 · 构图 · 色彩 · 文字</text>
25
+ <rect x="580" y="116" width="250" height="66" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
26
+ <text x="705" y="145" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">生成自包含提示词</text>
27
+ <text x="705" y="165.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">区分原图事实与本次改动要求</text>
28
+ <rect x="880" y="116" width="230" height="66" rx="9" fill="#fff6e5" stroke="#c98a1b" stroke-width="1.6"/>
29
+ <text x="995" y="145" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">iris_draw_image</text>
30
+ <text x="995" y="165.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">付费步骤 · 按产物数计</text>
31
+ <rect x="880" y="286" width="230" height="66" rx="9" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
32
+ <text x="995" y="315" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">iris_relook_attachment</text>
33
+ <text x="995" y="335.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">对照原始需求逐项复核</text>
34
+ <path d="M690,272 L780,319 L690,366 L600,319 Z" fill="#eef3ff" stroke="#4d6bfe" stroke-width="1.6"/>
35
+ <text x="690" y="324.25" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">与需求一致?</text>
36
+ <rect x="320" y="278" width="220" height="82" rx="9" fill="#eaf7ef" stroke="#2e9e5b" stroke-width="1.6"/>
37
+ <text x="430" y="315" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">交付产物</text>
38
+ <text x="430" y="335.5" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="12" font-weight="400" fill="#5b6472">attachment id + 任务记录 + 媒体链接</text>
39
+ <rect x="40" y="408" width="1070" height="40" rx="8" fill="#ffffff" stroke="#b9c2ce" stroke-width="1.3" stroke-dasharray="5 4"/>
40
+ <text x="575" y="433.25" text-anchor="middle" font-family="'PingFang SC','Hiragino Sans GB','Noto Sans CJK SC','Noto Sans SC','Microsoft YaHei',sans-serif" font-size="14.5" font-weight="600" fill="#1f2328">纪律:只在缺失会改变结果时提问;复核不通过才重绘;生成前明确用途与比例;保留 attachment id 与任务记录供后续步骤复用</text>
41
+ </svg>
@@ -0,0 +1,26 @@
1
+ # Iris Media for DSH v0.1.2
2
+
3
+ This release makes Iris easier to discover and use from a normal DSH installation, while giving prompt optimization a first-class place in the conversation composer.
4
+
5
+ ## Highlights
6
+
7
+ - Adds a borderless 🫧 prompt-optimization control to the DSH composer. It supports general, image, video, and S2V prompts; previews before write-back; never sends automatically; and uses a glass, mobile-safe panel.
8
+ - Adds an independent, private JSON configuration for optimizer prompts, target templates, model routing, reasoning strategy, and output budget, with import, export, reset, disable, and re-enable controls.
9
+ - Defaults optimizer reasoning to `off-if-supported`, using DSH model metadata instead of inheriting the session's High/Low setting and consuming the visible output budget.
10
+ - Registers the bundled `iris-verify-ui` and `iris-compose-media` Skills when the plugin starts, so npm users can discover them outside this repository.
11
+ - Uses the unique Cordis row ID `mokuyoaxis-dsh-iris`, avoiding collision with the unrelated unscoped `dsh-iris` package.
12
+ - Adds an English README, architecture and workflow diagrams, an Android 16 screenshot gallery, and a real Iris image generated through the documented prompt-to-artifact loop.
13
+ - Keeps the floating quick panel focused on running tasks and recent successful artifacts; failed and canceled history remains available in the full workbench.
14
+ - Makes capability indicators immediately reflect configured providers without requiring every action card to be opened first; an action error dims its indicator.
15
+
16
+ ## Compatibility
17
+
18
+ - DeepSeek Harness: `>=0.1.2-rc.1 <0.1.3-0`
19
+ - Node.js: `>=20.10.0`
20
+ - Verified locally on Linux ARM64 under Android 16 Termux/PRoot Debian with Node.js 22.23.2 and DSH 0.1.2-rc.1.
21
+
22
+ ## Upgrade note
23
+
24
+ Normal plugin upgrades need no manual migration. If a personal `cordis.patch.yml` explicitly overrides the old row ID `dsh-iris`, change that override to `mokuyoaxis-dsh-iris`.
25
+
26
+ The npm package remains `@mokuyoaxis/dsh-iris`; the unscoped `dsh-iris` name belongs to another project.
@@ -0,0 +1,34 @@
1
+ # Iris Android 真机截图 / Android screenshots
2
+
3
+ 这些画面来自 Android 16 浏览器,访问运行在 Termux/PRoot Debian ARM64 中的 DSH;界面使用 DSH 默认皮肤。截图与生成图均为 2026-09-06 的真实操作结果,公开副本已重新编码并移除 EXIF、XMP 与 IPTC 元数据。
4
+
5
+ These captures were taken in an Android 16 browser connected to DSH running under Termux/PRoot Debian ARM64, with the default DSH skin. They show real interactions from 2026-09-06. Public copies were re-encoded without EXIF, XMP, or IPTC metadata.
6
+
7
+ ## 通用提示词优化 / General prompt optimization
8
+
9
+ | 优化前 / Before | 优化预览 / Preview |
10
+ |---|---|
11
+ | ![通用提示词优化前](assets/screenshots/prompt-general-before.webp) | ![通用提示词优化预览,显示实际模型与 off 思考档位](assets/screenshots/prompt-general-preview.webp) |
12
+
13
+ 优化结果只会先进入预览。点击“写回输入框”后,Iris 更新当前草稿,但不会自动发送消息。
14
+
15
+ ![通用提示词优化结果写回输入框](assets/screenshots/prompt-general-after.webp)
16
+
17
+ ## 图片生成闭环 / Image-generation loop
18
+
19
+ | 一句话草稿 / Short draft | 图片 Prompt 预览 / Image prompt preview |
20
+ |---|---|
21
+ | ![输入鸢尾花作为图片生成草稿](assets/screenshots/prompt-image-before.webp) | ![由 Iris 优化的鸢尾花图片生成提示词](assets/screenshots/prompt-image-preview.webp) |
22
+
23
+ 优化后的 Prompt 写回输入框并交给 DSH Agent 后,Iris 使用阿里云百炼的 `qwen-image-3.0-pro` 完成生成任务。任务详情和实际产物如下。
24
+
25
+ | 成功任务 / Succeeded task | 实际产物 / Generated artifact |
26
+ |---|---|
27
+ | ![Iris 图片生成任务成功详情](assets/screenshots/image-task-succeeded.webp) | ![Iris 生成的蓝紫色鸢尾花](assets/examples/iris-flower-generated.webp) |
28
+
29
+ 原始草稿:`鸢尾花`
30
+
31
+ 提示词优化模型:`deepseek-official / deepseek-v4-flash`,实际思考档位 `off`
32
+ 图片生成供应商与模型:阿里云百炼 / `qwen-image-3.0-pro`
33
+
34
+ 截图中的任务 ID 是本地任务标识,不是媒体访问令牌;画面不包含 API Key、账户信息、私有绝对路径或认证 URL。
package/lib/api.js CHANGED
@@ -18,6 +18,13 @@ import * as tasks from './tasks.js';
18
18
  import { runAction } from './actions.js';
19
19
  import { chmodPrivateFile, ensurePrivateDir } from './private-storage.js';
20
20
  import { inferMediaProtocol } from './provider-protocol.js';
21
+ import { optimizePrompt } from './prompt-optimizer.js';
22
+ import {
23
+ importPromptOptimizerConfig,
24
+ loadPromptOptimizerConfig,
25
+ resetPromptOptimizerConfig,
26
+ setPromptOptimizerEnabled
27
+ } from './prompt-optimizer-config.js';
21
28
 
22
29
  function readJsonSafe(file) {
23
30
  try {
@@ -244,7 +251,7 @@ function serveSse(res) {
244
251
  res.on('error', onClose);
245
252
  }
246
253
 
247
- /** webServer 前缀路由 handler:GET/HEAD /iris/api/state 和 /iris/api/task/:id,POST /iris/api/actions/:name */
254
+ /** webServer 前缀路由 handler:状态、任务、动作与提示词优化器 API。 */
248
255
  export function serveApi(req, res, ctx) {
249
256
  try {
250
257
  const method = String(req.method || 'GET').toUpperCase();
@@ -256,6 +263,8 @@ export function serveApi(req, res, ctx) {
256
263
  if (urlPath === '/iris/api/upload') {
257
264
  return handleUpload(req, res);
258
265
  }
266
+ const promptMatch = typeof ctx === 'object' && urlPath.match(/^\/iris\/api\/prompt-optimizer\/(optimize|import|reset|enabled)\/?$/);
267
+ if (promptMatch) return handlePromptOptimizer(ctx, promptMatch[1], req, res);
259
268
  const actionMatch = typeof ctx === 'object' && urlPath.match(/^\/iris\/api\/actions\/([A-Za-z0-9_-]+)\/?$/);
260
269
  if (!actionMatch) { return sendJson(res, 405, { error: 'method not allowed' }); }
261
270
  return handleAction(ctx, actionMatch[1], req, res);
@@ -269,6 +278,9 @@ export function serveApi(req, res, ctx) {
269
278
  if (method === 'HEAD') return sendJson(res, 405, { error: 'method not allowed' });
270
279
  return serveSse(res);
271
280
  }
281
+ if (urlPath === '/iris/api/prompt-optimizer/config') {
282
+ return sendJson(res, 200, method === 'HEAD' ? {} : loadPromptOptimizerConfig());
283
+ }
272
284
  const taskMatch = urlPath.match(/^\/iris\/api\/task\/([^/]+)$/);
273
285
  if (taskMatch) {
274
286
  return serveTaskDetail(taskMatch[1], method, res);
@@ -297,10 +309,12 @@ function sendJson(res, status, obj) {
297
309
 
298
310
  /** POST /iris/api/actions/:name:读取 JSON body 调 runAction 返回结果 */
299
311
  const MAX_BODY_BYTES = 1e6; // 1MB 上限(防滥用)
300
- function handleAction(ctx, name, req, res) {
312
+ function handleJsonPost(req, res, execute) {
301
313
  const chunks = [];
302
314
  const controller = new AbortController();
303
315
  let completed = false;
316
+ let totalBytes = 0;
317
+ let overflow = false;
304
318
  const finish = (status, body) => {
305
319
  if (completed) return;
306
320
  completed = true;
@@ -309,32 +323,49 @@ function handleAction(ctx, name, req, res) {
309
323
  const disconnect = () => { if (!completed) controller.abort(); };
310
324
  if (typeof req.on === 'function') req.on('aborted', disconnect);
311
325
  if (typeof res.on === 'function') res.on('close', disconnect);
312
- let totalBytes = 0; // 注意:必须按字节计——body 数组的 length 是块数不是字节数(旧代码的坑)
313
- let overflow = false;
314
- req.on('data', (c) => {
326
+ req.on('data', (chunk) => {
315
327
  if (overflow) return;
316
- totalBytes += c.length;
328
+ totalBytes += chunk.length;
317
329
  if (totalBytes > MAX_BODY_BYTES) {
318
330
  overflow = true;
319
- chunks.length = 0; // 丢弃已收块,不再占内存
331
+ chunks.length = 0;
320
332
  return;
321
333
  }
322
- chunks.push(c);
334
+ chunks.push(chunk);
323
335
  });
324
336
  req.on('end', async () => {
325
337
  if (overflow) return finish(413, { error: 'request body too large (limit 1MB)' });
326
338
  try {
327
339
  const raw = Buffer.concat(chunks).toString('utf8');
328
- const args = raw ? JSON.parse(raw) : {};
329
- const result = await runAction(ctx, name, args, { signal: controller.signal });
340
+ const body = raw ? JSON.parse(raw) : {};
341
+ const result = await execute(body, controller.signal);
330
342
  finish(200, result);
331
343
  } catch (err) {
332
- finish(controller.signal.aborted ? 499 : 400, { error: controller.signal.aborted ? 'request canceled' : String((err && err.message) || err) });
344
+ finish(controller.signal.aborted ? 499 : 400, {
345
+ error: controller.signal.aborted ? 'request canceled' : String((err && err.message) || err)
346
+ });
333
347
  }
334
348
  });
335
349
  req.on('error', () => finish(400, { error: 'bad request' }));
336
350
  }
337
351
 
352
+ function handleAction(ctx, name, req, res) {
353
+ return handleJsonPost(req, res, (args, signal) => runAction(ctx, name, args, { signal }));
354
+ }
355
+
356
+ function handlePromptOptimizer(ctx, operation, req, res) {
357
+ return handleJsonPost(req, res, async (body, signal) => {
358
+ if (operation === 'optimize') return optimizePrompt(ctx, body, { signal });
359
+ if (operation === 'import') {
360
+ const imported = body && body.config !== undefined ? body.config : body;
361
+ return { ok: true, ...importPromptOptimizerConfig(imported) };
362
+ }
363
+ if (operation === 'reset') return { ok: true, ...resetPromptOptimizerConfig() };
364
+ if (operation === 'enabled') return { ok: true, ...setPromptOptimizerEnabled(body && body.enabled) };
365
+ throw new Error('unknown prompt optimizer operation');
366
+ });
367
+ }
368
+
338
369
  /* ---------- 文件选择器 L2:本地文件上传(阶段 10) ---------- */
339
370
  export const MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
340
371
  export const UPLOAD_TTL_MS = 7 * 24 * 60 * 60 * 1000;
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const SKILL_NAMES = ['iris-verify-ui', 'iris-compose-media'];
8
+ const SKILL_ROOT = fileURLToPath(new URL('../.dsh/skills/', import.meta.url));
9
+
10
+ function parseScalar(value, file) {
11
+ const trimmed = value.trim();
12
+ if (trimmed.startsWith('"')) {
13
+ try {
14
+ return JSON.parse(trimmed);
15
+ } catch (error) {
16
+ throw new Error(`随包 Skill frontmatter 字符串无效:${file}: ${error.message}`);
17
+ }
18
+ }
19
+ if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
20
+ return trimmed.slice(1, -1).replaceAll("''", "'");
21
+ }
22
+ return trimmed;
23
+ }
24
+
25
+ /** 读取由 Iris 自己发布的固定 Skill;不把此解析器当作通用 YAML 解析器。 */
26
+ export function loadBundledSkills() {
27
+ return SKILL_NAMES.map((expectedName) => {
28
+ const directory = path.join(SKILL_ROOT, expectedName);
29
+ const file = path.join(directory, 'SKILL.md');
30
+ const source = fs.readFileSync(file, 'utf8');
31
+ const frontmatter = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
32
+ if (!frontmatter) throw new Error(`随包 Skill 缺少 frontmatter:${file}`);
33
+
34
+ const nameLine = frontmatter[1].match(/^name:\s*(.+)$/m);
35
+ const descriptionLine = frontmatter[1].match(/^description:\s*(.+)$/m);
36
+ const name = nameLine ? parseScalar(nameLine[1], file) : '';
37
+ const description = descriptionLine ? parseScalar(descriptionLine[1], file) : '';
38
+ if (name !== expectedName) throw new Error(`随包 Skill 名称不匹配:${file}`);
39
+ if (!description) throw new Error(`随包 Skill 缺少 description:${file}`);
40
+
41
+ return Object.freeze({
42
+ name,
43
+ description,
44
+ content: source.slice(frontmatter[0].length).replace(/^\r?\n/, ''),
45
+ source: 'bundled',
46
+ provider: 'iris-bundled',
47
+ path: file,
48
+ resourceBase: Object.freeze({ kind: 'directory', path: directory })
49
+ });
50
+ });
51
+ }
52
+
53
+ /** 注册为宿主级 runtime Skill;DSH 项目目录中的同名 Skill 仍按原生优先级胜出。 */
54
+ export function registerBundledSkills(ctx) {
55
+ const registry = ctx && (ctx.skills || (typeof ctx.get === 'function' && ctx.get('skills')));
56
+ if (!registry || typeof registry.register !== 'function') {
57
+ throw new Error('DSH Skill registry 不可用');
58
+ }
59
+ const skills = loadBundledSkills();
60
+ for (const skill of skills) registry.register(skill);
61
+ return skills.map((skill) => skill.name);
62
+ }