UNPKG

hua-sdk-lite

Version:

HUA Lite API SDK - Emotional AI Chatbot Integration

243 lines (242 loc) β€’ 9.66 kB
"use strict"; // 🌟 HUA SDK Lite v2.0.0 - API ν΄λΌμ΄μ–ΈνŠΈ // λ¦¬λ“¬μ΄μ˜ 마음처럼 μ‚¬μš©μžλ₯Ό λ°°λ €ν•˜λŠ” μ„¬μ„Έν•œ ν΄λΌμ΄μ–ΈνŠΈ πŸ’– Object.defineProperty(exports, "__esModule", { value: true }); exports.HUALiteClient = void 0; const types_1 = require("./types"); const errors_1 = require("./errors"); // ===== API ν΄λΌμ΄μ–ΈνŠΈ 클래슀 ===== class HUALiteClient { constructor(apiKey, config = {}) { this.eventListeners = new Map(); if (!apiKey || apiKey.trim().length === 0) { throw new errors_1.MissingApiKeyError(); } // API ν‚€ ν˜•μ‹ 검증 μΆ”κ°€ const trimmedKey = apiKey.trim(); if (trimmedKey.length < 10) { throw new errors_1.ValidationError(`API ν‚€λŠ” μ΅œμ†Œ 10자 이상이어야 ν•©λ‹ˆλ‹€. ν˜„μž¬ 길이: ${trimmedKey.length}`); } // API ν‚€ ν˜•μ‹ 검증 // 게슀트 ν‚€: 64자리 랜덀 λ¬Έμžμ—΄ // νšŒμ›κ°€μž… ν‚€: hua_ + 랜덀 λ¬Έμžμ—΄ const guestKeyPattern = /^[A-Za-z0-9\-_]{64}$/; const userKeyPattern = /^hua_[A-Za-z0-9]{20,}$/; if (!guestKeyPattern.test(trimmedKey) && !userKeyPattern.test(trimmedKey)) { throw new errors_1.ValidationError('API ν‚€λŠ” 64자리 랜덀 λ¬Έμžμ—΄(게슀트) λ˜λŠ” "hua_"둜 μ‹œμž‘ν•˜λŠ” λ¬Έμžμ—΄(νšŒμ›κ°€μž…)이어야 ν•©λ‹ˆλ‹€.'); } this.apiKey = trimmedKey; this.config = { ...types_1.DEFAULT_CONFIG, ...config }; } // ===== 이벀트 μ‹œμŠ€ν…œ ===== on(event, listener) { if (!this.eventListeners.has(event)) { this.eventListeners.set(event, []); } this.eventListeners.get(event).push(listener); } off(event, listener) { const listeners = this.eventListeners.get(event); if (listeners) { const index = listeners.indexOf(listener); if (index > -1) { listeners.splice(index, 1); } } } emit(event) { const listeners = this.eventListeners.get(event.type); if (listeners) { listeners.forEach(listener => { try { listener(event); } catch (error) { console.error('Event listener error:', error); } }); } } // ===== μž…λ ₯ 검증 ===== validateChatRequest(request) { const errors = []; // λ©”μ‹œμ§€ 검증 if (!request.message || request.message.trim().length === 0) { throw new errors_1.MissingMessageError(); } if (request.message.length > 1000) { errors.push('λ©”μ‹œμ§€λŠ” 1000자λ₯Ό μ΄ˆκ³Όν•  수 μ—†μŠ΅λ‹ˆλ‹€.'); } // 톀 검증 if (request.tone && !types_1.TONE_PRESETS.includes(request.tone)) { throw new errors_1.InvalidToneError(request.tone); } // λͺ¨λ“œ 검증 if (request.mode && !types_1.MODE_PRESETS.includes(request.mode)) { throw new errors_1.InvalidModeError(request.mode); } // λ“±κΈ‰ 검증 if (request.tier && !types_1.TIER_PRESETS.includes(request.tier)) { throw new errors_1.InvalidTierError(request.tier); } // μ–Έμ–΄ 검증 if (request.lang && !types_1.SUPPORTED_LANGS.includes(request.lang)) { throw new errors_1.InvalidLanguageError(request.lang); } // 제곡자 검증 if (request.provider && !types_1.LLM_PROVIDERS.includes(request.provider)) { errors.push(`μ§€μ›ν•˜μ§€ μ•ŠλŠ” μ œκ³΅μžμž…λ‹ˆλ‹€: ${request.provider}. 지원 제곡자: ${types_1.LLM_PROVIDERS.join(', ')}`); } if (errors.length > 0) { throw new errors_1.ValidationError(errors.join('; ')); } } // ===== HTTP μš”μ²­ μœ ν‹Έλ¦¬ν‹° ===== async makeRequest(endpoint, options = {}) { const url = `${this.config.baseUrl}${endpoint}`; const requestOptions = { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, ...options.headers }, ...options }; // 이벀트 λ°œμƒ this.emit({ type: 'request', timestamp: new Date(), data: { url, method: requestOptions.method } }); let lastError = null; for (let attempt = 0; attempt <= this.config.retries; attempt++) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.config.timeout); const response = await fetch(url, { ...requestOptions, signal: controller.signal }); clearTimeout(timeoutId); // 응닡 이벀트 λ°œμƒ this.emit({ type: 'response', timestamp: new Date(), data: { status: response.status, url } }); if (!response.ok) { let errorData; try { errorData = await response.json(); } catch { errorData = { error: 'UNKNOWN_ERROR', message: `HTTP ${response.status}` }; } const error = (0, errors_1.createErrorFromResponse)(errorData, response.status); // μž¬μ‹œλ„ κ°€λŠ₯ν•œ μ—λŸ¬μΈμ§€ 확인 const retryableStatus = [502, 503, 504]; if (attempt < this.config.retries && ((0, errors_1.isRetryableError)(error) || retryableStatus.includes(response.status))) { lastError = error; // μž¬μ‹œλ„ 이벀트 λ°œμƒ this.emit({ type: 'retry', timestamp: new Date(), data: { attempt: attempt + 1, error: error.code } }); // μ§€μˆ˜ λ°±μ˜€ν”„ const delay = this.config.retryDelay * Math.pow(2, attempt); await new Promise(resolve => setTimeout(resolve, delay)); continue; } throw error; } const data = await response.json(); return data; } catch (error) { if (error instanceof errors_1.HUAError) { throw error; } if (error instanceof Error) { if (error.name === 'AbortError') { throw new errors_1.TimeoutError(); } if (error.message.includes('fetch') || error.message.includes('network')) { if (attempt < this.config.retries) { lastError = new errors_1.NetworkError(error.message); await new Promise(resolve => setTimeout(resolve, this.config.retryDelay)); continue; } throw new errors_1.NetworkError(error.message); } } throw new errors_1.ServerError('μ•Œ 수 μ—†λŠ” 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); } } // λͺ¨λ“  μž¬μ‹œλ„ μ‹€νŒ¨ throw new errors_1.RetryLimitExceededError(lastError?.message); } // ===== 메인 API λ©”μ„œλ“œ ===== async chat(request) { try { // μž…λ ₯ 검증 this.validateChatRequest(request); // μš”μ²­ 데이터 μ€€λΉ„ const requestData = { message: request.message.trim(), tone: request.tone || 'gentle', mode: request.mode || 'empathy', tier: request.tier || 1.0, lang: request.lang || 'ko', provider: request.provider || 'openai', ...(request.llmApiKey && { llmApiKey: request.llmApiKey }) }; // API 호좜 const response = await this.makeRequest('/api/lite', { body: JSON.stringify(requestData) }); return response; } catch (error) { // μ—λŸ¬ 이벀트 λ°œμƒ this.emit({ type: 'error', timestamp: new Date(), data: { error: error instanceof Error ? error.message : 'Unknown error' } }); throw error; } } // ===== API ν‚€ λ°œκΈ‰ ===== async issueKey() { try { const response = await this.makeRequest('/api/issue-key', { headers: { 'Authorization': '' // API ν‚€ λ°œκΈ‰μ€ 인증 λΆˆν•„μš” } }); return response.apiKey; } catch (error) { this.emit({ type: 'error', timestamp: new Date(), data: { error: error instanceof Error ? error.message : 'Unknown error' } }); throw error; } } // ===== μœ ν‹Έλ¦¬ν‹° λ©”μ„œλ“œ ===== getApiKey() { return this.apiKey; } getConfig() { return { ...this.config }; } updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; } } exports.HUALiteClient = HUALiteClient;