- 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
129 lines
4.5 KiB
TypeScript
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)
|
|
})
|
|
}
|