UNPKG

svector-sdk

Version:

Official JavaScript and TypeScript SDK for accessing SVECTOR APIs.

331 lines (330 loc) 13.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SVECTOR = void 0; const chat_1 = require("./api/chat"); const conversations_1 = require("./api/conversations"); const files_1 = require("./api/files"); const knowledge_1 = require("./api/knowledge"); const models_1 = require("./api/models"); const vision_1 = require("./api/vision"); const errors_1 = require("./errors"); function isNode() { return typeof process !== 'undefined' && process.versions != null && process.versions.node != null; } function isBrowser() { return typeof window !== 'undefined' && typeof window.document !== 'undefined'; } function isWebWorker() { return typeof globalThis.importScripts === 'function' && typeof self !== 'undefined' && typeof window === 'undefined'; } function isDeno() { return typeof globalThis.Deno !== 'undefined'; } function isEdgeRuntime() { return typeof globalThis.EdgeRuntime !== 'undefined'; } function isSSR() { return typeof window === 'undefined' && typeof process !== 'undefined' && process.env !== undefined; } class SVECTOR { constructor(options = {}) { this.apiKey = options.apiKey || this.getApiKeyFromEnv(); this.baseURL = options.baseURL?.replace(/\/+$/, '') || 'https://spec-chat.tech'; this.maxRetries = options.maxRetries ?? 2; this.timeout = options.timeout ?? 10 * 60 * 1000; this.fetch = options.fetch || this.getDefaultFetch(); this.dangerouslyAllowBrowser = options.dangerouslyAllowBrowser ?? false; if (!this.apiKey) { throw new errors_1.AuthenticationError('SVECTOR API key is required. Set it via the apiKey option or SVECTOR_API_KEY environment variable.'); } this.checkBrowserEnvironment(); this.chat = new chat_1.ChatCompletions(this); this.conversations = new conversations_1.Conversations(this); this.models = new models_1.Models(this); this.files = new files_1.Files(this); this.knowledge = new knowledge_1.Knowledge(this); this.vision = new vision_1.Vision(this); } getApiKeyFromEnv() { if (typeof process !== 'undefined' && process.env) { return process.env['SVECTOR_API_KEY'] || ''; } return ''; } getDefaultFetch() { if (typeof globalThis !== 'undefined' && globalThis.fetch) { return globalThis.fetch.bind(globalThis); } if (typeof window !== 'undefined' && window.fetch) { return window.fetch.bind(window); } if (typeof global !== 'undefined' && global.fetch) { return global.fetch; } if (isDeno() && globalThis.fetch) { return globalThis.fetch; } if (isWebWorker() && typeof self !== 'undefined' && self.fetch) { return self.fetch; } if (isNode()) { try { const nodeFetch = require('node-fetch'); return nodeFetch.default || nodeFetch; } catch (error) { console.warn('SVECTOR SDK: node-fetch not available, falling back to native fetch'); } } throw new Error('No fetch implementation found. Please provide a fetch function via the fetch option, ' + 'or install node-fetch in Node.js environments.'); } checkBrowserEnvironment() { if (isSSR()) { return; } if (isEdgeRuntime()) { return; } if (isWebWorker()) { return; } if (isDeno()) { return; } if (isBrowser() && !this.dangerouslyAllowBrowser) { throw new Error('SVECTOR SDK is being used in a browser environment without dangerouslyAllowBrowser set to true. ' + 'This is strongly discouraged as it exposes your API key to client-side code. ' + 'If you understand the risks, set dangerouslyAllowBrowser: true in the constructor options.'); } } async request(method, path, body, options = {}) { const url = new URL(`${this.baseURL}${path}`); if (options.query) { Object.entries(options.query).forEach(([key, value]) => { url.searchParams.append(key, value); }); } const headers = this.buildHeaders(body, options.headers); const maxRetries = options.maxRetries ?? this.maxRetries; const timeout = options.timeout ?? this.timeout; let retries = 0; while (retries <= maxRetries) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const requestBody = this.prepareRequestBody(body); const response = await this.fetch(url.toString(), { method, headers, body: requestBody, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorData = await this.parseErrorResponse(response); throw this.createErrorFromResponse(response, errorData); } const responseData = await response.json(); const requestId = response.headers.get('x-request-id'); if (requestId && typeof responseData === 'object' && responseData !== null) { responseData._request_id = requestId; } return responseData; } catch (err) { if (err instanceof Error && err.name === 'AbortError') { throw new errors_1.APIConnectionTimeoutError('Request timed out'); } if (err instanceof errors_1.APIError && this.shouldRetry(err.status, retries, maxRetries)) { retries++; await this.sleep(this.calculateBackoffDelay(retries)); continue; } if (retries === maxRetries) { throw new errors_1.APIConnectionError('Max retries exceeded'); } throw err; } } throw new errors_1.APIConnectionError('Unexpected error'); } async requestStream(method, path, body, options = {}) { const url = new URL(`${this.baseURL}${path}`); if (options.query) { Object.entries(options.query).forEach(([key, value]) => { url.searchParams.append(key, value); }); } const headers = { ...this.buildHeaders(body, options.headers), 'Accept': 'text/event-stream', }; const controller = new AbortController(); const timeout = options.timeout ?? this.timeout; const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await this.fetch(url.toString(), { method, headers, body: this.prepareRequestBody(body), signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorData = await this.parseErrorResponse(response); throw this.createErrorFromResponse(response, errorData); } return response; } catch (err) { clearTimeout(timeoutId); if (err instanceof Error && err.name === 'AbortError') { throw new errors_1.APIConnectionTimeoutError('Request timed out'); } throw err; } } async withResponse(method, path, body, options = {}) { const url = new URL(`${this.baseURL}${path}`); if (options.query) { Object.entries(options.query).forEach(([key, value]) => { url.searchParams.append(key, value); }); } const headers = this.buildHeaders(body, options.headers); const maxRetries = options.maxRetries ?? this.maxRetries; const timeout = options.timeout ?? this.timeout; let retries = 0; while (retries <= maxRetries) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const requestBody = this.prepareRequestBody(body); const response = await this.fetch(url.toString(), { method, headers, body: requestBody, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const errorData = await this.parseErrorResponse(response); throw this.createErrorFromResponse(response, errorData); } const responseData = await response.json(); const requestId = response.headers.get('x-request-id'); if (requestId && typeof responseData === 'object' && responseData !== null) { responseData._request_id = requestId; } return { data: responseData, response }; } catch (err) { if (err instanceof Error && err.name === 'AbortError') { throw new errors_1.APIConnectionTimeoutError('Request timed out'); } if (err instanceof errors_1.APIError && this.shouldRetry(err.status, retries, maxRetries)) { retries++; await this.sleep(this.calculateBackoffDelay(retries)); continue; } if (retries === maxRetries) { throw new errors_1.APIConnectionError('Max retries exceeded'); } throw err; } } throw new errors_1.APIConnectionError('Unexpected error'); } buildHeaders(body, customHeaders = {}) { const headers = { 'Authorization': `Bearer ${this.apiKey}`, 'User-Agent': 'svector-sdk/1.0.0', ...customHeaders, }; if (body && !(body instanceof FormData) && !headers['Content-Type'] && !headers['content-type']) { headers['Content-Type'] = 'application/json'; } return headers; } prepareRequestBody(body) { if (!body) return undefined; if (body instanceof FormData) return body; return JSON.stringify(body); } async parseErrorResponse(response) { try { return await response.json(); } catch { return { message: response.statusText }; } } createErrorFromResponse(response, data) { const message = data.message || data.error || data.detail || `HTTP ${response.status}`; const requestId = response.headers.get('x-request-id') || undefined; const headers = Object.fromEntries(response.headers.entries()); switch (response.status) { case 401: return new errors_1.AuthenticationError(message, requestId, headers); case 403: return new errors_1.PermissionDeniedError(message, requestId, headers); case 404: return new errors_1.NotFoundError(message, requestId, headers); case 405: return new errors_1.APIError('Method Not Allowed. Please check the API endpoint and HTTP method.', response.status, requestId, headers); case 422: return new errors_1.UnprocessableEntityError(message, requestId, headers); case 429: return new errors_1.RateLimitError(message, requestId, headers); case 502: return new errors_1.InternalServerError('Bad Gateway - API server temporarily unavailable', response.status, requestId, headers); case 503: return new errors_1.InternalServerError('Service Unavailable - API server temporarily overloaded', response.status, requestId, headers); case 504: return new errors_1.InternalServerError('Gateway Timeout - API request timed out', response.status, requestId, headers); case 524: return new errors_1.InternalServerError('Cloudflare Timeout - Request took too long to process', response.status, requestId, headers); default: if (response.status >= 500) { return new errors_1.InternalServerError(message, response.status, requestId, headers); } return new errors_1.APIError(message, response.status, requestId, headers); } } shouldRetry(status, retries, maxRetries) { if (retries >= maxRetries) return false; if (!status) return false; return [408, 409, 429, 502, 503, 504, 524].includes(status) || status >= 500; } calculateBackoffDelay(retryCount) { return Math.min(Math.pow(2, retryCount) * 1000, 8000); } sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async get(path, options) { return this.request('GET', path, undefined, options); } async post(path, body, options) { return this.request('POST', path, body, options); } async put(path, body, options) { return this.request('PUT', path, body, options); } async delete(path, options) { return this.request('DELETE', path, undefined, options); } } exports.SVECTOR = SVECTOR;