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.
125 lines • 5.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpClient = void 0;
const types_1 = require("../types");
const expo_1 = require("../strategies/retry/expo");
const undici_1 = require("undici");
const stream_1 = require("stream");
const logger_1 = require("../utils/logger");
class HttpClient {
constructor(config, baseUrl, retryHandler) {
var _a, _b;
this.config = {
baseUrl: baseUrl, // 'https://generativelanguage.googleapis.com',
timeout: 30000,
...config,
};
this.retryHandler =
retryHandler ||
new expo_1.RetryHandler({
maxRetries: (_a = this.config.retryConfig) === null || _a === void 0 ? void 0 : _a.maxRetries,
retryDelay: (_b = this.config.retryConfig) === null || _b === void 0 ? void 0 : _b.retryDelay,
});
}
async request(options = {}, isStream = false, endPointPath) {
const url = `${this.config.baseUrl}`;
logger_1.logger.info('base url:', url);
logger_1.logger.info('endpoint path: ', endPointPath);
const undiciOptions = {
path: endPointPath,
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),
};
logger_1.logger.info('undici options: ', JSON.stringify(undiciOptions, null, 2));
const operation = async () => {
const { statusCode, body } = await (0, undici_1.request)(url, undiciOptions);
logger_1.logger.debug('status of the response is :', statusCode);
//REMOVE FROM HERE
// const chunk2: Uint8Array[] = [];
// for await (const chunk of body) {
// chunk2.push(chunk);
// }
// const responseText2 = Buffer.concat(chunk2).toString();
// console.log("response text:", JSON.parse(responseText2) as T);
//REMOVE TO HERE
if (isStream) {
logger_1.logger.info('streaming response');
if (body instanceof stream_1.Readable) {
return body;
}
throw new types_1.NonRetryableError('Expected a Readable stream for streaming requests', statusCode, this.getStatusText(statusCode));
}
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'} `);
}
async streamRequest(options = {}, endPointPath) {
logger_1.logger.info('endpoint path: ', endPointPath);
return this.request(options, true, endPointPath);
}
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