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
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:
parent
5cd1de6859
commit
708e20f747
406 changed files with 42464 additions and 6199 deletions
163
apps/desktop/src/main/services/OnlineLLMService.ts
Normal file
163
apps/desktop/src/main/services/OnlineLLMService.ts
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// src/main/services/OnlineLLMService.ts
|
||||
// C# .NET Backend API Server 연결 서비스.
|
||||
// 온라인 모드 사용 시 필수 인증(JWT Bearer)을 통해 서버로 AI 요청 전달.
|
||||
|
||||
import { EventEmitter } from 'events'
|
||||
import { getLogger } from './LoggerService'
|
||||
import { configGet, configSet } from './ConfigService'
|
||||
import { D3ROError, ErrorCode } from '@d3ro/core/errors'
|
||||
import type { LLMAction } from '@d3ro/core/types'
|
||||
import { resolveSystemPrompt } from './llm-prompts'
|
||||
|
||||
const logger = getLogger('OnlineLLMService')
|
||||
|
||||
interface OnlineGenerateOptions {
|
||||
model?: string
|
||||
temperature?: number
|
||||
maxTokens?: number
|
||||
systemPrompt?: string
|
||||
}
|
||||
|
||||
interface OnlineGenerateResponse {
|
||||
text: string
|
||||
model: string
|
||||
promptTokens: number
|
||||
completionTokens: number
|
||||
totalDurationMs: number
|
||||
cost: number
|
||||
}
|
||||
|
||||
class OnlineLLMService extends EventEmitter {
|
||||
private _abortController: AbortController | null = null
|
||||
private _disposed = false
|
||||
|
||||
isAvailable(): boolean {
|
||||
const token = configGet('authToken')
|
||||
return Boolean(token && token.length > 0)
|
||||
}
|
||||
|
||||
private _ensureAuth(): string {
|
||||
if (this._disposed) {
|
||||
throw new D3ROError(ErrorCode.LLMProcessingFailed, 'OnlineLLMService disposed')
|
||||
}
|
||||
const token = configGet('authToken')
|
||||
if (!token || token.length === 0) {
|
||||
throw new D3ROError(
|
||||
ErrorCode.LLMServerUnreachable,
|
||||
'온라인 모드를 이용하기 위해서는 로그인이 꼭 필요합니다.'
|
||||
)
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
async processText(
|
||||
text: string,
|
||||
action: LLMAction,
|
||||
targetLanguage?: string,
|
||||
customPrompt?: string
|
||||
): Promise<string> {
|
||||
const token = this._ensureAuth()
|
||||
const apiUrl = configGet('onlineApiUrl') ?? 'http://localhost:5000'
|
||||
const systemPrompt = resolveSystemPrompt(action, targetLanguage, customPrompt)
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiUrl}/api/llm/generate`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompt: text,
|
||||
systemPrompt,
|
||||
model: configGet('llmModelId') ?? 'd3ro-gpt4o-mini',
|
||||
temperature: 0.7,
|
||||
maxTokens: 2048
|
||||
})
|
||||
})
|
||||
|
||||
if (response.status === 401) {
|
||||
configSet('authToken', null)
|
||||
throw new D3ROError(
|
||||
ErrorCode.LLMServerUnreachable,
|
||||
'인증 토큰이 만료되었습니다. 다시 로그인해주세요.'
|
||||
)
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new D3ROError(
|
||||
ErrorCode.LLMProcessingFailed,
|
||||
`API Server responded with status ${response.status}`
|
||||
)
|
||||
}
|
||||
|
||||
const data = (await response.json()) as OnlineGenerateResponse
|
||||
if (!data.text || !data.text.trim()) {
|
||||
throw new D3ROError(ErrorCode.LLMProcessingFailed, 'Online LLM returned empty text')
|
||||
}
|
||||
return data.text.trim()
|
||||
} catch (err) {
|
||||
if (err instanceof D3ROError) throw err
|
||||
throw new D3ROError(
|
||||
ErrorCode.LLMProcessingFailed,
|
||||
`온라인 API 서버 연결 실패: ${err instanceof Error ? err.message : String(err)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async *chatStream(
|
||||
messages: Array<{ role: string; content: string }>,
|
||||
options?: { model?: string; temperature?: number }
|
||||
): AsyncGenerator<string, string> {
|
||||
const token = this._ensureAuth()
|
||||
const apiUrl = configGet('onlineApiUrl') ?? 'http://localhost:5000'
|
||||
|
||||
const response = await fetch(`${apiUrl}/api/llm/chat`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messages: messages.map((m) => ({ role: m.role, content: m.content })),
|
||||
model: options?.model ?? configGet('llmModelId') ?? 'd3ro-gpt4o-mini',
|
||||
temperature: options?.temperature ?? 0.7
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new D3ROError(ErrorCode.LLMProcessingFailed, `Online API Error: ${response.status}`)
|
||||
}
|
||||
|
||||
const data = (await response.json()) as OnlineGenerateResponse
|
||||
yield data.text
|
||||
return data.text
|
||||
}
|
||||
|
||||
cancelGeneration(): void {
|
||||
if (this._abortController) {
|
||||
this._abortController.abort()
|
||||
this._abortController = null
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposed = true
|
||||
this.cancelGeneration()
|
||||
this.removeAllListeners()
|
||||
}
|
||||
}
|
||||
|
||||
let instance: OnlineLLMService | null = null
|
||||
|
||||
export function resetOnlineLLMServiceForTests(): void {
|
||||
if (instance) instance.removeAllListeners()
|
||||
instance = null
|
||||
}
|
||||
|
||||
export function getOnlineLLMService(): OnlineLLMService {
|
||||
if (!instance) {
|
||||
instance = new OnlineLLMService()
|
||||
}
|
||||
return instance
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue