Phase 12~13 전체 구현: Pro+ 피처 6종 + 음성 대화 + RAG + OS 자동화
Phase 12: - FileTranscriptionService: ffmpeg PCM 변환 + 30초 청크 순차 STT - MeetingSummaryService: 자막 세션 → LLM 자동 요약 + DB summaryText - DictationTemplateService: 필드별 음성 입력 상태 머신 + 프리셋 3개 Phase 13.1: - VoiceConversationService: STT→Ollama /api/chat→TTS 대화 루프 (10턴) - TTSPlaybackService: Windows SAPI 문장 단위 큐 재생 - LocalLLMService.chatStream: Ollama /api/chat 스트리밍 Phase 13.2: - RAGService: Ollama 임베딩 + SQLite 벡터 + 코사인 유사도 검색 - KnowledgeBasePage: 문서 관리 + 질문/답변 UI - PDF 파서: zlib FlateDecode 해제 + BT/ET 텍스트 추출 Phase 13.3: - VoiceActionService: LLM JSON 액션 플랜 생성 + 실행 - 프리셋 6개 (크롬/메모장/탐색기/볼륨), 위험 명령 차단 공통: IPC ~70채널, 에러코드 780-878, i18n 100+키 버그픽스: 라이선스 로컬 키 우선, i18n featureLabel, DOM 중첩
This commit is contained in:
parent
a31f96bbb8
commit
eb83682269
38 changed files with 5678 additions and 19 deletions
|
|
@ -95,6 +95,47 @@ import type {
|
|||
UsageQuota,
|
||||
UpgradePromptEvent,
|
||||
TierComparison,
|
||||
// Phase 12
|
||||
FileTranscriptionStartParams,
|
||||
// Phase 13.1
|
||||
ConversationSessionInfo,
|
||||
ConversationMessage,
|
||||
ConversationSendParams,
|
||||
ConversationAssistantDelta,
|
||||
ConversationAssistantMessage,
|
||||
ConversationError,
|
||||
// Phase 13.2
|
||||
RAGDocument,
|
||||
RAGQueryParams,
|
||||
RAGQueryResult,
|
||||
RAGStateInfo,
|
||||
RAGIndexProgress,
|
||||
RAGAddDocumentParams,
|
||||
RAGRemoveDocumentParams,
|
||||
// Phase 13.3
|
||||
VoiceActionExecuteParams,
|
||||
VoiceActionPreset,
|
||||
VoiceActionHistoryEntry,
|
||||
VoiceActionPlannedEvent,
|
||||
VoiceActionExecutedEvent,
|
||||
VoiceActionErrorEvent,
|
||||
FileTranscriptionProgress,
|
||||
FileTranscriptionResult,
|
||||
FileTranscriptionStateInfo,
|
||||
MeetingSummarizeParams,
|
||||
MeetingSummaryResult,
|
||||
MeetingSummaryGetParams,
|
||||
MeetingSummaryExportParams,
|
||||
MeetingSummaryProgress,
|
||||
DictationTemplate,
|
||||
CreateTemplateParams,
|
||||
UpdateTemplateParams,
|
||||
DeleteTemplateParams,
|
||||
StartTemplateSessionParams,
|
||||
SetFieldValueParams,
|
||||
TemplateSessionInfo,
|
||||
TemplateFieldCompletedEvent,
|
||||
TemplateSessionCompletedEvent,
|
||||
} from '@shared/types'
|
||||
import { Feature } from '@shared/types'
|
||||
import type { IPCResult } from '@shared/errors'
|
||||
|
|
@ -382,6 +423,136 @@ const electronAPI = {
|
|||
onTierChanged: (cb: (e: LicenseInfo) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.LICENSE.TIER_CHANGED, cb),
|
||||
},
|
||||
// ── File Transcription (Phase 12.1) ────────────────────
|
||||
fileTranscription: {
|
||||
start: (params: FileTranscriptionStartParams) =>
|
||||
invoke<FileTranscriptionResult>(IPC_CHANNELS.FILE_TRANSCRIPTION.START, params),
|
||||
cancel: () =>
|
||||
invoke<void>(IPC_CHANNELS.FILE_TRANSCRIPTION.CANCEL),
|
||||
getState: () =>
|
||||
invoke<FileTranscriptionStateInfo>(IPC_CHANNELS.FILE_TRANSCRIPTION.GET_STATE),
|
||||
onProgress: (cb: (data: FileTranscriptionProgress) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.FILE_TRANSCRIPTION.PROGRESS, cb),
|
||||
onComplete: (cb: (data: FileTranscriptionResult) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.FILE_TRANSCRIPTION.COMPLETE, cb),
|
||||
onError: (cb: (data: { message: string }) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.FILE_TRANSCRIPTION.ERROR, cb),
|
||||
},
|
||||
|
||||
// ── Meeting Summary (Phase 12.2) ─────────────────────
|
||||
meetingSummary: {
|
||||
summarize: (params: MeetingSummarizeParams) =>
|
||||
invoke<MeetingSummaryResult>(IPC_CHANNELS.MEETING_SUMMARY.SUMMARIZE, params),
|
||||
getSummary: (params: MeetingSummaryGetParams) =>
|
||||
invoke<MeetingSummaryResult | null>(IPC_CHANNELS.MEETING_SUMMARY.GET_SUMMARY, params),
|
||||
exportMarkdown: (params: MeetingSummaryExportParams) =>
|
||||
invoke<string>(IPC_CHANNELS.MEETING_SUMMARY.EXPORT_MARKDOWN, params),
|
||||
onSummaryReady: (cb: (data: MeetingSummaryResult) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.MEETING_SUMMARY.SUMMARY_READY, cb),
|
||||
onProgress: (cb: (data: MeetingSummaryProgress) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.MEETING_SUMMARY.SUMMARY_PROGRESS, cb),
|
||||
},
|
||||
|
||||
// ── Dictation Templates (Phase 12.3) ─────────────────
|
||||
dictationTemplate: {
|
||||
getAll: () =>
|
||||
invoke<DictationTemplate[]>(IPC_CHANNELS.DICTATION_TEMPLATE.GET_ALL),
|
||||
create: (params: CreateTemplateParams) =>
|
||||
invoke<DictationTemplate>(IPC_CHANNELS.DICTATION_TEMPLATE.CREATE, params),
|
||||
update: (params: UpdateTemplateParams) =>
|
||||
invoke<DictationTemplate>(IPC_CHANNELS.DICTATION_TEMPLATE.UPDATE, params),
|
||||
delete: (params: DeleteTemplateParams) =>
|
||||
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.DELETE, params),
|
||||
startSession: (params: StartTemplateSessionParams) =>
|
||||
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.START_SESSION, params),
|
||||
cancelSession: () =>
|
||||
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.CANCEL_SESSION),
|
||||
getSessionState: () =>
|
||||
invoke<TemplateSessionInfo | null>(IPC_CHANNELS.DICTATION_TEMPLATE.GET_SESSION_STATE),
|
||||
setFieldValue: (params: SetFieldValueParams) =>
|
||||
invoke<void>(IPC_CHANNELS.DICTATION_TEMPLATE.SET_FIELD_VALUE, params),
|
||||
onSessionStateChanged: (cb: (data: TemplateSessionInfo) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.DICTATION_TEMPLATE.SESSION_STATE_CHANGED, cb),
|
||||
onFieldCompleted: (cb: (data: TemplateFieldCompletedEvent) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.DICTATION_TEMPLATE.FIELD_COMPLETED, cb),
|
||||
onSessionCompleted: (cb: (data: TemplateSessionCompletedEvent) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.DICTATION_TEMPLATE.SESSION_COMPLETED, cb),
|
||||
},
|
||||
// ── RAG (Phase 13.2) ──────────────────────────────────
|
||||
rag: {
|
||||
addDocument: (params?: RAGAddDocumentParams) =>
|
||||
invoke<RAGDocument>(IPC_CHANNELS.RAG.ADD_DOCUMENT, params ?? {}),
|
||||
removeDocument: (params: RAGRemoveDocumentParams) =>
|
||||
invoke<void>(IPC_CHANNELS.RAG.REMOVE_DOCUMENT, params),
|
||||
getDocuments: () =>
|
||||
invoke<RAGDocument[]>(IPC_CHANNELS.RAG.GET_DOCUMENTS),
|
||||
query: (params: RAGQueryParams) =>
|
||||
invoke<RAGQueryResult>(IPC_CHANNELS.RAG.QUERY, params),
|
||||
getState: () =>
|
||||
invoke<RAGStateInfo>(IPC_CHANNELS.RAG.GET_STATE),
|
||||
reindex: (documentId: string) =>
|
||||
invoke<void>(IPC_CHANNELS.RAG.REINDEX, { documentId }),
|
||||
onIndexProgress: (cb: (data: RAGIndexProgress) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.RAG.INDEX_PROGRESS, cb),
|
||||
onIndexComplete: (cb: (data: { documentId: string; fileName: string }) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.RAG.INDEX_COMPLETE, cb),
|
||||
},
|
||||
|
||||
// ── Voice Action (Phase 13.3) ────────────────────────
|
||||
voiceAction: {
|
||||
execute: (params: VoiceActionExecuteParams) =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_ACTION.EXECUTE, params),
|
||||
getPresets: () =>
|
||||
invoke<VoiceActionPreset[]>(IPC_CHANNELS.VOICE_ACTION.GET_PRESETS),
|
||||
getHistory: () =>
|
||||
invoke<VoiceActionHistoryEntry[]>(IPC_CHANNELS.VOICE_ACTION.GET_HISTORY),
|
||||
clearHistory: () =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_ACTION.CLEAR_HISTORY),
|
||||
setEnabled: (enabled: boolean) =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_ACTION.SET_ENABLED, { enabled }),
|
||||
isEnabled: () =>
|
||||
invoke<boolean>(IPC_CHANNELS.VOICE_ACTION.IS_ENABLED),
|
||||
onActionPlanned: (cb: (data: VoiceActionPlannedEvent) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_ACTION.ACTION_PLANNED, cb),
|
||||
onActionExecuted: (cb: (data: VoiceActionExecutedEvent) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_ACTION.ACTION_EXECUTED, cb),
|
||||
onActionError: (cb: (data: VoiceActionErrorEvent) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_ACTION.ACTION_ERROR, cb),
|
||||
},
|
||||
|
||||
// ── Voice Conversation (Phase 13.1) ───────────────────
|
||||
voiceConversation: {
|
||||
startSession: () =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.START_SESSION),
|
||||
stopSession: () =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.STOP_SESSION),
|
||||
sendMessage: (params: ConversationSendParams) =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.SEND_MESSAGE, params),
|
||||
getState: () =>
|
||||
invoke<ConversationSessionInfo>(IPC_CHANNELS.VOICE_CONVERSATION.GET_STATE),
|
||||
getHistory: () =>
|
||||
invoke<ConversationMessage[]>(IPC_CHANNELS.VOICE_CONVERSATION.GET_HISTORY),
|
||||
clearHistory: () =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.CLEAR_HISTORY),
|
||||
cancelResponse: () =>
|
||||
invoke<void>(IPC_CHANNELS.VOICE_CONVERSATION.CANCEL_RESPONSE),
|
||||
finishListening: () =>
|
||||
invoke<void>('voiceConversation:finishListening'),
|
||||
onStateChanged: (cb: (data: ConversationSessionInfo) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_CONVERSATION.STATE_CHANGED, cb),
|
||||
onUserMessage: (cb: (data: ConversationMessage) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_CONVERSATION.USER_MESSAGE, cb),
|
||||
onAssistantDelta: (cb: (data: ConversationAssistantDelta) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_CONVERSATION.ASSISTANT_DELTA, cb),
|
||||
onAssistantMessage: (cb: (data: ConversationAssistantMessage) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_CONVERSATION.ASSISTANT_MESSAGE, cb),
|
||||
onTTSStarted: (cb: () => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_CONVERSATION.TTS_STARTED, cb),
|
||||
onTTSFinished: (cb: () => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_CONVERSATION.TTS_FINISHED, cb),
|
||||
onError: (cb: (data: ConversationError) => void): Unsubscribe =>
|
||||
on(IPC_CHANNELS.VOICE_CONVERSATION.ERROR, cb),
|
||||
},
|
||||
} as const
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', electronAPI)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue