UNPKG

recallrai

Version:

Official Node.js SDK for RecallrAI - Revolutionary contextual memory system that enables AI assistants to form meaningful connections between conversations, just like human memory.

235 lines (234 loc) 8.95 kB
"use strict"; /** * HTTP client for making requests to the RecallrAI API. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HTTPClient = void 0; const axios_1 = __importDefault(require("axios")); const errors_1 = require("../errors"); const models_1 = require("../models"); /** * HTTP client for making requests to the RecallrAI API. */ class HTTPClient { /** * Initialize the HTTP client. * * @param apiKey - Your RecallrAI API key. * @param projectId - Your project ID. * @param baseUrl - The base URL for the RecallrAI API. * @param timeout - Request timeout in seconds. */ constructor(apiKey, projectId, baseUrl, timeout = 30) { this._systemPromptCacheByStrategy = new Map(); this._systemPromptCacheExpiresAt = new Map(); this.apiKey = apiKey; this.projectId = projectId; this.baseUrl = baseUrl.replace(/\/$/, ""); this.timeout = timeout * 1000; this.client = axios_1.default.create({ timeout: this.timeout, validateStatus: () => true, headers: { "X-Recallr-Api-Key": this.apiKey, "X-Recallr-Project-Id": this.projectId, "Content-Type": "application/json", "Accept": "application/json", "User-Agent": "RecallrAI-Node-SDK/0.6.6", }, }); } /** * Filter out null and undefined values from params. */ filterParams(params) { if (!params) return {}; const filtered = {}; for (const [key, value] of Object.entries(params)) { if (value !== null && value !== undefined) { filtered[key] = value; } } return filtered; } /** * Filter out null and undefined values from data. */ filterData(data) { if (!data) return {}; const filtered = {}; for (const [key, value] of Object.entries(data)) { if (value !== null && value !== undefined) { filtered[key] = value; } } return filtered; } /** * Make a request to the RecallrAI API. * * @param method - HTTP method (GET, POST, PUT, DELETE). * @param path - API endpoint path. * @param params - Query parameters. * @param data - Request body data. * @returns The parsed JSON response. */ async request(method, path, params, data) { const url = `${this.baseUrl}${path}`; const filteredParams = this.filterParams(params); const filteredData = this.filterData(data); try { const response = await this.client.request({ method, url, params: filteredParams, data: filteredData, }); if (response.status === 422) { const detail = response.data?.detail || "Validation error"; throw new errors_1.ValidationError(detail, response.status); } else if (response.status === 500) { const detail = response.data?.detail || "Internal server error"; throw new errors_1.InternalServerError(detail, response.status); } else if (response.status === 404 && response.data === "404 page not found") { throw new errors_1.ConnectionError("Resource not found", response.status); } else if (response.status === 401) { const detail = response.data?.detail || "Authentication failed"; throw new errors_1.AuthenticationError(detail, response.status); } else if (response.status === 429) { const detail = "429 Too Many Requests. Please try again in a few moments."; throw new errors_1.RateLimitError(detail, response.status); } return response; } catch (error) { if (axios_1.default.isAxiosError(error)) { const axiosError = error; if (axiosError.code === "ECONNABORTED" || axiosError.message.includes("timeout")) { throw new errors_1.TimeoutError(`Request timed out: ${axiosError.message}`, 0); } if (axiosError.code === "ECONNREFUSED" || axiosError.code === "ENOTFOUND") { throw new errors_1.ConnectionError(`Failed to connect to the API: ${axiosError.message}`, 0); } } throw error; } } /** * Make a GET request. */ async get(path, params) { return this.request("GET", path, params); } /** * Make a POST request. */ async post(path, data) { return this.request("POST", path, undefined, data); } /** * Make a PUT request. */ async put(path, data) { return this.request("PUT", path, undefined, data); } /** * Make a DELETE request. */ async delete(path, params) { return this.request("DELETE", path, params); } /** * Stream Server-Sent Events (SSE) from the API. */ async *streamLines(path, params) { const url = `${this.baseUrl}${path}`; const filteredParams = this.filterParams(params); try { const response = await this.client.request({ method: "GET", url, params: filteredParams, responseType: "stream", headers: { Accept: "text/event-stream", }, }); if (response.status === 422) { throw new errors_1.ValidationError("Validation error", response.status); } if (response.status === 500) { throw new errors_1.InternalServerError("Internal server error", response.status); } if (response.status === 404) { // Read the stream body to check if it's a router-level 404 const chunks = []; for await (const chunk of response.data) { chunks.push(chunk); if (Buffer.concat(chunks).length > 100) break; } const bodyText = Buffer.concat(chunks).toString("utf8").trim(); if (bodyText === "404 page not found") { throw new errors_1.ConnectionError("Resource not found", response.status); } return; // API-level 404, empty generator } if (response.status === 401) { throw new errors_1.AuthenticationError("Authentication failed", response.status); } if (response.status === 429) { throw new errors_1.RateLimitError("Please try again in a few moments.", response.status); } let buffer = ""; for await (const chunk of response.data) { buffer += chunk.toString("utf8"); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (const line of lines) { if (line.length > 0) { yield line; } } } } catch (error) { if (axios_1.default.isAxiosError(error)) { const axiosError = error; if (axiosError.code === "ECONNABORTED" || axiosError.message.includes("timeout")) { throw new errors_1.TimeoutError(`Request timed out: ${axiosError.message}`, 0); } if (axiosError.code === "ECONNREFUSED" || axiosError.code === "ENOTFOUND") { throw new errors_1.ConnectionError(`Failed to connect to the API: ${axiosError.message}`, 0); } } throw error; } } /** * Fetch the strategy-specific system prompt, caching it per strategy for one hour. */ async getCachedSystemPrompt(recallStrategy = models_1.RecallStrategy.BALANCED) { const key = recallStrategy; const now = Date.now(); const cached = this._systemPromptCacheByStrategy.get(key); if (cached !== undefined && now < (this._systemPromptCacheExpiresAt.get(key) ?? 0)) { return cached; } const response = await this.get("/api/v1/system-prompt", { recall_strategy: key }); const prompt = response.data.system_prompt; this._systemPromptCacheByStrategy.set(key, prompt); this._systemPromptCacheExpiresAt.set(key, now + 3600 * 1000); return prompt; } } exports.HTTPClient = HTTPClient;