@retrivora-ai/rag-engine 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -2151,6 +2151,285 @@ module.exports = __toCommonJS(server_exports);
2151
2151
  // src/core/ConfigResolver.ts
2152
2152
  init_templateUtils();
2153
2153
 
2154
+ // src/core/LicenseVerifier.ts
2155
+ var crypto = __toESM(require("crypto"));
2156
+
2157
+ // src/exceptions/index.ts
2158
+ var RetrivoraError = class extends Error {
2159
+ constructor(message, code, details) {
2160
+ super(message);
2161
+ this.name = new.target.name;
2162
+ this.code = code;
2163
+ this.details = details;
2164
+ }
2165
+ };
2166
+ var ProviderNotFoundException = class extends RetrivoraError {
2167
+ constructor(providerType, provider, details) {
2168
+ super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
2169
+ }
2170
+ };
2171
+ var EmbeddingFailedException = class extends RetrivoraError {
2172
+ constructor(message = "Embedding generation failed", details) {
2173
+ super(message, "EMBEDDING_FAILED", details);
2174
+ }
2175
+ };
2176
+ var RetrievalException = class extends RetrivoraError {
2177
+ constructor(message = "Retrieval failed", details) {
2178
+ super(message, "RETRIEVAL_FAILED", details);
2179
+ }
2180
+ };
2181
+ var RateLimitException = class extends RetrivoraError {
2182
+ constructor(message = "Provider rate limit exceeded", details) {
2183
+ super(message, "RATE_LIMITED", details);
2184
+ }
2185
+ };
2186
+ var ConfigurationException = class extends RetrivoraError {
2187
+ constructor(message, details) {
2188
+ super(message, "CONFIGURATION_ERROR", details);
2189
+ }
2190
+ };
2191
+ var AuthenticationException = class extends RetrivoraError {
2192
+ constructor(message = "Provider authentication failed", details) {
2193
+ super(message, "AUTHENTICATION_ERROR", details);
2194
+ }
2195
+ };
2196
+ function wrapError(err, defaultCode, defaultMessage) {
2197
+ var _a2;
2198
+ if (err instanceof RetrivoraError) {
2199
+ return err;
2200
+ }
2201
+ const error = err;
2202
+ const message = (error == null ? void 0 : error.message) || defaultMessage || String(err);
2203
+ const status = (error == null ? void 0 : error.status) || (error == null ? void 0 : error.statusCode) || ((_a2 = error == null ? void 0 : error.response) == null ? void 0 : _a2.status);
2204
+ const code = error == null ? void 0 : error.code;
2205
+ if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
2206
+ return new RateLimitException(message, err);
2207
+ }
2208
+ if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
2209
+ return new AuthenticationException(message, err);
2210
+ }
2211
+ switch (defaultCode) {
2212
+ case "PROVIDER_NOT_FOUND":
2213
+ return new ProviderNotFoundException("provider", message, err);
2214
+ case "EMBEDDING_FAILED":
2215
+ return new EmbeddingFailedException(message, err);
2216
+ case "RETRIEVAL_FAILED":
2217
+ return new RetrievalException(message, err);
2218
+ case "RATE_LIMITED":
2219
+ return new RateLimitException(message, err);
2220
+ case "CONFIGURATION_ERROR":
2221
+ return new ConfigurationException(message, err);
2222
+ case "AUTHENTICATION_ERROR":
2223
+ return new AuthenticationException(message, err);
2224
+ default:
2225
+ return new RetrivoraError(message, defaultCode, err);
2226
+ }
2227
+ }
2228
+
2229
+ // src/core/LicenseVerifier.ts
2230
+ var LicenseVerifier = class {
2231
+ /**
2232
+ * Decodes, verifies signature, and checks metadata of a license key.
2233
+ *
2234
+ * @param licenseKey - Base64url signed JWT license key.
2235
+ * @param currentProjectId - Project namespace ID.
2236
+ * @param publicKeyOverride - Optional override for unit/integration tests.
2237
+ */
2238
+ static verify(licenseKey, currentProjectId, provider, publicKeyOverride) {
2239
+ const isProduction = process.env.NODE_ENV === "production";
2240
+ const now = Date.now();
2241
+ if (licenseKey) {
2242
+ const cacheKey = `${licenseKey}:${currentProjectId}:${provider || ""}:${publicKeyOverride || ""}`;
2243
+ const cached = this.cache.get(cacheKey);
2244
+ if (cached && now - cached.cachedAt < this.CACHE_TTL_MS) {
2245
+ const currentTimestampSec = Math.floor(now / 1e3);
2246
+ if (cached.payload.expiresAt && currentTimestampSec > cached.payload.expiresAt) {
2247
+ this.cache.delete(cacheKey);
2248
+ } else {
2249
+ return cached.payload;
2250
+ }
2251
+ }
2252
+ }
2253
+ if (!licenseKey) {
2254
+ if (isProduction) {
2255
+ throw new ConfigurationException(
2256
+ "[Retrivora SDK] License key (licenseKey) is required in production environments."
2257
+ );
2258
+ }
2259
+ console.warn(
2260
+ `\x1B[33m[Retrivora SDK] WARNING: Running without a license key. This namespace (${currentProjectId}) is permitted for local development only.\x1B[0m`
2261
+ );
2262
+ return {
2263
+ projectId: currentProjectId,
2264
+ expiresAt: Math.floor(Date.now() / 1e3) + 86400,
2265
+ // Valid for 24h
2266
+ tier: "hobby"
2267
+ };
2268
+ }
2269
+ try {
2270
+ const parts = licenseKey.split(".");
2271
+ if (parts.length !== 3) {
2272
+ throw new Error("Malformed token structure (expected 3 parts).");
2273
+ }
2274
+ const [headerB64, payloadB64, signatureB64] = parts;
2275
+ const dataToVerify = `${headerB64}.${payloadB64}`;
2276
+ const publicKey = publicKeyOverride != null ? publicKeyOverride : this.PUBLIC_KEY;
2277
+ const signature = Buffer.from(signatureB64, "base64url");
2278
+ const data = Buffer.from(dataToVerify);
2279
+ const isValid = crypto.verify(
2280
+ "sha256",
2281
+ data,
2282
+ publicKey,
2283
+ signature
2284
+ );
2285
+ if (!isValid) {
2286
+ throw new Error("Signature verification failed.");
2287
+ }
2288
+ const payload = JSON.parse(
2289
+ Buffer.from(payloadB64, "base64url").toString("utf8")
2290
+ );
2291
+ if (!payload.projectId) {
2292
+ throw new Error('License payload is missing "projectId".');
2293
+ }
2294
+ if (payload.projectId !== currentProjectId) {
2295
+ throw new Error(
2296
+ `Project ID mismatch. License is bound to project namespace "${payload.projectId}" but configuration has "${currentProjectId}".`
2297
+ );
2298
+ }
2299
+ if (!payload.expiresAt) {
2300
+ throw new Error('License payload is missing expiration ("expiresAt").');
2301
+ }
2302
+ const currentTimestampSec = Math.floor(Date.now() / 1e3);
2303
+ if (currentTimestampSec > payload.expiresAt) {
2304
+ throw new Error(
2305
+ `License key has expired. Expiration: ${new Date(
2306
+ payload.expiresAt * 1e3
2307
+ ).toDateString()}`
2308
+ );
2309
+ }
2310
+ if (isProduction && provider) {
2311
+ const tier = (payload.tier || "").toLowerCase();
2312
+ const normalizedProvider = provider.toLowerCase();
2313
+ if (tier === "hobby") {
2314
+ const allowedHobby = ["postgresql", "pgvector", "supabase"];
2315
+ if (!allowedHobby.includes(normalizedProvider)) {
2316
+ throw new Error(
2317
+ `The database provider "${provider}" is not allowed on the Hobby tier. Hobby tier is restricted to PostgreSQL and Supabase. Please upgrade to a Developer Pro or Enterprise plan.`
2318
+ );
2319
+ }
2320
+ } else if (tier === "pro" || tier === "developer_pro" || tier === "premium" || tier === "growth" || tier === "free_trial" || tier === "free_tier" || tier === "free") {
2321
+ const allowedPro = [
2322
+ "postgresql",
2323
+ "pgvector",
2324
+ "supabase",
2325
+ "pinecone",
2326
+ "qdrant",
2327
+ "mongodb",
2328
+ "milvus",
2329
+ "chroma",
2330
+ "chromadb"
2331
+ ];
2332
+ if (!allowedPro.includes(normalizedProvider)) {
2333
+ throw new Error(
2334
+ `The database provider "${provider}" is not allowed on the Developer Pro tier. Please upgrade to an Enterprise plan.`
2335
+ );
2336
+ }
2337
+ }
2338
+ }
2339
+ if (licenseKey) {
2340
+ const cacheKey = `${licenseKey}:${currentProjectId}:${provider || ""}:${publicKeyOverride || ""}`;
2341
+ this.cache.set(cacheKey, { payload, cachedAt: now });
2342
+ }
2343
+ return payload;
2344
+ } catch (err) {
2345
+ const message = err instanceof Error ? err.message : String(err);
2346
+ throw new ConfigurationException(
2347
+ `[Retrivora SDK] License key validation failed: ${message}`
2348
+ );
2349
+ }
2350
+ }
2351
+ static async verifyIP(licenseKey) {
2352
+ if (!licenseKey) return;
2353
+ try {
2354
+ const parts = licenseKey.split(".");
2355
+ if (parts.length !== 3) return;
2356
+ const [, payloadB64] = parts;
2357
+ const payload = JSON.parse(
2358
+ Buffer.from(payloadB64, "base64url").toString("utf8")
2359
+ );
2360
+ const tier = (payload.tier || "").toLowerCase();
2361
+ if (tier === "enterprise" || tier === "custom") {
2362
+ return;
2363
+ }
2364
+ if (payload.ipv4 || payload.ipv6) {
2365
+ let currentIpv4 = "";
2366
+ let currentIpv6 = "";
2367
+ try {
2368
+ const res = await fetch("https://api4.ipify.org?format=json", { signal: AbortSignal.timeout(3e3) });
2369
+ const data = await res.json();
2370
+ currentIpv4 = data.ip;
2371
+ } catch (e) {
2372
+ }
2373
+ try {
2374
+ const res = await fetch("https://api6.ipify.org?format=json", { signal: AbortSignal.timeout(3e3) });
2375
+ const data = await res.json();
2376
+ currentIpv6 = data.ip;
2377
+ } catch (e) {
2378
+ }
2379
+ const localIps = [];
2380
+ try {
2381
+ const osModule = require("os");
2382
+ const interfaces = osModule.networkInterfaces();
2383
+ for (const name of Object.keys(interfaces)) {
2384
+ for (const iface of interfaces[name] || []) {
2385
+ if (!iface.internal) {
2386
+ localIps.push(iface.address);
2387
+ }
2388
+ }
2389
+ }
2390
+ } catch (e) {
2391
+ }
2392
+ const isIpv4Match = payload.ipv4 && (currentIpv4 === payload.ipv4 || localIps.includes(payload.ipv4));
2393
+ const isIpv6Match = payload.ipv6 && (currentIpv6 === payload.ipv6 || localIps.includes(payload.ipv6));
2394
+ if (payload.ipv4 && payload.ipv6) {
2395
+ if (!isIpv4Match && !isIpv6Match) {
2396
+ throw new Error(
2397
+ `License key access restricted. This license key was created for a different system (authorized IPv4: ${payload.ipv4}, IPv6: ${payload.ipv6}) and cannot be used on this machine.`
2398
+ );
2399
+ }
2400
+ } else if (payload.ipv4 && !isIpv4Match) {
2401
+ throw new Error(
2402
+ `License key access restricted. This license key was created for a different system (authorized IPv4: ${payload.ipv4}) and cannot be used on this machine.`
2403
+ );
2404
+ } else if (payload.ipv6 && !isIpv6Match) {
2405
+ throw new Error(
2406
+ `License key access restricted. This license key was created for a different system (authorized IPv6: ${payload.ipv6}) and cannot be used on this machine.`
2407
+ );
2408
+ }
2409
+ }
2410
+ } catch (err) {
2411
+ if (err.message.includes("License key access restricted")) {
2412
+ throw new ConfigurationException(`[Retrivora SDK] ${err.message}`);
2413
+ }
2414
+ }
2415
+ }
2416
+ };
2417
+ // A simple in-memory cache to save CPU overhead of cryptographic signature checks.
2418
+ LicenseVerifier.cache = /* @__PURE__ */ new Map();
2419
+ LicenseVerifier.CACHE_TTL_MS = 60 * 1e3;
2420
+ // Cache verified license for 60 seconds
2421
+ // Retrivora's Public Key used to verify the license key signature.
2422
+ // In production, this matches the private key held by Retrivora SaaS.
2423
+ LicenseVerifier.PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
2424
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArMieCkCBtTfByRNOserm
2425
+ XM4T0Am29JyNzB4XQPe+WJJ56CN73H1HLXx9n1ByFcxkEJ9po+SURj5DnvUXwEy+
2426
+ xstx33wDPpVmcMQe33j5CRYS0S4gICiNqIRN7XkTlJtrcXqrmh1/hdOAwfRbjO1T
2427
+ NVtORHwUwWfI/lBLyxUPGtKPed+0fQ7NtcW4o00JXxs3EjCB5BV9CbnXoTjQb/4h
2428
+ iwZof4l8rb7oaNzOsVg0RSyxsETX7Ex5sI0CjBz1p2mYp4oVhw/A/h8Ls7H0XzjC
2429
+ +Eb6BLtsytt5JHoSp0jExLlCpDmpys2L+kETcBVx+IqiXtdN1n6KbkksvEDUVzLI
2430
+ MwIDAQAB
2431
+ -----END PUBLIC KEY-----`;
2432
+
2154
2433
  // src/config/constants.ts
2155
2434
  var VECTOR_DB_PROVIDERS = [
2156
2435
  "pinecone",
@@ -2223,14 +2502,12 @@ function getRagConfig(baseConfig, env = process.env) {
2223
2502
  return getEnvConfig(env, baseConfig);
2224
2503
  }
2225
2504
  function getEnvConfig(env = process.env, base) {
2226
- var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua;
2505
+ var _a2, _b, _c, _d, _e, _f, _g2, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb, _lb, _mb, _nb, _ob, _pb, _qb, _rb, _sb, _tb, _ub, _vb, _wb;
2227
2506
  const projectId = (_c = (_b = (_a2 = readString(env, "RAG_PROJECT_ID")) != null ? _a2 : readString(env, "NEXT_PUBLIC_PROJECT_ID")) != null ? _b : base == null ? void 0 : base.projectId) != null ? _c : "__default__";
2228
2507
  const licenseKey = (_g2 = (_f = (_e = (_d = readString(env, "RAG_LICENSE_KEY")) != null ? _d : readString(env, "RETRIVORA_LICENSE_KEY")) != null ? _e : readString(env, "NEXT_PUBLIC_RETRIVORA_LICENSE_KEY")) != null ? _f : readString(env, "LICENSE_KEY")) != null ? _g2 : base == null ? void 0 : base.licenseKey;
2229
2508
  const telemetryEnabled = readString(env, "TELEMETRY_ENABLED") === "true" || readString(env, "NEXT_PUBLIC_TELEMETRY_ENABLED") === "true" || ((_h = base == null ? void 0 : base.telemetry) == null ? void 0 : _h.enabled) || Boolean(licenseKey);
2230
2509
  const vectorProvider = readEnum(env, "VECTOR_DB_PROVIDER", "pinecone", VECTOR_DB_PROVIDERS);
2231
- const llmProvider = readEnum(env, "LLM_PROVIDER", "openai", LLM_PROVIDERS);
2232
- const embeddingProvider = readEnum(env, "EMBEDDING_PROVIDER", "openai", EMBEDDING_PROVIDERS);
2233
- const embeddingDimensions = readNumber(env, "EMBEDDING_DIMENSIONS", 1536);
2510
+ const embeddingDimensions = readNumber(env, "EMBEDDING_DIMENSIONS", 768);
2234
2511
  const vectorDbOptions = {};
2235
2512
  if (vectorProvider === "pinecone") {
2236
2513
  vectorDbOptions.apiKey = (_l = (_k = readString(env, "PINECONE_API_KEY")) != null ? _k : (_j = (_i = base == null ? void 0 : base.vectorDb) == null ? void 0 : _i.options) == null ? void 0 : _j.apiKey) != null ? _l : "";
@@ -2295,65 +2572,88 @@ function getEnvConfig(env = process.env, base) {
2295
2572
  rest: "default",
2296
2573
  custom: "default"
2297
2574
  };
2575
+ const isFreeTier = (() => {
2576
+ if (!licenseKey) return true;
2577
+ try {
2578
+ const payload = LicenseVerifier.verify(licenseKey, projectId);
2579
+ const tier = (payload.tier || "").toLowerCase();
2580
+ return tier === "free_trial" || tier === "free_tier" || tier === "free" || tier === "hobby";
2581
+ } catch (e) {
2582
+ return true;
2583
+ }
2584
+ })();
2585
+ const DEFAULT_FREE_GATEWAY = "https://llm.retrivora.com/api/v1";
2586
+ const defaultLlmProvider = isFreeTier ? "universal_rest" : "universal_rest";
2587
+ const llmProvider = (_na = (_ma = readEnum(env, "LLM_PROVIDER", defaultLlmProvider, LLM_PROVIDERS)) != null ? _ma : (_la = base == null ? void 0 : base.llm) == null ? void 0 : _la.provider) != null ? _na : defaultLlmProvider;
2588
+ const llmBaseUrl = (_qa = (_pa = readString(env, "LLM_BASE_URL")) != null ? _pa : (_oa = base == null ? void 0 : base.llm) == null ? void 0 : _oa.baseUrl) != null ? _qa : DEFAULT_FREE_GATEWAY;
2589
+ const llmModel = (_ua = (_sa = readString(env, "LLM_MODEL")) != null ? _sa : (_ra = base == null ? void 0 : base.llm) == null ? void 0 : _ra.model) != null ? _ua : isFreeTier ? "llama-3.1-8b-instant" : (_ta = DEFAULT_MODEL_BY_PROVIDER[llmProvider]) != null ? _ta : "gpt-4o";
2590
+ const llmApiKey = (_ya = (_xa = (_va = llmApiKeyByProvider[llmProvider]) != null ? _va : readString(env, "LLM_API_KEY")) != null ? _xa : (_wa = base == null ? void 0 : base.llm) == null ? void 0 : _wa.apiKey) != null ? _ya : "sk-retrivora-cancer-280590";
2591
+ const llmProfile = (_Ca = (_Ba = readString(env, "LLM_UNIVERSAL_PROFILE")) != null ? _Ba : (_Aa = (_za = base == null ? void 0 : base.llm) == null ? void 0 : _za.options) == null ? void 0 : _Aa.profile) != null ? _Ca : "litellm";
2592
+ const defaultEmbeddingProvider = isFreeTier ? "universal_rest" : "universal_rest";
2593
+ const embeddingProvider = (_Fa = (_Ea = readEnum(env, "EMBEDDING_PROVIDER", defaultEmbeddingProvider, EMBEDDING_PROVIDERS)) != null ? _Ea : (_Da = base == null ? void 0 : base.embedding) == null ? void 0 : _Da.provider) != null ? _Fa : defaultEmbeddingProvider;
2594
+ const embeddingBaseUrl = (_Ia = (_Ha = readString(env, "EMBEDDING_BASE_URL")) != null ? _Ha : (_Ga = base == null ? void 0 : base.embedding) == null ? void 0 : _Ga.baseUrl) != null ? _Ia : DEFAULT_FREE_GATEWAY;
2595
+ const embeddingModel = (_La = (_Ka = readString(env, "EMBEDDING_MODEL")) != null ? _Ka : (_Ja = base == null ? void 0 : base.embedding) == null ? void 0 : _Ja.model) != null ? _La : "text-embedding-004";
2596
+ const embeddingApiKey = (_Qa = (_Pa = (_Na = (_Ma = embeddingApiKeyByProvider[embeddingProvider]) != null ? _Ma : readString(env, "EMBEDDING_API_KEY")) != null ? _Na : readString(env, "LLM_API_KEY")) != null ? _Pa : (_Oa = base == null ? void 0 : base.embedding) == null ? void 0 : _Oa.apiKey) != null ? _Qa : "sk-retrivora-cancer-280590";
2597
+ const embeddingProfile = (_Ua = (_Ta = readString(env, "EMBEDDING_UNIVERSAL_PROFILE")) != null ? _Ta : (_Sa = (_Ra = base == null ? void 0 : base.embedding) == null ? void 0 : _Ra.options) == null ? void 0 : _Sa.profile) != null ? _Ua : "litellm";
2298
2598
  return __spreadProps(__spreadValues({
2299
2599
  projectId,
2300
- licenseKey: (_ma = (_la = readString(env, "RETRIVORA_LICENSE_KEY")) != null ? _la : readString(env, "LICENSE_KEY")) != null ? _ma : base == null ? void 0 : base.licenseKey,
2600
+ licenseKey,
2301
2601
  vectorDb: {
2302
2602
  provider: vectorProvider,
2303
- indexName: (_oa = (_na = readString(env, "VECTOR_DB_INDEX")) != null ? _na : vectorDbOptions.indexName) != null ? _oa : "rag-index",
2603
+ indexName: (_Wa = (_Va = readString(env, "VECTOR_DB_INDEX")) != null ? _Va : vectorDbOptions.indexName) != null ? _Wa : "rag-index",
2304
2604
  options: vectorDbOptions
2305
2605
  },
2306
2606
  llm: {
2307
2607
  provider: llmProvider,
2308
- model: (_qa = (_pa = readString(env, "LLM_MODEL")) != null ? _pa : DEFAULT_MODEL_BY_PROVIDER[llmProvider]) != null ? _qa : "gpt-4o",
2309
- apiKey: (_ra = llmApiKeyByProvider[llmProvider]) != null ? _ra : "",
2310
- baseUrl: readString(env, "LLM_BASE_URL"),
2608
+ model: llmModel,
2609
+ apiKey: llmApiKey,
2610
+ baseUrl: llmBaseUrl,
2311
2611
  systemPrompt: readString(env, "LLM_SYSTEM_PROMPT"),
2312
2612
  maxTokens: readNumber(env, "LLM_MAX_TOKENS", 4096),
2313
2613
  temperature: readNumber(env, "LLM_TEMPERATURE", 0.7),
2314
2614
  options: {
2315
- profile: readString(env, "LLM_UNIVERSAL_PROFILE"),
2615
+ profile: llmProfile,
2316
2616
  thinking: readString(env, "LLM_THINKING") === "true" || readString(env, "LLM_THINKING") === "enabled" ? true : readString(env, "LLM_THINKING") === "false" ? false : void 0,
2317
2617
  thinkingBudget: env.LLM_THINKING_BUDGET ? parseInt(env.LLM_THINKING_BUDGET, 10) : void 0
2318
2618
  }
2319
2619
  },
2320
2620
  embedding: {
2321
2621
  provider: embeddingProvider,
2322
- model: (_sa = readString(env, "EMBEDDING_MODEL")) != null ? _sa : "text-embedding-3-small",
2323
- apiKey: embeddingApiKeyByProvider[embeddingProvider],
2324
- baseUrl: readString(env, "EMBEDDING_BASE_URL"),
2622
+ model: embeddingModel,
2623
+ apiKey: embeddingApiKey,
2624
+ baseUrl: embeddingBaseUrl,
2325
2625
  dimensions: embeddingDimensions,
2326
2626
  queryPrefix: readString(env, "EMBEDDING_QUERY_PREFIX"),
2327
2627
  documentPrefix: readString(env, "EMBEDDING_DOCUMENT_PREFIX"),
2328
2628
  options: {
2329
- profile: readString(env, "EMBEDDING_UNIVERSAL_PROFILE")
2629
+ profile: embeddingProfile
2330
2630
  }
2331
2631
  },
2332
2632
  ui: {
2333
- title: (_ua = (_ta = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _ta : readString(env, "UI_TITLE")) != null ? _ua : "AI Assistant",
2334
- subtitle: (_wa = (_va = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _va : readString(env, "UI_SUBTITLE")) != null ? _wa : "Powered by RAG",
2335
- primaryColor: (_ya = (_xa = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _xa : readString(env, "UI_PRIMARY_COLOR")) != null ? _ya : "#10b981",
2336
- accentColor: (_Aa = (_za = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _za : readString(env, "UI_ACCENT_COLOR")) != null ? _Aa : "#3b82f6",
2337
- logoUrl: (_Ba = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _Ba : readString(env, "UI_LOGO_URL"),
2338
- placeholder: (_Da = (_Ca = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _Ca : readString(env, "UI_PLACEHOLDER")) != null ? _Da : "Ask me anything\u2026",
2339
- showSources: ((_Fa = (_Ea = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _Ea : readString(env, "UI_SHOW_SOURCES")) != null ? _Fa : "true") !== "false",
2340
- welcomeMessage: (_Ha = (_Ga = readString(env, "NEXT_PUBLIC_WELCOME_MESSAGE")) != null ? _Ga : readString(env, "UI_WELCOME_MESSAGE")) != null ? _Ha : "Hello! I'm your AI assistant. Ask me anything about your documents.",
2341
- visualStyle: (_Ja = (_Ia = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _Ia : readString(env, "UI_VISUAL_STYLE")) != null ? _Ja : "glass",
2342
- borderRadius: (_La = (_Ka = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _Ka : readString(env, "UI_BORDER_RADIUS")) != null ? _La : "xl",
2343
- allowUpload: ((_Na = (_Ma = readString(env, "NEXT_PUBLIC_ALLOW_UPLOAD")) != null ? _Ma : readString(env, "UI_ALLOW_UPLOAD")) != null ? _Na : "false") === "true"
2633
+ title: (_Ya = (_Xa = readString(env, "NEXT_PUBLIC_UI_TITLE")) != null ? _Xa : readString(env, "UI_TITLE")) != null ? _Ya : "AI Assistant",
2634
+ subtitle: (__a = (_Za = readString(env, "NEXT_PUBLIC_UI_SUBTITLE")) != null ? _Za : readString(env, "UI_SUBTITLE")) != null ? __a : "Powered by RAG",
2635
+ primaryColor: (_ab = (_$a = readString(env, "NEXT_PUBLIC_PRIMARY_COLOR")) != null ? _$a : readString(env, "UI_PRIMARY_COLOR")) != null ? _ab : "#10b981",
2636
+ accentColor: (_cb = (_bb = readString(env, "NEXT_PUBLIC_ACCENT_COLOR")) != null ? _bb : readString(env, "UI_ACCENT_COLOR")) != null ? _cb : "#3b82f6",
2637
+ logoUrl: (_db = readString(env, "NEXT_PUBLIC_LOGO_URL")) != null ? _db : readString(env, "UI_LOGO_URL"),
2638
+ placeholder: (_fb = (_eb = readString(env, "NEXT_PUBLIC_PLACEHOLDER")) != null ? _eb : readString(env, "UI_PLACEHOLDER")) != null ? _fb : "Ask me anything\u2026",
2639
+ showSources: ((_hb = (_gb = readString(env, "NEXT_PUBLIC_SHOW_SOURCES")) != null ? _gb : readString(env, "UI_SHOW_SOURCES")) != null ? _hb : "true") !== "false",
2640
+ welcomeMessage: (_jb = (_ib = readString(env, "NEXT_PUBLIC_WELCOME_MESSAGE")) != null ? _ib : readString(env, "UI_WELCOME_MESSAGE")) != null ? _jb : "Hello! I'm your AI assistant. Ask me anything about your documents.",
2641
+ visualStyle: (_lb = (_kb = readString(env, "NEXT_PUBLIC_UI_VISUAL_STYLE")) != null ? _kb : readString(env, "UI_VISUAL_STYLE")) != null ? _lb : "glass",
2642
+ borderRadius: (_nb = (_mb = readString(env, "NEXT_PUBLIC_UI_BORDER_RADIUS")) != null ? _mb : readString(env, "UI_BORDER_RADIUS")) != null ? _nb : "xl",
2643
+ allowUpload: ((_pb = (_ob = readString(env, "NEXT_PUBLIC_ALLOW_UPLOAD")) != null ? _ob : readString(env, "UI_ALLOW_UPLOAD")) != null ? _pb : "false") === "true"
2344
2644
  },
2345
2645
  rag: {
2346
2646
  topK: readNumber(env, "RAG_TOP_K", 5),
2347
2647
  scoreThreshold: readNumber(env, "RAG_SCORE_THRESHOLD", 0),
2348
2648
  chunkSize: readNumber(env, "RAG_CHUNK_SIZE", 1e3),
2349
2649
  chunkOverlap: readNumber(env, "RAG_CHUNK_OVERLAP", 200),
2350
- filterableFields: (_Oa = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _Oa.split(",").map((f) => f.trim()),
2650
+ filterableFields: (_qb = readString(env, "RAG_FILTERABLE_FIELDS")) == null ? void 0 : _qb.split(",").map((f) => f.trim()),
2351
2651
  // Query pipeline toggles — read from .env.local
2352
2652
  useQueryTransformation: readString(env, "RAG_USE_QUERY_TRANSFORMATION") === "true",
2353
2653
  useReranking: readString(env, "RAG_USE_RERANKING") === "true",
2354
2654
  useGraphRetrieval: readString(env, "RAG_USE_GRAPH_RETRIEVAL") === "true",
2355
- architecture: (_Pa = readString(env, "RAG_ARCHITECTURE")) != null ? _Pa : "simple",
2356
- chunkingStrategy: (_Qa = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _Qa : "recursive",
2655
+ architecture: (_rb = readString(env, "RAG_ARCHITECTURE")) != null ? _rb : "simple",
2656
+ chunkingStrategy: (_sb = readString(env, "RAG_CHUNKING_STRATEGY")) != null ? _sb : "recursive",
2357
2657
  uiMapping: (() => {
2358
2658
  const raw = readString(env, "RAG_UI_MAPPING");
2359
2659
  if (!raw) return void 0;
@@ -2366,7 +2666,7 @@ function getEnvConfig(env = process.env, base) {
2366
2666
  },
2367
2667
  telemetry: {
2368
2668
  enabled: telemetryEnabled,
2369
- url: (_Ua = (_Ta = (_Ra = readString(env, "TELEMETRY_URL")) != null ? _Ra : readString(env, "NEXT_PUBLIC_TELEMETRY_URL")) != null ? _Ta : (_Sa = base == null ? void 0 : base.telemetry) == null ? void 0 : _Sa.url) != null ? _Ua : process.env.NODE_ENV === "development" ? "http://localhost:3001/api/telemetry" : "https://retrivora.com/api/telemetry"
2669
+ url: (_wb = (_vb = (_tb = readString(env, "TELEMETRY_URL")) != null ? _tb : readString(env, "NEXT_PUBLIC_TELEMETRY_URL")) != null ? _vb : (_ub = base == null ? void 0 : base.telemetry) == null ? void 0 : _ub.url) != null ? _wb : process.env.NODE_ENV === "development" ? "http://localhost:3001/api/telemetry" : "https://retrivora.com/api/telemetry"
2370
2670
  }
2371
2671
  }, readString(env, "GRAPH_DB_PROVIDER") ? {
2372
2672
  graphDb: {
@@ -2853,10 +3153,12 @@ var AnthropicProvider = class {
2853
3153
  var import_axios = __toESM(require("axios"));
2854
3154
  var OllamaProvider = class {
2855
3155
  constructor(llmConfig, embeddingConfig) {
2856
- var _a2, _b;
2857
- const baseURL = (_a2 = llmConfig.baseUrl) != null ? _a2 : "http://localhost:11434";
3156
+ var _a2, _b, _c;
3157
+ const rawBaseURL = ((_a2 = llmConfig.baseUrl) != null ? _a2 : "http://localhost:11434").replace(/\/+$/, "");
3158
+ const baseURL = rawBaseURL.replace(/\/(?:api\/v1|v1|api)$/i, "");
2858
3159
  const timeout = Number((_b = llmConfig.options) == null ? void 0 : _b.timeout) || 3e5;
2859
- this.http = import_axios.default.create({ baseURL, timeout });
3160
+ const headers = ((_c = llmConfig.options) == null ? void 0 : _c.headers) || {};
3161
+ this.http = import_axios.default.create({ baseURL, timeout, headers });
2860
3162
  this.llmConfig = llmConfig;
2861
3163
  this.embeddingConfig = embeddingConfig;
2862
3164
  }
@@ -3705,7 +4007,7 @@ var UniversalLLMAdapter = class {
3705
4007
  });
3706
4008
  }
3707
4009
  async chat(messages, context) {
3708
- var _a2, _b;
4010
+ var _a2, _b, _c, _d, _e, _f;
3709
4011
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3710
4012
  const formattedMessages = [
3711
4013
  { role: "system", content: `${this.systemPrompt}
