Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1454.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Workflow stage now shows short task summaries with pause, resume, and progress tied to live runs only; sidebar status never mirrors scripted or seeded runs (`demos[]` and `workflow/demo-step` removed). Factory workflows such as the delivery preset can be removed from the sidebar via Delete (confirm), not reset-only ([#1454](https://github.com/ThinkFlowLab/FlyBot/issues/1454)).
16 changes: 8 additions & 8 deletions docs/subsystems/workflow.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion plugins/workflow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ npx -y @deepseek-ai/dsh@0.2.0-rc.2 plugin --profile web add "github:ThinkFlowLab
npm test
```

Endpoints, all under `flybot/workflow/`: `state`, `templates` (read), `create`, `assign`, `concurrency`, `advance`, `demo-step`, `reset`, `say`, `graph`, `ingest`.
Endpoints, all under `flybot/workflow/`: `state`, `templates` (read), `create`, `assign`, `concurrency`, `advance`, `reset`, `say`, `graph`, `ingest`.

## 中文

Expand Down
107 changes: 73 additions & 34 deletions plugins/workflow/client.js

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion plugins/workflow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ export function apply(ctx, config = {}) {
fb.api.handle('workflow/assign', body => service.assign(body)),
fb.api.handle('workflow/concurrency', body => service.setConcurrency(body)),
fb.api.handle('workflow/advance', body => service.advance(body)),
fb.api.handle('workflow/demo-step', body => service.demoStep(body)),
fb.api.handle('workflow/reset', body => service.reset(body)),
fb.api.handle('workflow/pause', body => service.pause(body)),
fb.api.handle('workflow/resume', body => service.resume(body)),
fb.api.handle('workflow/say', body => service.say(body)),
fb.api.handle('workflow/graph', body => service.saveGraph(body)),
fb.api.handle('workflow/templates', () => ({ ok: true, templates: service.state().templates }), { read: true }),
Expand Down
22 changes: 17 additions & 5 deletions plugins/workflow/src/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -580,8 +580,8 @@
const index = layerIndex(graph, nodeId)
const layer = graph.layers[index]
const outputs = ctx.outputs && typeof ctx.outputs === 'object' ? ctx.outputs : {}
const loops = { ...(ctx.loops ?? {}) }

Check warning on line 583 in plugins/workflow/src/graph.js

View workflow job for this annotation

GitHub Actions / plugins (workflow)

unicorn(no-useless-fallback-in-spread)

Empty fallbacks in spreads are unnecessary
const fired = { ...(ctx.fired ?? {}) }

Check warning on line 584 in plugins/workflow/src/graph.js

View workflow job for this annotation

GitHub Actions / plugins (workflow)

unicorn(no-useless-fallback-in-spread)

Empty fallbacks in spreads are unnecessary
const looping = loopDecision(graph, nodeId, outputs, ctx.loops)
if (looping) {
loops[edgeKey(looping.edge)] = looping.ran
Expand Down Expand Up @@ -651,9 +651,7 @@
return ids
}

// The demo player (and the "next" button) ask the graph what one step is.
// A node marked failOnce fails the first time it is the running choice, then
// a later step retries it. The join still waits until that node is done.
// Scripted step chooser for graph unit tests (failOnce retry ordering).
export function nextDemoOp(graph, status, memory = {}) {
ensure(graph)
const failed = orderedIds(graph, status, 'failed')
Expand Down Expand Up @@ -687,15 +685,29 @@
return graph.layers.map(layer => layer.map(id => nodeById(graph, id)?.name ?? id).join(' ‖ ')).join(' → ')
}

export function summarizeTask(task, maxTitle = 72) {
const id = cleanLine(task?.id, 80)
const title = cleanLine(task?.title, maxTitle)
if (!id && !title) return ''
if (!title) return id
if (!id) return title
return `${id} · ${title}`
}

export function rowPreview(workflow, run) {
if (run?.status === 'running') {
const live = run?.live === true
if (live && run?.status === 'running') {
const id = orderedIds(workflow, run.nodeStatus, 'running')[0]
const name = id ? nodeById(workflow, id)?.name : ''
const task = run.task?.id ?? ''
if (run.paused === true) {
return name && task ? `已暂停 · ${name} · ${task}` : (task ? `已暂停 · ${task}` : '已暂停')
}
return name ? `${name} 正在处理 ${task}…` : '运行中'
}
if (workflow.trigger === 'schedule' && workflow.scheduleLabel) return `下次运行:${workflow.scheduleLabel}`
return run?.status === 'done' && run.task?.id ? `✓ ${run.task.id} 已完成` : '等待任务'
if (live && run?.status === 'done' && run.task?.id) return `✓ ${run.task.id} 已完成`
return '等待任务'
}

export function stepLabel(graph, nodeId) {
Expand Down
16 changes: 8 additions & 8 deletions plugins/workflow/src/narrate.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Chat lines for one graph step. Delivery-template nodes use the demo's copy;
// any other graph gets a short line that names the Bot and the task. Text is
// Chat lines for one graph step. Delivery-template nodes use canned copy when
// no Bot reply is quoted; any other graph gets a short line that names the Bot and the task. Text is
// plain: the client renders it as a text node.

import { nodeById, stepLabel } from './graph.js'
import { nodeById, stepLabel, summarizeTask } from './graph.js'
import { DELIVERY_ID } from './template.js'

function names(workflow, ids) {
Expand Down Expand Up @@ -34,11 +34,11 @@ function liveText(value, fallback) {
return text || fallback
}

// A live run quotes the Bot. The delivery script's canned lines stay on the demo path.
// A live run quotes the Bot when event.text is set; otherwise doneText fills in.
function liveLines(workflow, task, event) {
const label = `${task?.id ?? ''} ${task?.title ?? ''}`.trim()
const label = summarizeTask(task)
if (event.type === 'start') {
return [{ kind: 'system', tone: 'accent', text: `任务 ${label} 已进入 Workflow,${names(workflow, event.started)} 认领` }]
return [{ kind: 'system', tone: 'accent', text: `开始 · ${label} · ${names(workflow, event.started)}` }]
}
if (event.type === 'fail') {
const node = nodeById(workflow, event.nodeId)
Expand All @@ -59,7 +59,7 @@ function liveLines(workflow, task, event) {
if (event.joined && event.layerSize > 1 && event.started.length > 0) {
lines.push({ kind: 'system', text: `⇢ 并行分支汇合(join)→ ${names(workflow, event.started)}` })
}
if (event.finished) lines.push({ kind: 'system', tone: 'ok', text: `✓ Workflow 运行完成 · ${task.id} · ${workflow.nodes.length} 个节点` })
if (event.finished) lines.push({ kind: 'system', tone: 'ok', text: `✓ 完成 · ${summarizeTask(task)} · ${workflow.nodes.length} 步` })
return lines
}
if (event.type === 'note') return [{ kind: 'system', tone: event.tone, text: event.text }]
Expand Down Expand Up @@ -96,7 +96,7 @@ export function linesFor(workflow, task, event) {
if (event.joined && event.layerSize > 1 && event.started.length > 0) {
lines.push({ kind: 'system', text: `⇢ 并行分支汇合(join)→ ${names(workflow, event.started)}` })
}
if (event.finished) lines.push({ kind: 'system', tone: 'ok', text: `✓ Workflow 运行完成 · ${task.id} · ${workflow.nodes.length} 个节点` })
if (event.finished) lines.push({ kind: 'system', tone: 'ok', text: `✓ 完成 · ${summarizeTask(task)} · ${workflow.nodes.length} 步` })
return lines
}
if (event.type === 'reset') return [{ kind: 'system', text: '↺ 已重置运行' }]
Expand Down
Loading
Loading