返回 AiToEarn
TaskInstance.ts
根目录 / project / aitoearn-web / src / store / agent / task-instance / TaskInstance.ts
1 /**
2 * TaskInstance - 任务实例类
3 * 每个 Agent 任务有独立的实例,消除多任务竞态条件
4 *
5 * 设计理念:
6 * - 每个任务创建一个 TaskInstance,持有自己的 taskId
7 * - 所有消息/工作流操作自动使用实例的 taskId,无需传参
8 * - SSE 回调绑定到具体实例,不受全局 currentTaskId 切换影响
9 *
10 * 模块拆分:
11 * - task-instance.types.ts: 类型定义
12 * - message.handler.ts: 消息处理逻辑
13 * - workflow.handler.ts: 工作流处理逻辑
14 * - sse.handler.ts: SSE 消息处理逻辑
15 */
16
17 import type {
18 IActionCard,
19 IActionContext,
20 IDisplayMessage,
21 IPublishFlowData,
22 ISSEMessage,
23 ITaskMessageData,
24 IUploadedMedia,
25 IWorkflowStep,
26 } from '../agent.types'
27 import type {
28 IMessageHandlerContext,
29 ISSECallbacks,
30 ISSEHandlerContext,
31 ITaskInstanceContext,
32 IWorkflowHandlerContext,
33 } from './task-instance.types'
34 import { getDefaultTaskData } from '../agent.state'
35 import * as MessageHandler from './message.handler'
36 import * as SSEHandler from './sse.handler'
37 import * as WorkflowHandler from './workflow.handler'
38
39 // ============ TaskInstance 类 ============
40
41 /**
42 * 任务实例类
43 * 封装单个任务的所有状态和操作
44 */
45 export class TaskInstance {
46 // ========== 实例标识 ==========
47
48 /** 实例ID(创建时确定,不可变,用于 Map key) */
49 readonly instanceId: string
50
51 /** 任务ID(可从 temp-xxx 更新为真实ID) */
52 private _taskId: string
53
54 /** 获取当前任务ID */
55 get taskId(): string {
56 return this._taskId
57 }
58
59 // ========== 实例级别的状态(不会被其他任务覆盖) ==========
60
61 /** 当前 assistant 消息 ID */
62 private currentAssistantMessageId: string = ''
63
64 /** 流式文本(正在生成的内容) */
65 private streamingText: string = ''
66
67 /** 当前步骤的工作流步骤 */
68 private currentStepWorkflow: IWorkflowStep[] = []
69
70 /** 当前步骤索引 */
71 private currentStepIndex: number = -1
72
73 /** SSE abort 函数 */
74 private sseAbort: (() => void) | null = null
75
76 /** 翻译函数 */
77 private t: ((key: string) => string) | null = null
78
79 /** Action 上下文 */
80 private actionContext: IActionContext | null = null
81
82 // ========== 上下文 ==========
83
84 /** Store 交互上下文 */
85 private ctx: ITaskInstanceContext
86
87 // ========== 构造函数 ==========
88
89 constructor(taskId: string, ctx: ITaskInstanceContext) {
90 this.instanceId = taskId // 实例ID = 初始 taskId
91 this._taskId = taskId
92 this.ctx = ctx
93 }
94
95 // ========== 生命周期方法 ==========
96
97 /**
98 * 更新真实 taskId(SSE init 返回后调用)
99 */
100 migrateToRealTaskId(realTaskId: string): void {
101 const oldTaskId = this._taskId
102 if (oldTaskId === realTaskId) {
103 return
104 }
105 this._taskId = realTaskId
106 this.ctx.migrateTaskData(oldTaskId, realTaskId)
107 }
108
109 /**
110 * 设置 SSE abort 函数
111 */
112 setAbort(abortFn: () => void): void {
113 this.sseAbort = abortFn
114 }
115
116 /**
117 * 中止 SSE 连接
118 */
119 abort(): void {
120 if (this.sseAbort) {
121 this.sseAbort()
122 this.sseAbort = null
123 }
124 }
125
126 /**
127 * 设置翻译函数
128 */
129 setTranslation(t: (key: string) => string): void {
130 this.t = t
131 }
132
133 /**
134 * 设置 Action 上下文
135 */
136 setActionContext(context: IActionContext): void {
137 this.actionContext = context
138 }
139
140 /**
141 * 重置实例状态(新一轮对话前调用)
142 */
143 resetForNewRound(): void {
144 this.currentAssistantMessageId = ''
145 this.streamingText = ''
146 this.currentStepWorkflow = []
147 this.currentStepIndex = -1
148 }
149
150 // ========== 数据访问方法 ==========
151
152 /**
153 * 获取当前任务数据
154 */
155 private getData(): ITaskMessageData {
156 return this.ctx.getData(this.taskId) || getDefaultTaskData()
157 }
158
159 /**
160 * 更新当前任务数据
161 */
162 private updateData(updater: (data: ITaskMessageData) => Partial<ITaskMessageData>): void {
163 this.ctx.syncToStore(this.taskId, updater)
164 }
165
166 // ========== Handler 上下文构建 ==========
167
168 /**
169 * 获取消息处理上下文
170 */
171 private getMessageContext(): IMessageHandlerContext {
172 return {
173 getTaskId: () => this.taskId,
174 getCurrentAssistantMessageId: () => this.currentAssistantMessageId,
175 setCurrentAssistantMessageId: (id: string) => {
176 this.currentAssistantMessageId = id
177 },
178 updateData: updater => this.updateData(updater),
179 }
180 }
181
182 /**
183 * 获取工作流处理上下文
184 */
185 private getWorkflowContext(): IWorkflowHandlerContext {
186 return {
187 ...this.getMessageContext(),
188 getStreamingText: () => this.streamingText,
189 setStreamingText: (text: string) => {
190 this.streamingText = text
191 },
192 appendStreamingText: (text: string) => {
193 this.streamingText += text
194 },
195 getCurrentStepWorkflow: () => this.currentStepWorkflow,
196 setCurrentStepWorkflow: (steps: IWorkflowStep[]) => {
197 this.currentStepWorkflow = steps
198 },
199 pushToCurrentStepWorkflow: (step: IWorkflowStep) => {
200 this.currentStepWorkflow.push(step)
201 },
202 getCurrentStepIndex: () => this.currentStepIndex,
203 setCurrentStepIndex: (index: number) => {
204 this.currentStepIndex = index
205 },
206 incrementCurrentStepIndex: () => {
207 this.currentStepIndex++
208 },
209 addMarkdownMessage: (message: string) =>
210 MessageHandler.addMarkdownMessage(this.getMessageContext(), message),
211 }
212 }
213
214 /**
215 * 获取 SSE 处理上下文
216 */
217 private getSSEContext(): ISSEHandlerContext {
218 return {
219 ...this.getWorkflowContext(),
220 getActionContext: () => this.actionContext,
221 migrateToRealTaskId: (realTaskId: string) => this.migrateToRealTaskId(realTaskId),
222 markMessageDone: () => this.markMessageDone(),
223 addMessage: (message: IDisplayMessage) => this.addMessage(message),
224 setIsGenerating: (value: boolean) => this.setIsGenerating(value),
225 setProgress: (value: number) => this.setProgress(value),
226 }
227 }
228
229 // ========== 消息方法(代理到 message.handler) ==========
230
231 /**
232 * 创建用户消息
233 */
234 createUserMessage(content: string, medias?: IUploadedMedia[]): IDisplayMessage {
235 return MessageHandler.createUserMessage(content, medias)
236 }
237
238 /**
239 * 创建 assistant 消息
240 */
241 createAssistantMessage(): IDisplayMessage {
242 return MessageHandler.createAssistantMessage(this.getMessageContext())
243 }
244
245 /**
246 * 添加消息到列表
247 */
248 addMessage(message: IDisplayMessage): void {
249 MessageHandler.addMessage(this.getMessageContext(), message)
250 }
251
252 /**
253 * 设置消息列表(用于加载历史消息)
254 */
255 setMessages(messages: IDisplayMessage[]): void {
256 MessageHandler.setMessages(this.getMessageContext(), messages)
257 }
258
259 /**
260 * 标记当前 assistant 消息为完成
261 */
262 markMessageDone(): void {
263 MessageHandler.markMessageDone(this.getMessageContext())
264 }
265
266 /**
267 * 标记当前 assistant 消息为错误
268 */
269 markMessageError(errorMessage: string): void {
270 MessageHandler.markMessageError(this.getMessageContext(), errorMessage)
271 }
272
273 /**
274 * 更新当前 assistant 消息内容
275 */
276 updateMessageContent(content: string): void {
277 MessageHandler.updateMessageContent(this.getMessageContext(), content)
278 }
279
280 /**
281 * 更新当前 assistant 消息的 actions
282 */
283 updateMessageActions(actions: IActionCard[]): void {
284 MessageHandler.updateMessageActions(this.getMessageContext(), actions)
285 }
286
287 /**
288 * 更新当前 assistant 消息内容和 actions
289 */
290 updateMessageWithActions(content: string, actions: IActionCard[]): void {
291 MessageHandler.updateMessageWithActions(this.getMessageContext(), content, actions)
292 }
293
294 /**
295 * 更新当前 assistant 消息内容,并将 medias 附加到最后一个 step
296 */
297 updateMessageContentWithMedias(
298 content: string,
299 medias?: Array<{ type: string, url: string, thumbUrl?: string }>,
300 ): void {
301 MessageHandler.updateMessageContentWithMedias(this.getMessageContext(), content, medias)
302 }
303
304 /**
305 * 更新当前 assistant 消息的发布流程数据
306 */
307 updateMessageWithPublishFlows(publishFlows: IPublishFlowData[]): void {
308 MessageHandler.updateMessageWithPublishFlows(this.getMessageContext(), publishFlows)
309 }
310
311 /**
312 * 更新当前 assistant 消息内容、actions 和发布流程数据
313 */
314 updateMessageWithActionsAndPublishFlows(
315 content: string,
316 actions: IActionCard[],
317 publishFlows: IPublishFlowData[],
318 ): void {
319 MessageHandler.updateMessageWithActionsAndPublishFlows(
320 this.getMessageContext(),
321 content,
322 actions,
323 publishFlows,
324 )
325 }
326
327 /**
328 * 添加到 markdown 消息历史
329 */
330 addMarkdownMessage(message: string): void {
331 MessageHandler.addMarkdownMessage(this.getMessageContext(), message)
332 }
333
334 /**
335 * 更新最后一条 markdown 消息
336 */
337 updateLastMarkdownMessage(message: string): void {
338 MessageHandler.updateLastMarkdownMessage(this.getMessageContext(), message)
339 }
340
341 // ========== 工作流方法(代理到 workflow.handler) ==========
342
343 /**
344 * 开始新步骤
345 */
346 startNewStep(): void {
347 WorkflowHandler.startNewStep(this.getWorkflowContext())
348 }
349
350 /**
351 * 添加工作流步骤
352 */
353 addWorkflowStep(step: IWorkflowStep): void {
354 WorkflowHandler.addWorkflowStep(this.getWorkflowContext(), step)
355 }
356
357 /**
358 * 更新最后一个工作流步骤
359 */
360 updateLastWorkflowStep(updater: (step: IWorkflowStep) => IWorkflowStep): void {
361 WorkflowHandler.updateLastWorkflowStep(this.getWorkflowContext(), updater)
362 }
363
364 /**
365 * 处理工具调用完成
366 */
367 handleToolCallComplete(toolName: string, toolInput: string): void {
368 WorkflowHandler.handleToolCallComplete(this.getWorkflowContext(), toolName, toolInput)
369 }
370
371 /**
372 * 处理工具结果
373 */
374 handleToolResult(resultText: string): void {
375 WorkflowHandler.handleToolResult(this.getWorkflowContext(), resultText)
376 }
377
378 // ========== 状态更新方法 ==========
379
380 /**
381 * 设置生成状态
382 */
383 setIsGenerating(isGenerating: boolean): void {
384 this.updateData(() => ({ isGenerating }))
385 }
386
387 /**
388 * 设置进度
389 */
390 setProgress(progress: number): void {
391 this.updateData(() => ({ progress }))
392 }
393
394 /**
395 * 清空工作流步骤(新一轮对话开始时调用)
396 */
397 clearWorkflowSteps(): void {
398 this.updateData(() => ({ workflowSteps: [] }))
399 }
400
401 // ========== SSE 消息处理(代理到 sse.handler) ==========
402
403 /**
404 * 处理 SSE 消息
405 */
406 handleSSEMessage(msg: ISSEMessage, callbacks?: ISSECallbacks): void {
407 SSEHandler.handleSSEMessage(this.getSSEContext(), msg, callbacks)
408 }
409
410 // ========== 调试方法 ==========
411
412 /**
413 * 获取实例状态信息(用于调试)
414 */
415 getDebugInfo(): object {
416 return {
417 instanceId: this.instanceId,
418 taskId: this.taskId,
419 currentAssistantMessageId: this.currentAssistantMessageId,
420 streamingTextLength: this.streamingText.length,
421 currentStepWorkflowLength: this.currentStepWorkflow.length,
422 currentStepIndex: this.currentStepIndex,
423 hasAbort: !!this.sseAbort,
424 }
425 }
426 }
427
428 // ============ 工厂函数 ============
429
430 /**
431 * 创建任务实例
432 */
433 export function createTaskInstance(taskId: string, ctx: ITaskInstanceContext): TaskInstance {
434 return new TaskInstance(taskId, ctx)
435 }
436
436 lines TYPESCRIPT