@@ -3730,8 +4032,26 @@ ${context != null ? context : "None"}` },
3730
4032
  temperature: this.temperature
3731
4033
  };
3732
4034
  }
4035
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4036
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4037
+ try {
4038
+ const _g2 = globalThis;
4039
+ if (typeof _g2.__retrivoraDispatchChat === "function") {
4040
+ const res = await _g2.__retrivoraDispatchChat({
4041
+ model: this.model,
4042
+ messages: formattedMessages,
4043
+ max_tokens: this.maxTokens,
4044
+ temperature: this.temperature
4045
+ }, this.apiKey);
4046
+ if ((_e = (_d = (_c = (_b = res == null ? void 0 : res.response) == null ? void 0 : _b.choices) == null ? void 0 : _c[0]) == null ? void 0 : _d.message) == null ? void 0 : _e.content) {
4047
+ return res.response.choices[0].message.content;
4048
+ }
4049
+ }
4050
+ } catch (e) {
4051
+ }
4052
+ }
3733
4053
  const { data } = await this.http.post(path2, payload);
3734
- const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
4054
+ const extractPath = (_f = this.opts.responseExtractPath) != null ? _f : "choices[0].message.content";
3735
4055
  const result = resolvePath(data, extractPath);
3736
4056
  if (result === void 0) {
3737
4057
  throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
@@ -3745,7 +4065,7 @@ ${context != null ? context : "None"}` },
3745
4065
  */
