Phase 4 구현: Ollama LLM 연동 (다듬기, 번역, 스트리밍)
- LocalLLMService: Ollama REST API, 스트리밍 NDJSON 파싱, 5초 가용성 폴링 - 시스템 프롬프트: refine/translate/summarize/grammar/expand/custom 6개 액션 - VoiceModeService: 전사→LLM 후처리→텍스트 삽입, LLM 실패 시 원본 폴백 - LLM IPC 핸들러: status/models/process/cancel/serverUrl 8개 - StatusBar: Ollama 연결 상태 + 활성 모델 표시 - Preload: llm API 섹션 추가 - Bootstrap: llm-polling 초기화 단계 추가
This commit is contained in:
parent
517210af2f
commit
4a5cf6c819
10 changed files with 663 additions and 9 deletions
|
|
@ -13,6 +13,7 @@ import { getHotkeyService } from './HotkeyService'
|
|||
import type { HotkeyConfig } from './HotkeyService'
|
||||
import { configGet } from './ConfigService'
|
||||
import { getTextInsertService } from './TextInsertService'
|
||||
import { getLocalLLMService } from './LocalLLMService'
|
||||
import { D3ROError, ErrorCode } from '@shared/errors'
|
||||
import { TIMING } from '@shared/constants'
|
||||
import { RecognitionState, AudioState } from '@shared/types'
|
||||
|
|
@ -407,8 +408,14 @@ class VoiceModeService extends EventEmitter {
|
|||
|
||||
this.emit('transcription-update', { text: result.text, isFinal: true })
|
||||
|
||||
// Phase 2: LLM 후처리 없이 바로 완료
|
||||
this._completeSession(result.text)
|
||||
// LLM 후처리
|
||||
const llmAction = configGet('defaultLLMAction')
|
||||
if (llmAction !== 'refine' || !getLocalLLMService().isAvailable()) {
|
||||
// LLM 미가용이거나 기본 액션이면 원본 텍스트로 완료
|
||||
this._completeSession(result.text)
|
||||
} else {
|
||||
await this._processWithLLM(result.text)
|
||||
}
|
||||
} catch (error) {
|
||||
if (this._isInTerminalState()) return
|
||||
this._handleError(
|
||||
|
|
@ -420,6 +427,35 @@ class VoiceModeService extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
// ── LLM 후처리 ─────────────────────────────────────────
|
||||
|
||||
private async _processWithLLM(transcribedText: string): Promise<void> {
|
||||
if (this._isInTerminalState()) return
|
||||
|
||||
// RECOGNIZING 상태 유지 (UI에서 thinking으로 표시됨)
|
||||
try {
|
||||
const llm = getLocalLLMService()
|
||||
const action = configGet('defaultLLMAction')
|
||||
|
||||
logger.info(`Processing with LLM (action: ${action})`)
|
||||
|
||||
const processedText = await llm.processText(transcribedText, action)
|
||||
|
||||
if (this._isInTerminalState()) return
|
||||
|
||||
if (this._session) {
|
||||
this._session.processedText = processedText
|
||||
}
|
||||
|
||||
this._completeSession(processedText)
|
||||
} catch (error) {
|
||||
if (this._isInTerminalState()) return
|
||||
logger.warn(`LLM processing failed, using original text: ${error instanceof Error ? error.message : String(error)}`)
|
||||
// LLM 실패 시 원본 텍스트로 폴백
|
||||
this._completeSession(transcribedText)
|
||||
}
|
||||
}
|
||||
|
||||
// ── 세션 완료/취소 ─────────────────────────────────────
|
||||
|
||||
private async _completeSession(finalText: string): Promise<void> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue