# Phase 4: Ollama LLM 연동 (텍스트 다듬기, 번역) ## 목표 Ollama REST API를 통해 로컬 LLM과 연동하여, 전사된 텍스트를 다듬기(polishing)하거나 번역(translate)할 수 있는 상태. 스트리밍 응답을 UI에 실시간 표시하고, 커스텀 명령어의 기본 구조를 갖춘다. ## 태스크 ### 4.1 LocalLLMService 구현 - `src/main/services/LocalLLMService.ts` - Ollama REST API 연동 (`/api/generate`, `/api/chat`, `/api/tags`) - 싱글톤 + EventEmitter 패턴 - 상태 머신: `Unavailable → Available → Generating → Available` (서비스 명세 LLMState 참조) - Ollama 가용성 폴링 (5초 간격, `OLLAMA_CONFIG.pollIntervalMs`) - `availability-changed` 이벤트로 UI에 상태 전파 - ConfigService에서 `llm.serverUrl`, `llm.defaultModel` 읽기 ### 4.2 비스트리밍 생성 (`generate`) - `POST /api/generate` 호출 (stream: false) - GenerateOptions 지원: model, temperature, maxTokens, topP, topK, systemPrompt - GenerateResult 반환: text, model, promptTokens, completionTokens, totalDuration - 타임아웃 처리 (AbortController, 기본 120초) ### 4.3 스트리밍 생성 (`stream`) - `POST /api/generate` (stream: true) → NDJSON 파싱 - fetch + ReadableStream으로 청크 처리 - `token` 이벤트를 통해 점진적 토큰 전달 - AbortController로 중간 취소 지원 - 버퍼 관리: 불완전한 JSON 라인 처리 ### 4.4 텍스트 다듬기 모드 (Polishing) - PostProcessCommand `{ type: 'polish', style?: 'formal' | 'casual' }` 처리 - 시스템 프롬프트 설계: ``` [formal] 다음 음성 전사 텍스트를 자연스럽고 격식 있는 문어체로 다듬어주세요. 원래 의미를 유지하면서 문법 오류를 수정하고, 불필요한 반복이나 필러를 제거하세요. 다듬어진 텍스트만 출력하세요. 설명이나 부가 문구를 붙이지 마세요. [casual] 다음 음성 전사 텍스트를 자연스러운 구어체로 다듬어주세요. 원래 의미와 톤을 유지하면서 문법 오류만 수정하세요. 다듬어진 텍스트만 출력하세요. ``` - VoiceModeService의 Processing 상태에서 호출 ### 4.5 번역 모드 (Translate) - PostProcessCommand `{ type: 'translate', targetLanguage: string }` 처리 - 시스템 프롬프트 설계: ``` 다음 텍스트를 {targetLanguage}로 번역해주세요. 자연스럽고 정확한 번역만 출력하세요. 원문이나 설명을 붙이지 마세요. ``` - 감지 언어 → 대상 언어 자동 전환 (한국어 감지 시 영어로, 영어 감지 시 한국어로) - ConfigService에서 기본 대상 언어 설정 지원 ### 4.6 커스텀 명령어 기본 구조 - PostProcessCommand `{ type: 'custom', prompt: string }` 처리 - 사용자가 직접 시스템 프롬프트를 지정하여 LLM에 전달 - CustomInstruction 인터페이스 초기 정의: ```typescript interface CustomInstruction { id: string; name: string; prompt: string; isBuiltin: boolean; createdAt: number; } ``` - Phase 6에서 완전 구현 (CRUD, 프리셋, 핫키 바인딩) ### 4.7 스트리밍 응답 UI 표시 - RecordingTipWindow에 `processing` 상태 추가 - Thinking 프로그레스 바 (점근 수렴 패턴: `min(95, (1 - 1/(1+1.5*t)) * 100)%`) - 스트리밍 토큰이 도착하면 텍스트 실시간 표시 - ResultPopupWindow에 스트리밍 텍스트 영역 추가 - 토큰 도착 시 점진적 텍스트 추가 - 완료 시 복사 버튼 활성화 - IPC 채널: - `llm:streamToken` (main→renderer, 스트리밍 토큰) - `llm:streamComplete` (main→renderer, 생성 완료) - `llm:streamError` (main→renderer, 에러) ### 4.8 VoiceModeService LLM 통합 - RecognitionState.Processing 상태에서 LLM 호출 - 전사 완료 → postProcessCommand에 따라 분기: - `none`: 바로 텍스트 삽입 - `polish` / `translate` / `custom`: LLM 스트리밍 호출 → 완료 후 텍스트 삽입 - `processing-update` 이벤트를 통해 UI에 진행 상황 전달 - LLM 미가용 시 postProcess를 `none`으로 폴백 + 사용자 알림 ### 4.9 에러 처리 - Ollama 서버 미실행: `LLM_CONNECTION_FAILED` 에러, UI에 "Ollama가 실행 중이 아닙니다" 표시 - 모델 미설치: `LLM_MODEL_NOT_FOUND` 에러, UI에 모델 설치 안내 표시 - 생성 실패/타임아웃: `LLM_GENERATION_FAILED`, 원본 텍스트로 폴백 옵션 제공 - 네트워크 에러: 재시도 없이 즉시 에러 표시 (로컬이므로 재시도 불필요) ### 4.10 IPC 채널 추가 - `llm:getStatus` — LLM 가용 상태 조회 - `llm:getModels` — 설치된 Ollama 모델 목록 - `llm:generate` — 비스트리밍 텍스트 생성 - `llm:stream` — 스트리밍 텍스트 생성 시작 - `llm:cancelStream` — 스트리밍 취소 - `llm:checkConnection` — Ollama 연결 테스트 ## Speakly RE 참조 - GensparkService: 클라우드 LLM 호출 패턴 (D3RO는 로컬 Ollama로 대체) - CustomInstructionService: 명령어 구조, 프롬프트 템플릿 - VoiceModeService: Processing 상태 전이, 후처리 파이프라인 - RecordingTip: thinking 상태 프로그레스 바, 점근 수렴 패턴 ## 완료 조건 - [ ] Ollama 가용성 자동 감지 및 UI 표시 - [ ] 전사된 텍스트를 다듬기(polish) 모드로 LLM 처리 가능 - [ ] 전사된 텍스트를 번역(translate) 모드로 LLM 처리 가능 - [ ] 커스텀 프롬프트로 자유 LLM 처리 가능 - [ ] 스트리밍 토큰이 UI에 실시간 표시됨 - [ ] Ollama 미실행/모델 미설치 시 적절한 에러 메시지 표시 - [ ] LLM 미가용 시 원본 텍스트 삽입으로 폴백