3746
4066
  chatStream(messages, context) {
3747
4067
  return __asyncGenerator(this, null, function* () {
3748
- var _a2, _b, _c;
4068
+ var _a2, _b, _c, _d, _e, _f, _g2;
3749
4069
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3750
4070
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
3751
4071
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
@@ -3776,19 +4096,45 @@ ${context != null ? context : "None"}` },
3776
4096
  stream: true
3777
4097
  };
3778
4098
  }
3779
- const response = yield new __await(fetch(url, {
3780
- method: "POST",
3781
- headers: this.resolvedHeaders,
3782
- body: JSON.stringify(payload)
3783
- }));
3784
- if (!response.ok) {
3785
- const errorText = yield new __await(response.text().catch(() => response.statusText));
3786
- throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
4099
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4100
+ let streamBody = null;
4101
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4102
+ try {
4103
+ const _g3 = globalThis;
4104
+ if (typeof _g3.__retrivoraDispatchChat === "function") {
4105
+ const res = yield new __await(_g3.__retrivoraDispatchChat({
4106
+ model: this.model,
4107
+ messages: formattedMessages,
4108
+ max_tokens: this.maxTokens,
4109
+ temperature: this.temperature,
4110
+ stream: true
4111
+ }, this.apiKey));
4112
+ if (res == null ? void 0 : res.stream) {
4113
+ streamBody = res.stream;
4114
+ } else if ((_f = (_e = (_d = (_c = res == null ? void 0 : res.response) == null ? void 0 : _c.choices) == null ? void 0 : _d[0]) == null ? void 0 : _e.message) == null ? void 0 : _f.content) {
4115
+ yield res.response.choices[0].message.content;
4116
+ return;
4117
+ }
4118
+ }
4119
+ } catch (e) {
4120
+ }
3787
4121
  }
3788
- if (!response.body) {
3789
- throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
4122
+ if (!streamBody) {
4123
+ const response = yield new __await(fetch(url, {
4124
+ method: "POST",
4125
+ headers: this.resolvedHeaders,
4126
+ body: JSON.stringify(payload)
4127
+ }));
4128
+ if (!response.ok) {
4129
+ const errorText = yield new __await(response.text().catch(() => response.statusText));
4130
+ throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
4131
+ }
4132
+ if (!response.body) {
4133
+ throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
4134
+ }
4135
+ streamBody = response.body;
3790
4136
  }
3791
- const reader = response.body.getReader();
4137
+ const reader = streamBody.getReader();
3792
4138
  const decoder = new TextDecoder("utf-8");
3793
4139
  let buffer = "";
3794
4140
  try {
@@ -3797,7 +4143,7 @@ ${context != null ? context : "None"}` },
3797
4143
  if (done) break;
3798
4144
  buffer += decoder.decode(value, { stream: true });
3799
4145
  const lines = buffer.split("\n");
3800
- buffer = (_c = lines.pop()) != null ? _c : "";
4146
+ buffer = (_g2 = lines.pop()) != null ? _g2 : "";
3801
4147
  for (const line of lines) {
3802
4148
  const trimmed = line.trim();
3803
4149
  if (!trimmed || trimmed === "data: [DONE]") continue;
@@ -3825,7 +4171,7 @@ ${context != null ? context : "None"}` },
3825
4171
  });
3826
4172
  }
3827
4173
  async embed(text) {
3828
- var _a2, _b;
4174
+ var _a2, _b, _c, _d;
3829
4175
  const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
3830
4176
  let payload;
3831
4177
  if (this.opts.embedPayloadTemplate) {
@@ -3839,105 +4185,49 @@ ${context != null ? context : "None"}` },
3839
4185
  input: text
3840
4186
  };
3841
4187
  }
