d3ro-voice/apps/desktop/src/main/ipc/llm-handlers.ts
Yun Chan 5b6e7aae6b refactor(main): ipcSuccess/ipcError 헬퍼 통일 + catch(error) 패턴 (WS-PATTERN)
- cloud-sync-handlers 수동 ok/fail -> ipcSuccess/ipcError 헬퍼 (6 핸들러)
- catch {} -> catch(error) 에러 메시지 보강 32건 (11 ipc handler 파일)
KEEP: template-handlers(주석 명시 에러 무시), license-handlers(이미 헬퍼 사용),
       audio-handlers stop().catch(() => {})(의도적 무시)
정책: docs/REFACTOR_POLICY.md DP2
2026-07-22 02:38:23 +09:00

129 lines
4.5 KiB
TypeScript

// src/main/ipc/llm-handlers.ts
import { ipcMain } from 'electron'
import { IPC_CHANNELS } from '@d3ro/core/ipc-channels'
import { ipcSuccess, ipcError, ErrorCode } from '@d3ro/core/errors'
import { getLocalLLMService } from '../services/LocalLLMService'
import { getPremiumLLMService } from '../services/PremiumLLMService'
import { getVoiceModeService } from '../services/VoiceModeService'
import { configGet, configSet } from '../services/ConfigService'
import { getMainWindow } from '../windows/WindowManager'
import type { SetLLMModelParams, SetServerUrlParams, LLMProcessParams } from '@d3ro/core/types'
function safeSendToRenderer(channel: string, data: unknown): void {
const win = getMainWindow()
if (win && !win.isDestroyed()) {
win.webContents.send(channel, data)
}
}
export function registerLLMHandlers(): void {
// LLM 가용성 변경 시 렌더러에 상태 전파
const llm = getLocalLLMService()
llm.on('availability-changed', () => {
safeSendToRenderer(IPC_CHANNELS.LLM.STATUS_CHANGED, { status: llm.getStatus() })
})
// Pull 진행률 → 렌더러
llm.on('pull-progress', (payload: unknown) => {
safeSendToRenderer(IPC_CHANNELS.LLM.PULL_PROGRESS, payload)
})
ipcMain.handle(IPC_CHANNELS.LLM.PULL_MODEL, async (_event, params: { modelId: string }) => {
try {
await getLocalLLMService().pullModel(params.modelId)
return ipcSuccess(undefined)
} catch (err) {
const msg = err instanceof Error ? err.message : String(err)
return ipcError(ErrorCode.LLMServerUnreachable, `Pull 실패: ${msg}`)
}
})
// Phase 3.2: VoiceModeService의 premium-llm-fallback 이벤트를 렌더러로 전달
getVoiceModeService().on('premium-llm-fallback', (payload: { reason: string }) => {
safeSendToRenderer(IPC_CHANNELS.LLM.PREMIUM_FALLBACK, payload)
})
// Phase 3.2: PremiumLLMService 이벤트 → 렌더러
const premium = getPremiumLLMService()
premium.on('quota-warning', (payload) => {
safeSendToRenderer(IPC_CHANNELS.LLM.PREMIUM_QUOTA_WARNING, payload)
})
premium.on('upgrade-required', (payload) => {
safeSendToRenderer(IPC_CHANNELS.LLM.PREMIUM_UPGRADE_REQUIRED, payload)
})
ipcMain.handle(IPC_CHANNELS.LLM.GET_STATUS, async () => {
return ipcSuccess(getLocalLLMService().getStatus())
})
ipcMain.handle(IPC_CHANNELS.LLM.GET_MODELS, async () => {
try {
const models = await getLocalLLMService().getModels()
return ipcSuccess(models)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return ipcError(ErrorCode.LLMServerUnreachable, `Failed to get models: ${message}`)
}
})
ipcMain.handle(IPC_CHANNELS.LLM.GET_ACTIVE_MODEL, async () => {
return ipcSuccess(configGet('llmModelId'))
})
ipcMain.handle(IPC_CHANNELS.LLM.SET_MODEL, async (_event, params: SetLLMModelParams) => {
configSet('llmModelId', params.modelId)
return ipcSuccess(undefined)
})
ipcMain.handle(IPC_CHANNELS.LLM.PROCESS, async (_event, params: LLMProcessParams) => {
try {
const llm = getLocalLLMService()
const start = performance.now()
const processedText = await llm.processText(
params.text,
params.action,
params.targetLanguage,
params.customPrompt
)
return ipcSuccess({
originalText: params.text,
processedText,
action: params.action,
processingTimeMs: Math.round(performance.now() - start),
tokenCount: 0
})
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return ipcError(ErrorCode.LLMProcessingFailed, `LLM processing failed: ${message}`)
}
})
ipcMain.handle(IPC_CHANNELS.LLM.CANCEL_PROCESS, async () => {
getLocalLLMService().cancelGeneration()
return ipcSuccess(undefined)
})
ipcMain.handle(IPC_CHANNELS.LLM.GET_SERVER_URL, async () => {
return ipcSuccess(configGet('ollamaServerUrl'))
})
ipcMain.handle(IPC_CHANNELS.LLM.SET_SERVER_URL, async (_event, params: SetServerUrlParams) => {
configSet('ollamaServerUrl', params.url)
return ipcSuccess(undefined)
})
// Phase 3.2: Premium LLM 상태/쿼터 조회
ipcMain.handle(IPC_CHANNELS.LLM.PREMIUM_GET_STATUS, async () => {
const premium = getPremiumLLMService()
return ipcSuccess({
available: premium.isAvailable(),
backend: configGet('llmBackend')
})
})
ipcMain.handle(IPC_CHANNELS.LLM.PREMIUM_GET_QUOTA, async () => {
const premium = getPremiumLLMService()
const snapshot = premium.getLastQuota()
return ipcSuccess(snapshot)
})
}