feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped

This commit is contained in:
Yun Chan 2026-08-20 11:12:05 +09:00
parent 5cd1de6859
commit 708e20f747
406 changed files with 42464 additions and 6199 deletions

View file

@ -1,11 +1,11 @@
// src/main/services/PremiumLLMService.ts
// Phase 3.2: Anthropic Claude 프리미엄 LLM 서비스.
// LocalLLMService와 같은 인터페이스를 제공하되, 내부적으로는
// 내부적으로는 온라인 API를 호출한다.
// Supabase Edge Function(`llm-proxy`)을 경유해 Claude Messages API를 호출.
//
// 특징:
// - 싱글톤 + EventEmitter (설계서 01 패턴)
// - processText(), chatStream() — LocalLLMService와 시그니처 동일
// - processText(), chatStream() — 시그니처 동일
// - 네트워크 실패 / 401 / 429 / 5xx 감지 시 에러 throw → LLMRouterService가 local로 fallback
// - quota-warning / upgrade-required / fallback-triggered 이벤트 emit
@ -124,7 +124,7 @@ class PremiumLLMService extends EventEmitter {
}
/**
* LocalLLMService.processText .
* .
* Phase 3.2 MVP는 (stream=false).
*/
async processText(
@ -148,16 +148,10 @@ class PremiumLLMService extends EventEmitter {
const response = await this._invokeProxy(body)
// Claude 응답 → text 추출
const firstBlock = response.content?.[0]
if (!firstBlock || firstBlock.type !== 'text') {
logger.warn('Premium LLM returned empty content — falling back to original')
return text
if (!firstBlock || firstBlock.type !== 'text' || !firstBlock.text.trim()) {
throw new D3ROError(ErrorCode.LLMProcessingFailed, 'Premium LLM returned empty content')
}
const result = firstBlock.text.trim()
if (result.length === 0) {
logger.warn('Premium LLM returned empty text — falling back to original')
return text
}
return result
return firstBlock.text.trim()
} catch (err) {
// LLMRouter가 local fallback 처리. 여기서는 에러 전파.
if (err instanceof D3ROError) throw err
@ -169,7 +163,7 @@ class PremiumLLMService extends EventEmitter {
}
/**
* (Voice Conversation용) LocalLLMService.chatStream .
* (Voice Conversation용).
* SSE 스트리밍: llm-proxy에 stream=true로 , Anthropic SSE를 yield.
*/
async *chatStream(
@ -229,6 +223,27 @@ class PremiumLLMService extends EventEmitter {
return accumulated
}
/**
* // . processText .
*/
async generate(
text: string,
options?: { systemPrompt?: string; temperature?: number; maxTokens?: number },
): Promise<{ text: string }> {
this._ensureAuth()
const response = await this._invokeProxy({
messages: [{ role: 'user', content: text }],
system: options?.systemPrompt,
max_tokens: options?.maxTokens ?? 2048,
stream: false,
})
const firstBlock = response.content?.[0]
if (!firstBlock || firstBlock.type !== 'text' || !firstBlock.text.trim()) {
throw new D3ROError(ErrorCode.LLMProcessingFailed, 'Premium LLM generate returned empty content')
}
return { text: firstBlock.text.trim() }
}
cancelGeneration(): void {
if (this._abortController) {
this._abortController.abort()
@ -302,6 +317,11 @@ class PremiumLLMService extends EventEmitter {
let _instance: PremiumLLMService | null = null
export function resetPremiumLLMServiceForTests(): void {
if (_instance) _instance.removeAllListeners()
_instance = null
}
export function getPremiumLLMService(): PremiumLLMService {
if (!_instance) {
_instance = new PremiumLLMService()