4188
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4189
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4190
+ try {
4191
+ const _g2 = globalThis;
4192
+ if (typeof _g2.__retrivoraDispatchEmbedding === "function") {
4193
+ const res = await _g2.__retrivoraDispatchEmbedding({
4194
+ model: this.model,
4195
+ input: text
4196
+ }, this.apiKey);
4197
+ if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
4198
+ return res.data[0].embedding;
4199
+ }
4200
+ }
4201
+ } catch (e) {
4202
+ }
4203
+ }
3842
4204
  const { data } = await this.http.post(path2, payload);
3843
- const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
4205
+ const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
3844
4206
  const vector = resolvePath(data, extractPath);
3845
4207
  if (!Array.isArray(vector)) {
3846
4208
  throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
3847
4209
  }
3848
4210
  return vector;
3849
4211
  }
3850
- async batchEmbed(texts) {
3851
- const vectors = [];
3852
- for (const text of texts) {
3853
- vectors.push(await this.embed(text));
3854
- }
3855
- return vectors;
3856
- }
3857
- async ping() {
3858
- try {
3859
- if (this.opts.pingPath) {
3860
- await this.http.get(this.opts.pingPath);
3861
- }
3862
- return true;
3863
- } catch (err) {
3864
- console.error("[UniversalLLMAdapter] Ping failed:", err);
3865
- return false;
3866
- }
3867
- }
3868
- };
3869
-
3870
- // src/exceptions/index.ts
3871
- var RetrivoraError = class extends Error {
3872
- constructor(message, code, details) {
3873
- super(message);
3874
- this.name = new.target.name;
3875
- this.code = code;
3876
- this.details = details;
3877
- }
3878
- };
3879
- var ProviderNotFoundException = class extends RetrivoraError {
3880
- constructor(providerType, provider, details) {
3881
- super(`Unsupported ${providerType} provider: ${provider}`, "PROVIDER_NOT_FOUND", details);
3882
- }
3883
- };
3884
- var EmbeddingFailedException = class extends RetrivoraError {
3885
- constructor(message = "Embedding generation failed", details) {
3886
- super(message, "EMBEDDING_FAILED", details);
3887
- }
3888
- };
3889
- var RetrievalException = class extends RetrivoraError {
3890
- constructor(message = "Retrieval failed", details) {
3891
- super(message, "RETRIEVAL_FAILED", details);
3892
- }
3893
- };
3894
- var RateLimitException = class extends RetrivoraError {
3895
- constructor(message = "Provider rate limit exceeded", details) {
3896
- super(message, "RATE_LIMITED", details);
3897
- }
3898
- };
3899
- var ConfigurationException = class extends RetrivoraError {
3900
- constructor(message, details) {
3901
- super(message, "CONFIGURATION_ERROR", details);
3902
- }
3903
- };
3904
- var AuthenticationException = class extends RetrivoraError {
3905
- constructor(message = "Provider authentication failed", details) {
3906
- super(message, "AUTHENTICATION_ERROR", details);
3907
- }
3908
- };
3909
- function wrapError(err, defaultCode, defaultMessage) {
3910
- var _a2;
3911
- if (err instanceof RetrivoraError) {
3912
- return err;
3913
- }
3914
- const error = err;
3915
- const message = (error == null ? void 0 : error.message) || defaultMessage || String(err);
3916
- const status = (error == null ? void 0 : error.status) || (error == null ? void 0 : error.statusCode) || ((_a2 = error == null ? void 0 : error.response) == null ? void 0 : _a2.status);
3917
- const code = error == null ? void 0 : error.code;
3918
- if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
3919
- return new RateLimitException(message, err);
3920
- }
3921
- if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
3922
- return new AuthenticationException(message, err);
4212
+ async batchEmbed(texts) {
4213
+ const vectors = [];
4214
+ for (const text of texts) {
4215
+ vectors.push(await this.embed(text));
4216
+ }
4217
+ return vectors;
3923
4218
  }
