Files
gen2d/frontend/src/stores/generation.ts
T
Gmarker689 96ac56cb4e fix: 修复管线context取消、添加FIFO任务队列、移除重复提示词优化
三个修复:
1. runPipelineBg 改用 context.Background(),避免 HTTP 响应返回后
   Gin 取消 request context 导致后台管线静默失败
2. 新增 TaskQueue FIFO 串行队列,任务提交后进入 pending 状态排队,
   按提交顺序逐个执行,前端轮询显示排队中
3. promptOptimizerNode 移除 RunPromptAgent 调用,提示词优化仅由
   前端在提交前通过 /api/v1/prompt/optimize 执行一次,管线内只做
   风格合并和技术参数追加
2026-05-25 19:10:46 +08:00

130 lines
3.3 KiB
TypeScript
Executable File

import { create } from 'zustand'
import type { Asset, GenerateRequest, PipelineStage } from '../api/types'
import { submitGenerate, getTask, getAssets } from '../api/generate'
type Status = 'idle' | 'submitting' | 'running' | 'completed' | 'failed'
interface GenerationState {
taskId: string | null
projectId: string | null
stage: PipelineStage | null
progress: number
status: Status
statusText: string
retryCount: number
rejectReason: string | null
assets: Asset[]
error: string | null
submit: (req: GenerateRequest) => Promise<void>
reset: () => void
}
let pollTimer: ReturnType<typeof setInterval> | null = null
function stopPolling() {
if (pollTimer) {
clearInterval(pollTimer)
pollTimer = null
}
}
export const useGenerationStore = create<GenerationState>((set) => ({
taskId: null,
projectId: null,
stage: null,
progress: 0,
status: 'idle',
statusText: '',
retryCount: 0,
rejectReason: null,
assets: [],
error: null,
submit: async (req) => {
stopPolling()
set({ status: 'submitting', error: null, statusText: '提交中...', stage: null })
try {
const { taskId } = await submitGenerate(req)
set({
taskId,
projectId: req.projectId,
status: 'running',
progress: 5,
stage: 'prompt_builder',
statusText: '任务已提交,等待生成...',
})
pollTimer = setInterval(async () => {
try {
const task = await getTask(taskId)
set((s) => ({
stage: task.stage ?? s.stage,
progress: task.progress ?? s.progress,
retryCount: task.retryCount ?? s.retryCount,
statusText:
task.status === 'running'
? stageLabel(task.stage)
: task.status === 'pending'
? '排队中...'
: task.status,
}))
if (task.status === 'completed') {
stopPolling()
set({ progress: 90, statusText: '获取结果...' })
const assets = await getAssets(taskId)
set({
status: 'completed',
progress: 100,
stage: 'format_adapter',
statusText: '生成完成',
assets,
})
} else if (task.status === 'failed') {
stopPolling()
set({
status: 'failed',
error: task.error || '生成失败',
statusText: '生成失败',
})
}
} catch {
// 网络错误不中断轮询
}
}, 1500)
} catch (err) {
stopPolling()
set({ status: 'failed', error: (err as Error).message, statusText: '提交失败' })
}
},
reset: () => {
stopPolling()
set({
taskId: null,
projectId: null,
stage: null,
progress: 0,
status: 'idle',
statusText: '',
retryCount: 0,
rejectReason: null,
assets: [],
error: null,
})
},
}))
function stageLabel(stage?: string): string {
switch (stage) {
case 'prompt_builder': return '构建提示词...'
case 'asset_generator': return '生成素材中...'
case 'quality_supervisor': return '质检中...'
case 'format_adapter': return '格式转换中...'
default: return '生成中...'
}
}