/** * LLM(AI 검증) 앱 설정 (gc_app_settings.llm_settings_json) */ export interface LlmAppSettings { /** LLM AI 검증 사용 여부 */ enabled: boolean; /** LLM 서버 호스트 (IP 또는 도메인) */ host: string; /** LLM 서버 포트 */ port: number; /** HTTPS 사용 여부 */ useHttps: boolean; /** OpenAI 호환 chat completions 경로 */ chatPath: string; /** 모델 ID */ model: string; /** 최대 생성 토큰 */ maxTokens: number; /** temperature (0~2) */ temperature: number; /** 요청 타임아웃 (ms) */ timeoutMs: number; } export const DEFAULT_LLM_APP_SETTINGS: LlmAppSettings = { enabled: true, host: '127.0.0.1', port: 16000, useHttps: false, chatPath: '/v1/chat/completions', model: 'mlx-community/Qwen2.5-7B-Instruct-4bit', maxTokens: 1024, temperature: 0.1, timeoutMs: 120_000, }; function clampInt(v: unknown, min: number, max: number, fallback: number): number { const n = Number(v); if (!Number.isFinite(n)) return fallback; return Math.max(min, Math.min(max, Math.round(n))); } function clampFloat(v: unknown, min: number, max: number, fallback: number): number { const n = Number(v); if (!Number.isFinite(n)) return fallback; return Math.max(min, Math.min(max, n)); } export function resolveLlmAppSettings( raw?: Partial | null, ): LlmAppSettings { const d = DEFAULT_LLM_APP_SETTINGS; if (!raw || typeof raw !== 'object') return { ...d }; const host = typeof raw.host === 'string' && raw.host.trim() ? raw.host.trim() : d.host; const chatPath = typeof raw.chatPath === 'string' && raw.chatPath.trim() ? (raw.chatPath.startsWith('/') ? raw.chatPath.trim() : `/${raw.chatPath.trim()}`) : d.chatPath; const model = typeof raw.model === 'string' && raw.model.trim() ? raw.model.trim() : d.model; return { enabled: raw.enabled === undefined ? d.enabled : Boolean(raw.enabled), host, port: clampInt(raw.port, 1, 65535, d.port), useHttps: raw.useHttps === undefined ? d.useHttps : Boolean(raw.useHttps), chatPath, model, maxTokens: clampInt(raw.maxTokens, 64, 8192, d.maxTokens), temperature: clampFloat(raw.temperature, 0, 2, d.temperature), timeoutMs: clampInt(raw.timeoutMs, 5_000, 600_000, d.timeoutMs), }; } /** 설정 UI용 — host·port·프로토콜로 base URL 미리보기 */ export function buildLlmBaseUrl(settings: LlmAppSettings): string { const scheme = settings.useHttps ? 'https' : 'http'; const omitPort = (settings.useHttps && settings.port === 443) || (!settings.useHttps && settings.port === 80); if (omitPort) return `${scheme}://${settings.host}`; return `${scheme}://${settings.host}:${settings.port}`; } export function buildLlmEndpointUrl(settings: LlmAppSettings): string { return `${buildLlmBaseUrl(settings)}${settings.chatPath}`; }