@future-agi/sdk
Version:
We help GenAI teams maintain high-accuracy for their Models in production.
301 lines • 12.3 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.APIKeyAuth = exports.HttpClient = exports.ResponseHandler = void 0;
exports.createAuthenticatedClient = createAuthenticatedClient;
const axios_1 = __importStar(require("axios"));
const executor_1 = require("../utils/executor");
const errors_1 = require("../utils/errors");
const constants_1 = require("../utils/constants");
/**
* Generic response handler for parsing and validating HTTP responses
*/
class ResponseHandler {
/**
* Parse the response into the expected type
*/
static parse(response, handlerClass) {
if (!response || response.status !== 200) {
handlerClass._handleError(response);
}
return handlerClass._parseSuccess(response);
}
/**
* Parse successful response - to be implemented by subclasses
*/
static _parseSuccess(response) {
throw new Error("Method '_parseSuccess' must be implemented by subclass.");
}
/**
* Handle error responses - to be implemented by subclasses
*/
static _handleError(response) {
var _a;
const status = (response === null || response === void 0 ? void 0 : response.status) || 500;
let message;
if (response === null || response === void 0 ? void 0 : response.data) {
const d = response.data;
message = d.message || d.detail || d.result;
// If nothing explicit, stringify the whole payload so callers see something meaningful
if (!message) {
try {
message = JSON.stringify(d);
}
catch (_b) {
/* ignore */
}
}
}
if (!message) {
const url = ((_a = response === null || response === void 0 ? void 0 : response.config) === null || _a === void 0 ? void 0 : _a.url) ? ` – ${response.config.url}` : '';
message = ((response === null || response === void 0 ? void 0 : response.statusText) && response.statusText.trim().length > 0)
? response.statusText
: `HTTP ${status}${url}`;
}
switch (status) {
case 401:
case 403:
throw new errors_1.InvalidAuthError(message);
case 404:
throw new errors_1.DatasetNotFoundError(message);
case 429:
throw new errors_1.RateLimitError(message);
case 503:
throw new errors_1.ServiceUnavailableError(message);
case 500:
case 502:
case 504:
throw new errors_1.ServerError(message);
default:
throw new Error(`HTTP ${status}: ${message}`);
}
}
}
exports.ResponseHandler = ResponseHandler;
/**
* Base HTTP client with improved request handling, connection pooling, and async execution
*/
class HttpClient {
constructor(config = {}) {
this._baseUrl = (config.baseUrl || (0, constants_1.get_base_url)()).replace(/\/$/, '');
this._defaultTimeout = config.timeout || constants_1.DEFAULT_SETTINGS.TIMEOUT;
this._defaultRetryAttempts = config.retryAttempts || 3;
this._defaultRetryDelay = config.retryDelay || 1000;
// Create axios instance with default configuration
this._axiosInstance = axios_1.default.create({
baseURL: this._baseUrl,
timeout: this._defaultTimeout,
headers: Object.assign({ 'Content-Type': 'application/json', 'User-Agent': '@future-agi/sdk' }, config.defaultHeaders),
// Enable connection pooling
maxRedirects: 5,
validateStatus: () => true, // Handle all status codes manually
});
// Create bounded executor for managing concurrent requests
this._executor = new executor_1.BoundedExecutor(config.maxQueue || constants_1.DEFAULT_SETTINGS.MAX_QUEUE, config.maxWorkers || constants_1.DEFAULT_SETTINGS.MAX_WORKERS);
this._setupInterceptors();
}
/**
* Setup request and response interceptors
*/
_setupInterceptors() {
// Request interceptor for logging and debugging
this._axiosInstance.interceptors.request.use((config) => {
// Add request timestamp for performance monitoring
config.startTime = Date.now();
return config;
}, (error) => Promise.reject(error));
// Response interceptor for logging and performance monitoring
this._axiosInstance.interceptors.response.use((response) => {
const duration = Date.now() - (response.config.startTime || 0);
// Could add logging here for production monitoring
return response;
}, (error) => Promise.reject(error));
}
/**
* Make an HTTP request with retries and response handling
*/
request(config, responseHandler) {
return __awaiter(this, void 0, void 0, function* () {
const requestConfig = {
method: config.method,
url: config.url,
headers: config.headers,
params: config.params,
data: config.json || config.data,
timeout: config.timeout || this._defaultTimeout,
};
// Handle file uploads
if (config.files && Object.keys(config.files).length > 0) {
const formData = new FormData();
Object.entries(config.files).forEach(([key, file]) => {
formData.append(key, file);
});
if (config.data) {
Object.entries(config.data).forEach(([key, value]) => {
formData.append(key, value);
});
}
requestConfig.data = formData;
requestConfig.headers = Object.assign(Object.assign({}, requestConfig.headers), { 'Content-Type': 'multipart/form-data' });
}
const retryAttempts = config.retry_attempts || this._defaultRetryAttempts;
const retryDelay = config.retry_delay || this._defaultRetryDelay;
// Execute request with bounded concurrency
return this._executor.submit(() => __awaiter(this, void 0, void 0, function* () {
var _a;
for (let attempt = 0; attempt < retryAttempts; attempt++) {
try {
const response = yield this._axiosInstance.request(requestConfig);
if (responseHandler) {
return ResponseHandler.parse(response, responseHandler);
}
// Handle errors if no custom handler
if (response.status >= 400) {
ResponseHandler._handleError(response);
}
return response;
}
catch (error) {
// Don't retry certain errors
if (error instanceof errors_1.DatasetNotFoundError ||
error instanceof errors_1.InvalidAuthError ||
(error instanceof axios_1.AxiosError && ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 401)) {
throw error;
}
// Last attempt - throw the error
if (attempt === retryAttempts - 1) {
if (error instanceof axios_1.AxiosError) {
ResponseHandler._handleError(error.response);
}
throw error;
}
// Wait before retry
yield this._sleep(retryDelay * Math.pow(2, attempt)); // Exponential backoff
}
}
throw new Error('Unexpected end of retry loop');
}));
});
}
/**
* Helper method for sleep/delay
*/
_sleep(ms) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => setTimeout(resolve, ms));
});
}
/**
* Close the client and cleanup resources
*/
close() {
return __awaiter(this, void 0, void 0, function* () {
yield this._executor.shutdown(true);
});
}
/**
* Get the base URL
*/
get baseUrl() {
return this._baseUrl;
}
/**
* Get the default timeout
*/
get defaultTimeout() {
return this._defaultTimeout;
}
}
exports.HttpClient = HttpClient;
/**
* HTTP client with API key authentication
*/
class APIKeyAuth extends HttpClient {
constructor(config = {}) {
const fiApiKey = config.fiApiKey || process.env[constants_1.AUTH_ENVVAR_NAME.API_KEY];
const fiSecretKey = config.fiSecretKey || process.env[constants_1.AUTH_ENVVAR_NAME.SECRET_KEY];
if (!fiApiKey || !fiSecretKey) {
throw new errors_1.MissingAuthError(fiApiKey, fiSecretKey);
}
super(Object.assign(Object.assign({}, config), { baseUrl: config.fiBaseUrl || config.baseUrl, defaultHeaders: Object.assign({ 'X-Api-Key': fiApiKey, 'X-Secret-Key': fiSecretKey }, config.defaultHeaders) }));
// Set class-level credentials
this._fiApiKey = fiApiKey;
this._fiSecretKey = fiSecretKey;
}
/**
* Get the current API key
*/
get fiApiKey() {
return this._fiApiKey;
}
/**
* Get the current secret key
*/
get fiSecretKey() {
return this._fiSecretKey;
}
/**
* Get authentication headers
*/
get headers() {
return {
'X-Api-Key': this._fiApiKey,
'X-Secret-Key': this._fiSecretKey,
};
}
}
exports.APIKeyAuth = APIKeyAuth;
/**
* Factory function to create authenticated HTTP client
*/
function createAuthenticatedClient(config) {
return new APIKeyAuth(config);
}
exports.default = {
ResponseHandler,
HttpClient,
APIKeyAuth,
createAuthenticatedClient,
};
//# sourceMappingURL=auth.js.map
;