3924
- switch (defaultCode) {
3925
- case "PROVIDER_NOT_FOUND":
3926
- return new ProviderNotFoundException("provider", message, err);
3927
- case "EMBEDDING_FAILED":
3928
- return new EmbeddingFailedException(message, err);
3929
- case "RETRIEVAL_FAILED":
3930
- return new RetrievalException(message, err);
3931
- case "RATE_LIMITED":
3932
- return new RateLimitException(message, err);
3933
- case "CONFIGURATION_ERROR":
3934
- return new ConfigurationException(message, err);
3935
- case "AUTHENTICATION_ERROR":
3936
- return new AuthenticationException(message, err);
3937
- default:
3938
- return new RetrivoraError(message, defaultCode, err);
4219
+ async ping() {
4220
+ try {
4221
+ if (this.opts.pingPath) {
4222
+ await this.http.get(this.opts.pingPath);
4223
+ }
4224
+ return true;
4225
+ } catch (err) {
4226
+ console.error("[UniversalLLMAdapter] Ping failed:", err);
4227
+ return false;
4228
+ }
3939
4229
  }
3940
- }
4230
+ };
3941
4231
 
3942
4232
  // src/llm/LLMFactory.ts
3943
4233
  var customProviders = /* @__PURE__ */ new Map();
