返回 oh-my-ppt
edit-job-finalization.ts
根目录 / src / main / edit-jobs / edit-job-finalization.ts
1 import log from 'electron-log/main.js'
2 import type { IpcContext } from '../ipc/context'
3 import {
4 finalizeGenerationFailure,
5 resolveGenerationFailureSessionStatus
6 } from '../generation/finalization'
7 import { createGenerationContext } from '../generation/context'
8 import type { EditContext } from '../generation/types'
9
10 /** Persists the edit job before publishing its Runtime lifecycle terminal event. */
11 export async function settleEditJobSuccess(args: {
12 ctx: IpcContext
13 context: EditContext
14 }): Promise<void> {
15 const { ctx, context } = args
16 await ctx.db.updateSessionJobStatus(context.runId, 'finished')
17 ctx.emitRuntimeJobTerminal({
18 sessionId: context.sessionId,
19 jobId: context.runId,
20 domain: 'edit',
21 status: 'completed'
22 })
23 }
24
25 export async function settleEditJobFailure(args: {
26 ctx: IpcContext
27 context: EditContext
28 error: unknown
29 cancelled: boolean
30 hasPersistedJob: boolean
31 logPrefix: string
32 }): Promise<void> {
33 const { ctx, context, error, cancelled, hasPersistedJob, logPrefix } = args
34 const failure = cancelled ? new Error('生成已取消') : error
35 const message =
36 failure instanceof Error && failure.message.length > 0 ? failure.message : 'Edit generation failed'
37 let terminalStatePersisted = false
38 let finalizationFailed = false
39
40 try {
41 await finalizeGenerationFailure(createGenerationContext(ctx), context, failure)
42 terminalStatePersisted = true
43 } catch (finalizeError) {
44 finalizationFailed = true
45 log.error(`${logPrefix} failed to finalize generation`, {
46 sessionId: context.sessionId,
47 runId: context.runId,
48 message:
49 finalizeError instanceof Error ? finalizeError.message : String(finalizeError || '')
50 })
51 if (hasPersistedJob) {
52 const fallbackResults = await Promise.allSettled([
53 ctx.db.updateGenerationRunStatus(context.runId, 'failed', message),
54 ctx.db.updateSessionStatus(
55 context.sessionId,
56 resolveGenerationFailureSessionStatus(context, cancelled)
57 )
58 ])
59 terminalStatePersisted = fallbackResults.every((result) => result.status === 'fulfilled')
60 if (!terminalStatePersisted) {
61 const fallbackFailure = fallbackResults.find((result) => result.status === 'rejected')
62 log.error(`${logPrefix} failed to persist fallback generation terminal state`, {
63 sessionId: context.sessionId,
64 runId: context.runId,
65 message:
66 fallbackFailure?.status === 'rejected' && fallbackFailure.reason instanceof Error
67 ? fallbackFailure.reason.message
68 : String(fallbackFailure?.status === 'rejected' ? fallbackFailure.reason : '')
69 })
70 }
71 }
72 }
73
74 // A terminal session job is the crash-recovery boundary. Do not hide a run
75 // whose generation/session state could not be finalized.
76 if (!hasPersistedJob || !terminalStatePersisted) return
77 if (finalizationFailed) {
78 ctx.emitGenerateChunk(context.sessionId, {
79 type: 'run_error',
80 payload: { runId: context.runId, message, cancelled }
81 })
82 }
83 let jobStatusPersisted = false
84 try {
85 await ctx.db.updateSessionJobStatus(
86 context.runId,
87 cancelled ? 'aborted' : 'finished',
88 cancelled ? { abortReason: 'cancelled' } : undefined
89 )
90 jobStatusPersisted = true
91 } catch (statusError) {
92 log.error(`${logPrefix} failed to settle session job`, {
93 sessionId: context.sessionId,
94 runId: context.runId,
95 message: statusError instanceof Error ? statusError.message : String(statusError || '')
96 })
97 }
98 if (jobStatusPersisted) {
99 ctx.emitRuntimeJobTerminal({
100 sessionId: context.sessionId,
101 jobId: context.runId,
102 domain: 'edit',
103 status: cancelled ? 'cancelled' : 'failed',
104 errorCode: cancelled ? undefined : 'edit_failed',
105 errorMessage: cancelled
106 ? undefined
107 : message
108 })
109 }
110 }
111
111 lines TYPESCRIPT