| 22 | } |
| 23 | |
| 24 | class AIHandler { |
| 25 | private abortControllers = new Map<string, AbortController>() |
| 26 | private requestStates = new Map<string, RequestState>() |
| 27 | |
| 28 | private initRequestState( |
| 29 | requestId: string, |
| 30 | event: Electron.IpcMainInvokeEvent, |
| 31 | eventChannel: string |
| 32 | ): void { |
| 33 | this.requestStates.set(requestId, { |
| 34 | event, |
| 35 | eventChannel, |
| 36 | fullResponse: '', |
| 37 | fullReasoning: '', |
| 38 | status: 'running' |
| 39 | }) |
| 40 | } |
| 41 | |
| 42 | private cleanupRequest(requestId: string): void { |
| 43 | this.abortControllers.delete(requestId) |
| 44 | this.requestStates.delete(requestId) |
| 45 | } |
| 46 | |
| 47 | private emitChunk(requestId: string, content: string): void { |
| 48 | const state = this.requestStates.get(requestId) |
| 49 | if (!state || state.status !== 'running') { |
| 50 | return |
| 51 | } |
| 52 | |
| 53 | state.fullResponse += content |
| 54 | state.event.sender.send(state.eventChannel, { |
| 55 | requestId, |
| 56 | type: 'chunk', |
| 57 | content |
| 58 | } as AIStreamChunk) |
| 59 | } |
| 60 | |
| 61 | private emitReasoning(requestId: string, reasoning: string): void { |
| 62 | const state = this.requestStates.get(requestId) |
| 63 | if (!state || state.status !== 'running') { |
| 64 | return |
| 65 | } |
| 66 | |
| 67 | state.fullReasoning += reasoning |
| 68 | state.event.sender.send(state.eventChannel, { |
| 69 | requestId, |
| 70 | type: 'reasoning_content', |
| 71 | reasoning_content: reasoning |
| 72 | } as AIStreamChunk) |
| 73 | } |
| 74 | |
| 75 | private finalizeComplete(requestId: string): void { |
| 76 | const state = this.requestStates.get(requestId) |
| 77 | if (!state || (state.status !== 'running' && state.status !== 'aborted')) { |
| 78 | return |
| 79 | } |
| 80 | |
| 81 | state.status = 'completed' |
nothing calls this directly
no outgoing calls
no test coverage detected