@@ -4336,211 +4626,6 @@ var ConfigValidator = class {
4336
4626
  }
4337
4627
  };
4338
4628
 
4339
- // src/core/LicenseVerifier.ts
4340
- var crypto2 = __toESM(require("crypto"));
4341
- var LicenseVerifier = class {
4342
- /**
4343
- * Decodes, verifies signature, and checks metadata of a license key.
4344
- *
4345
- * @param licenseKey - Base64url signed JWT license key.
4346
- * @param currentProjectId - Project namespace ID.
4347
- * @param publicKeyOverride - Optional override for unit/integration tests.
4348
- */
4349
- static verify(licenseKey, currentProjectId, provider, publicKeyOverride) {
4350
- const isProduction = process.env.NODE_ENV === "production";
4351
- const now = Date.now();
4352
- if (licenseKey) {
4353
- const cacheKey = `${licenseKey}:${currentProjectId}:${provider || ""}:${publicKeyOverride || ""}`;
4354
- const cached = this.cache.get(cacheKey);
4355
- if (cached && now - cached.cachedAt < this.CACHE_TTL_MS) {
4356
- const currentTimestampSec = Math.floor(now / 1e3);
4357
- if (cached.payload.expiresAt && currentTimestampSec > cached.payload.expiresAt) {
4358
- this.cache.delete(cacheKey);
4359
- } else {
4360
- return cached.payload;
4361
- }
4362
- }
4363
- }
4364
- if (!licenseKey) {
4365
- if (isProduction) {
4366
- throw new ConfigurationException(
4367
- "[Retrivora SDK] License key (licenseKey) is required in production environments."
4368
- );
4369
- }
4370
- console.warn(
4371
- `\x1B[33m[Retrivora SDK] WARNING: Running without a license key. This namespace (${currentProjectId}) is permitted for local development only.\x1B[0m`
4372
- );
4373
- return {
4374
- projectId: currentProjectId,
4375
- expiresAt: Math.floor(Date.now() / 1e3) + 86400,
4376
- // Valid for 24h
4377
- tier: "hobby"
4378
- };
4379
- }
4380
- try {
4381
- const parts = licenseKey.split(".");
4382
- if (parts.length !== 3) {
4383
- throw new Error("Malformed token structure (expected 3 parts).");
4384
- }
4385
- const [headerB64, payloadB64, signatureB64] = parts;
4386
- const dataToVerify = `${headerB64}.${payloadB64}`;
4387
- const publicKey = publicKeyOverride != null ? publicKeyOverride : this.PUBLIC_KEY;
4388
- const signature = Buffer.from(signatureB64, "base64url");
4389
- const data = Buffer.from(dataToVerify);
4390
- const isValid = crypto2.verify(
4391
- "sha256",
4392
- data,
4393
- publicKey,
4394
- signature
4395
- );
4396
- if (!isValid) {
4397
- throw new Error("Signature verification failed.");
4398
- }
4399
- const payload = JSON.parse(
4400
- Buffer.from(payloadB64, "base64url").toString("utf8")
4401
- );
4402
- if (!payload.projectId) {
4403
- throw new Error('License payload is missing "projectId".');
4404
- }
4405
- if (payload.projectId !== currentProjectId) {
4406
- throw new Error(
4407
- `Project ID mismatch. License is bound to project namespace "${payload.projectId}" but configuration has "${currentProjectId}".`
4408
- );
4409
- }
4410
- if (!payload.expiresAt) {
4411
- throw new Error('License payload is missing expiration ("expiresAt").');
4412
- }
4413
- const currentTimestampSec = Math.floor(Date.now() / 1e3);
4414
- if (currentTimestampSec > payload.expiresAt) {
4415
- throw new Error(
4416
- `License key has expired. Expiration: ${new Date(
4417
- payload.expiresAt * 1e3
4418
- ).toDateString()}`
4419
- );
4420
- }
4421
- if (isProduction && provider) {
4422
- const tier = (payload.tier || "").toLowerCase();
4423
- const normalizedProvider = provider.toLowerCase();
4424
- if (tier === "hobby") {
4425
- const allowedHobby = ["postgresql", "pgvector", "supabase"];
4426
- if (!allowedHobby.includes(normalizedProvider)) {
4427
- throw new Error(
4428
- `The database provider "${provider}" is not allowed on the Hobby tier. Hobby tier is restricted to PostgreSQL and Supabase. Please upgrade to a Developer Pro or Enterprise plan.`
4429
- );
4430
- }
4431
- } else if (tier === "pro" || tier === "developer_pro" || tier === "premium" || tier === "growth" || tier === "free_trial" || tier === "free_tier" || tier === "free") {
4432
- const allowedPro = [
4433
- "postgresql",
4434
- "pgvector",
4435
- "supabase",
4436
- "pinecone",
4437
- "qdrant",
4438
- "mongodb",
4439
- "milvus",
4440
- "chroma",
4441
- "chromadb"
4442
- ];
4443
- if (!allowedPro.includes(normalizedProvider)) {
4444
- throw new Error(
4445
- `The database provider "${provider}" is not allowed on the Developer Pro tier. Please upgrade to an Enterprise plan.`
4446
- );
4447
- }
4448
- }
4449
- }
4450
- if (licenseKey) {
4451
- const cacheKey = `${licenseKey}:${currentProjectId}:${provider || ""}:${publicKeyOverride || ""}`;
4452
- this.cache.set(cacheKey, { payload, cachedAt: now });
4453
- }
4454
- return payload;
4455
- } catch (err) {
4456
- const message = err instanceof Error ? err.message : String(err);
4457
- throw new ConfigurationException(
4458
- `[Retrivora SDK] License key validation failed: ${message}`
4459
- );
4460
- }
4461
- }
4462
- static async verifyIP(licenseKey) {
4463
- if (!licenseKey) return;
4464
- try {
4465
- const parts = licenseKey.split(".");
4466
- if (parts.length !== 3) return;
4467
- const [, payloadB64] = parts;
4468
- const payload = JSON.parse(
4469
- Buffer.from(payloadB64, "base64url").toString("utf8")
4470
- );
4471
- const tier = (payload.tier || "").toLowerCase();
4472
- if (tier === "enterprise" || tier === "custom") {
4473
- return;
4474
- }
4475
- if (payload.ipv4 || payload.ipv6) {
4476
- let currentIpv4 = "";
4477
- let currentIpv6 = "";
4478
- try {
4479
- const res = await fetch("https://api4.ipify.org?format=json", { signal: AbortSignal.timeout(3e3) });
4480
- const data = await res.json();
4481
- currentIpv4 = data.ip;
4482
- } catch (e) {
4483
- }
4484
- try {
4485
- const res = await fetch("https://api6.ipify.org?format=json", { signal: AbortSignal.timeout(3e3) });
4486
- const data = await res.json();
4487
- currentIpv6 = data.ip;
4488
- } catch (e) {
4489
- }
4490
- const localIps = [];
4491
- try {
4492
- const osModule = require("os");
4493
- const interfaces = osModule.networkInterfaces();
4494
- for (const name of Object.keys(interfaces)) {
4495
- for (const iface of interfaces[name] || []) {
4496
- if (!iface.internal) {
4497
- localIps.push(iface.address);
4498
- }
4499
- }
4500
- }
4501
- } catch (e) {
4502
- }
4503
- const isIpv4Match = payload.ipv4 && (currentIpv4 === payload.ipv4 || localIps.includes(payload.ipv4));
4504
- const isIpv6Match = payload.ipv6 && (currentIpv6 === payload.ipv6 || localIps.includes(payload.ipv6));
4505
- if (payload.ipv4 && payload.ipv6) {
4506
- if (!isIpv4Match && !isIpv6Match) {
4507
- throw new Error(
4508
- `License key access restricted. This license key was created for a different system (authorized IPv4: ${payload.ipv4}, IPv6: ${payload.ipv6}) and cannot be used on this machine.`
4509
- );
4510
- }
4511
- } else if (payload.ipv4 && !isIpv4Match) {
4512
- throw new Error(
4513
- `License key access restricted. This license key was created for a different system (authorized IPv4: ${payload.ipv4}) and cannot be used on this machine.`
4514
- );
4515
- } else if (payload.ipv6 && !isIpv6Match) {
4516
- throw new Error(
4517
- `License key access restricted. This license key was created for a different system (authorized IPv6: ${payload.ipv6}) and cannot be used on this machine.`
4518
- );
4519
- }
4520
- }
4521
- } catch (err) {
4522
- if (err.message.includes("License key access restricted")) {
4523
- throw new ConfigurationException(`[Retrivora SDK] ${err.message}`);
4524
- }
4525
- }
4526
- }
4527
- };
4528
- // A simple in-memory cache to save CPU overhead of cryptographic signature checks.
4529
- LicenseVerifier.cache = /* @__PURE__ */ new Map();
4530
- LicenseVerifier.CACHE_TTL_MS = 60 * 1e3;
4531
- // Cache verified license for 60 seconds
4532
- // Retrivora's Public Key used to verify the license key signature.
4533
- // In production, this matches the private key held by Retrivora SaaS.
4534
- LicenseVerifier.PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
4535
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArMieCkCBtTfByRNOserm
4536
- XM4T0Am29JyNzB4XQPe+WJJ56CN73H1HLXx9n1ByFcxkEJ9po+SURj5DnvUXwEy+
4537
- xstx33wDPpVmcMQe33j5CRYS0S4gICiNqIRN7XkTlJtrcXqrmh1/hdOAwfRbjO1T
4538
- NVtORHwUwWfI/lBLyxUPGtKPed+0fQ7NtcW4o00JXxs3EjCB5BV9CbnXoTjQb/4h
4539
- iwZof4l8rb7oaNzOsVg0RSyxsETX7Ex5sI0CjBz1p2mYp4oVhw/A/h8Ls7H0XzjC
4540
- +Eb6BLtsytt5JHoSp0jExLlCpDmpys2L+kETcBVx+IqiXtdN1n6KbkksvEDUVzLI
4541
- MwIDAQAB
4542
- -----END PUBLIC KEY-----`;
4543
-
4544
4629
  // src/rag/DocumentChunker.ts
4545
4630
  var DocumentChunker = class {
4546
4631
  constructor(chunkSize = 1e3, chunkOverlap = 200, separators = ["\n# ", "\n## ", "\n### ", "\n#### ", "\n\n", "\n", " ", ""]) {
@@ -8467,8 +8552,11 @@ ${m.content}`).join("\n\n---\n\n");
8467
8552
  fullSources = fullSources.slice(0, topK);
