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
JavaScript
"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;