hua-sdk-lite
Version:
HUA Lite API SDK - Emotional AI Chatbot Integration
243 lines (242 loc) β’ 9.66 kB
JavaScript
;
// π 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;