8468
8553
  }
8469
8554
  const rerankMs = performance.now() - rerankStart;
8470
- let context = fullSources.length ? fullSources.map((m, i) => `[Source ${i + 1}]
8471
- ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
8555
+ let context = fullSources.length ? fullSources.map((m, i) => {
8556
+ var _a3;
8557
+ return `[Source ${i + 1}]
8558
+ ${(_a3 = m == null ? void 0 : m.content) != null ? _a3 : ""}`;
8559
+ }).join("\n\n---\n\n") : "No relevant context found.";
8472
8560
  let displayCount = 15;
8473
8561
  if (hasMetadataFilter) {
8474
8562
  displayCount = fullSources.length;
@@ -8552,7 +8640,10 @@ ${context}`;
8552
8640
  if (isSimulatedThinking) {
8553
8641
  finalRestrictionSuffix += "\n\n(IMPORTANT: You must think step-by-step before answering. Write your thinking process inside a `<think>...</think>` block. Write the `<think>` block first, then follow it with your final response. Keep the thinking process thorough and detailed. Example: `<think>Thinking details here...</think>Final answer text here.`)";
8554
8642
  }
8555
- const hardenedHistory = [...history];
8643
+ const hardenedHistory = (Array.isArray(history) ? history : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
8644
+ role: m.role || "user",
8645
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
8646
+ }));
8556
8647
  const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
