UNPKG

llmforge

Version:

One API, every AI model, instant switching. Change from GPT-4 to Gemini to local models with a single config update. LLMForge is the lightweight, TypeScript-first solution for multi-provider AI applications with zero vendor lock-in.

112 lines 4.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpClient = void 0; // http-client.ts const types_1 = require("../types"); const expo_1 = require("../strategies/retry/expo"); const undici_1 = require("undici"); class HttpClient { constructor(config, baseUrl, retryHandler) { this.config = { baseUrl: baseUrl, // 'https://generativelanguage.googleapis.com', timeout: 30000, maxRetries: 1, retryDelay: 1000, ...config, }; this.retryHandler = retryHandler || new expo_1.RetryHandler({ maxRetries: this.config.maxRetries, retryDelay: this.config.retryDelay, }); } async request(endpoint, options = {}, isStream = false) { const url = `${this.config.baseUrl}${endpoint}?key=${this.config.apiKey}`; // Convert RequestInit to undici's RequestOptions const undiciOptions = { path: `${endpoint}?key=${this.config.apiKey}`, method: options.method || 'POST', headers: Object.fromEntries(Object.entries({ 'Content-Type': 'application/json', ...options.headers, }).map(([k, v]) => [k, v === null || v === void 0 ? void 0 : v.toString()])), body: options.body, signal: this.createTimeoutSignal(this.config.timeout), }; const operation = async () => { const { statusCode, body } = await (0, undici_1.request)(url, undiciOptions); if (statusCode >= 400) { await this.handleErrorResponse(statusCode, body); } if (isStream) { // For streaming, return the readable stream return body; } // For non-streaming, parse JSON const chunks = []; for await (const chunk of body) { chunks.push(chunk); } const responseText = Buffer.concat(chunks).toString(); return JSON.parse(responseText); }; return this.retryHandler.executeWithRetry(operation, `${options.method || 'POST'} ${endpoint}`); } async streamRequest(endpoint, options = {}) { return this.request(endpoint, options, true); } async handleErrorResponse(statusCode, body) { let errorData; try { const chunks = []; for await (const chunk of body) { chunks.push(chunk); } const responseText = Buffer.concat(chunks).toString(); errorData = JSON.parse(responseText); } catch (_a) { errorData = { error: { code: statusCode, message: this.getStatusText(statusCode) || 'Unknown error', status: this.getStatusText(statusCode), }, }; } const { code, message, status, details } = errorData.error; const isRetryable = this.isRetryableStatusCode(code); const ErrorClass = isRetryable ? types_1.RetryableError : types_1.NonRetryableError; throw new ErrorClass(message, code, status, details); } isRetryableStatusCode(statusCode) { const retryableCodes = [429, 500, 502, 503, 504]; return retryableCodes.includes(statusCode); } getStatusText(statusCode) { const statusTexts = { 400: 'BAD_REQUEST', 401: 'UNAUTHORIZED', 403: 'FORBIDDEN', 404: 'NOT_FOUND', 429: 'RATE_LIMITED', 500: 'INTERNAL_SERVER_ERROR', 502: 'BAD_GATEWAY', 503: 'SERVICE_UNAVAILABLE', 504: 'GATEWAY_TIMEOUT', }; return statusTexts[statusCode] || 'UNKNOWN_ERROR'; } createTimeoutSignal(timeout) { const controller = new AbortController(); setTimeout(() => controller.abort(), timeout); return controller.signal; } updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; } getConfig() { return { ...this.config }; } } exports.HttpClient = HttpClient; //# sourceMappingURL=http-client.js.map