8557
8648
  const messages = [...hardenedHistory, userQuestion];
8558
8649
  const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
@@ -8699,13 +8790,13 @@ ${context}`;
8699
8790
  rewrittenQuery,
8700
8791
  systemPrompt,
8701
8792
  userPrompt: question + finalRestrictionSuffix,
8702
- chunks: sources.map((s) => {
8703
- var _a3;
8793
+ chunks: (sources || []).filter(Boolean).map((s) => {
8794
+ var _a3, _b2;
8704
8795
  return {
8705
8796
  id: s.id,
8706
8797
  score: s.score,
8707
- content: s.content,
8708
- metadata: (_a3 = s.metadata) != null ? _a3 : {},
8798
+ content: (_a3 = s == null ? void 0 : s.content) != null ? _a3 : "",
8799
+ metadata: (_b2 = s == null ? void 0 : s.metadata) != null ? _b2 : {},
8709
8800
  namespace: ns
8710
8801
  };
8711
8802
  }),
@@ -9606,6 +9697,7 @@ var import_server = require("next/server");
9606
9697
  // src/core/DatabaseStorage.ts
9607
9698
  var fs = __toESM(require("fs"));
9608
9699
  var path = __toESM(require("path"));
9700
+ var os = __toESM(require("os"));
9609
9701
  var DatabaseStorage = class {
9610
9702
  constructor(config) {
9611
9703
  // Dynamic PG Pool
@@ -9614,7 +9706,8 @@ var DatabaseStorage = class {
9614
9706
  this.mongoClient = null;
9615
9707
  this.mongoDbName = "";
9616
9708
  // Filesystem fallback configuration
9617
- this.fallbackDir = path.join(process.cwd(), ".retrivora");
9709
+ this.isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
9710
+ this.fallbackDir = this.isServerless ? path.join(os.tmpdir(), ".retrivora") : path.join(process.cwd(), ".retrivora");
9618
9711
  this.historyFile = path.join(this.fallbackDir, "history.json");
9619
9712
  this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
9620
9713
  var _a2, _b, _c, _d, _e;
@@ -9744,10 +9837,27 @@ var DatabaseStorage = class {
9744
9837
  }
9745
9838
  // Helper for FS fallback writes
9746
9839
  writeLocalFile(filePath, data) {
9747
- if (!fs.existsSync(this.fallbackDir)) {
9748
- fs.mkdirSync(this.fallbackDir, { recursive: true });
9840
+ try {
9841
+ const dir = path.dirname(filePath);
9842
+ if (!fs.existsSync(dir)) {
9843
+ fs.mkdirSync(dir, { recursive: true });
9844
+ }
9845
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9846
+ } catch (err) {
9847
+ if (err.code === "EROFS") {
9848
+ try {
9849
+ const tmpDir = path.join(os.tmpdir(), ".retrivora");
9850
+ if (!fs.existsSync(tmpDir)) {
9851
+ fs.mkdirSync(tmpDir, { recursive: true });
9852
+ }
9853
+ const tmpFilePath = path.join(tmpDir, path.basename(filePath));
9854
+ fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), "utf-8");
9855
+ } catch (e) {
9856
+ }
9857
+ } else {
9858
+ console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
9859
+ }
9749
9860
  }
9750
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9751
9861
  }
9752
9862
  // ─── Message History Operations ──────────────────────────────────────────
9753
9863
  async saveMessage(sessionId, message) {
@@ -10179,7 +10289,17 @@ function createChatHandler(configOrPlugin, options) {
10179
10289
  if (rateLimited) return rateLimited;
10180
10290
  try {
10181
10291
  const body = await req.json();
10182
- const { message: rawMessage, history = [], namespace, sessionId = "default", messageId, userMessageId } = body;
10292
+ const bodyObj = body != null ? body : {};
10293
+ let rawMessage = bodyObj.message;
10294
+ if (!rawMessage && Array.isArray(bodyObj.messages) && bodyObj.messages.length > 0) {
10295
+ const lastMsg = bodyObj.messages[bodyObj.messages.length - 1];
10296
+ rawMessage = typeof lastMsg === "string" ? lastMsg : (lastMsg == null ? void 0 : lastMsg.content) || (lastMsg == null ? void 0 : lastMsg.text);
10297
+ }
10298
+ if (!rawMessage && typeof bodyObj.prompt === "string") {
10299
+ rawMessage = bodyObj.prompt;
10300
+ }
10301
+ const history = bodyObj.history || (Array.isArray(bodyObj.messages) ? bodyObj.messages.slice(0, -1) : []);
10302
+ const { namespace, sessionId = "default", messageId, userMessageId } = bodyObj;
10183
10303
  const sanitized = sanitizeInput(rawMessage != null ? rawMessage : "");
10184
10304
  if (!sanitized.ok) {
10185
10305
  return import_server.NextResponse.json({ error: { code: "INVALID_INPUT", message: sanitized.error } }, { status: 400 });
@@ -10231,7 +10351,17 @@ function createStreamHandler(configOrPlugin, options) {
10231
10351
  headers: { "Content-Type": "application/json" }
10232
10352
  });
10233
10353
  }
10234
- const { message: rawMessage, history = [], namespace, sessionId = "default", messageId, userMessageId } = body;
10354
+ const bodyObj = body != null ? body : {};
10355
+ let rawMessage = bodyObj.message;
10356
+ if (!rawMessage && Array.isArray(bodyObj.messages) && bodyObj.messages.length > 0) {
10357
+ const lastMsg = bodyObj.messages[bodyObj.messages.length - 1];
10358
+ rawMessage = typeof lastMsg === "string" ? lastMsg : (lastMsg == null ? void 0 : lastMsg.content) || (lastMsg == null ? void 0 : lastMsg.text);
10359
+ }
10360
+ if (!rawMessage && typeof bodyObj.prompt === "string") {
10361
+ rawMessage = bodyObj.prompt;
10362
+ }
10363
+ const history = bodyObj.history || (Array.isArray(bodyObj.messages) ? bodyObj.messages.slice(0, -1) : []);
10364
+ const { namespace, sessionId = "default", messageId, userMessageId } = bodyObj;
10235
10365
  const sanitized = sanitizeInput(rawMessage != null ? rawMessage : "");
10236
10366
  if (!sanitized.ok) {
10237
10367
  return new Response(