@retrivora-ai/rag-engine 2.0.5 → 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.
@@ -3907,7 +3907,7 @@ var UniversalLLMAdapter = class {
3907
3907
  });
3908
3908
  }
3909
3909
  async chat(messages, context) {
3910
- var _a2, _b;
3910
+ var _a2, _b, _c, _d, _e, _f;
3911
3911
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3912
3912
  const formattedMessages = [
3913
3913
  { role: "system", content: `${this.systemPrompt}
@@ -3932,8 +3932,26 @@ ${context != null ? context : "None"}` },
3932
3932
  temperature: this.temperature
3933
3933
  };
3934
3934
  }
3935
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
3936
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
3937
+ try {
3938
+ const _g2 = globalThis;
3939
+ if (typeof _g2.__retrivoraDispatchChat === "function") {
3940
+ const res = await _g2.__retrivoraDispatchChat({
3941
+ model: this.model,
3942
+ messages: formattedMessages,
3943
+ max_tokens: this.maxTokens,
3944
+ temperature: this.temperature
3945
+ }, this.apiKey);
3946
+ 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) {
3947
+ return res.response.choices[0].message.content;
3948
+ }
3949
+ }
3950
+ } catch (e) {
3951
+ }
3952
+ }
3935
3953
  const { data } = await this.http.post(path2, payload);
3936
- const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
3954
+ const extractPath = (_f = this.opts.responseExtractPath) != null ? _f : "choices[0].message.content";
3937
3955
  const result = resolvePath(data, extractPath);
3938
3956
  if (result === void 0) {
3939
3957
  throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
@@ -3947,7 +3965,7 @@ ${context != null ? context : "None"}` },
3947
3965
  */
3948
3966
  chatStream(messages, context) {
3949
3967
  return __asyncGenerator(this, null, function* () {
3950
- var _a2, _b, _c;
3968
+ var _a2, _b, _c, _d, _e, _f, _g2;
3951
3969
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3952
3970
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
3953
3971
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
@@ -3978,19 +3996,45 @@ ${context != null ? context : "None"}` },
3978
3996
  stream: true
3979
3997
  };
3980
3998
  }
3981
- const response = yield new __await(fetch(url, {
3982
- method: "POST",
3983
- headers: this.resolvedHeaders,
3984
- body: JSON.stringify(payload)
3985
- }));
3986
- if (!response.ok) {
3987
- const errorText = yield new __await(response.text().catch(() => response.statusText));
3988
- throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
3999
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4000
+ let streamBody = null;
4001
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4002
+ try {
4003
+ const _g3 = globalThis;
4004
+ if (typeof _g3.__retrivoraDispatchChat === "function") {
4005
+ const res = yield new __await(_g3.__retrivoraDispatchChat({
4006
+ model: this.model,
4007
+ messages: formattedMessages,
4008
+ max_tokens: this.maxTokens,
4009
+ temperature: this.temperature,
4010
+ stream: true
4011
+ }, this.apiKey));
4012
+ if (res == null ? void 0 : res.stream) {
4013
+ streamBody = res.stream;
4014
+ } 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) {
4015
+ yield res.response.choices[0].message.content;
4016
+ return;
4017
+ }
4018
+ }
4019
+ } catch (e) {
4020
+ }
3989
4021
  }
3990
- if (!response.body) {
3991
- throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
4022
+ if (!streamBody) {
4023
+ const response = yield new __await(fetch(url, {
4024
+ method: "POST",
4025
+ headers: this.resolvedHeaders,
4026
+ body: JSON.stringify(payload)
4027
+ }));
4028
+ if (!response.ok) {
4029
+ const errorText = yield new __await(response.text().catch(() => response.statusText));
4030
+ throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
4031
+ }
4032
+ if (!response.body) {
4033
+ throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
4034
+ }
4035
+ streamBody = response.body;
3992
4036
  }
3993
- const reader = response.body.getReader();
4037
+ const reader = streamBody.getReader();
3994
4038
  const decoder = new TextDecoder("utf-8");
3995
4039
  let buffer = "";
3996
4040
  try {
@@ -3999,7 +4043,7 @@ ${context != null ? context : "None"}` },
3999
4043
  if (done) break;
4000
4044
  buffer += decoder.decode(value, { stream: true });
4001
4045
  const lines = buffer.split("\n");
4002
- buffer = (_c = lines.pop()) != null ? _c : "";
4046
+ buffer = (_g2 = lines.pop()) != null ? _g2 : "";
4003
4047
  for (const line of lines) {
4004
4048
  const trimmed = line.trim();
4005
4049
  if (!trimmed || trimmed === "data: [DONE]") continue;
@@ -4027,7 +4071,7 @@ ${context != null ? context : "None"}` },
4027
4071
  });
4028
4072
  }
4029
4073
  async embed(text) {
4030
- var _a2, _b;
4074
+ var _a2, _b, _c, _d;
4031
4075
  const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
4032
4076
  let payload;
4033
4077
  if (this.opts.embedPayloadTemplate) {
@@ -4041,8 +4085,24 @@ ${context != null ? context : "None"}` },
4041
4085
  input: text
4042
4086
  };
4043
4087
  }
4088
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4089
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4090
+ try {
4091
+ const _g2 = globalThis;
4092
+ if (typeof _g2.__retrivoraDispatchEmbedding === "function") {
4093
+ const res = await _g2.__retrivoraDispatchEmbedding({
4094
+ model: this.model,
4095
+ input: text
4096
+ }, this.apiKey);
4097
+ if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
4098
+ return res.data[0].embedding;
4099
+ }
4100
+ }
4101
+ } catch (e) {
4102
+ }
4103
+ }
4044
4104
  const { data } = await this.http.post(path2, payload);
4045
- const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
4105
+ const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
4046
4106
  const vector = resolvePath(data, extractPath);
4047
4107
  if (!Array.isArray(vector)) {
4048
4108
  throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
@@ -8378,8 +8438,11 @@ ${m.content}`).join("\n\n---\n\n");
8378
8438
  fullSources = fullSources.slice(0, topK);
8379
8439
  }
8380
8440
  const rerankMs = performance.now() - rerankStart;
8381
- let context = fullSources.length ? fullSources.map((m, i) => `[Source ${i + 1}]
8382
- ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
8441
+ let context = fullSources.length ? fullSources.map((m, i) => {
8442
+ var _a3;
8443
+ return `[Source ${i + 1}]
8444
+ ${(_a3 = m == null ? void 0 : m.content) != null ? _a3 : ""}`;
8445
+ }).join("\n\n---\n\n") : "No relevant context found.";
8383
8446
  let displayCount = 15;
8384
8447
  if (hasMetadataFilter) {
8385
8448
  displayCount = fullSources.length;
@@ -8463,7 +8526,10 @@ ${context}`;
8463
8526
  if (isSimulatedThinking) {
8464
8527
  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.`)";
8465
8528
  }
8466
- const hardenedHistory = [...history];
8529
+ const hardenedHistory = (Array.isArray(history) ? history : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
8530
+ role: m.role || "user",
8531
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
8532
+ }));
8467
8533
  const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
8468
8534
  const messages = [...hardenedHistory, userQuestion];
8469
8535
  const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
@@ -8610,13 +8676,13 @@ ${context}`;
8610
8676
  rewrittenQuery,
8611
8677
  systemPrompt,
8612
8678
  userPrompt: question + finalRestrictionSuffix,
8613
- chunks: sources.map((s) => {
8614
- var _a3;
8679
+ chunks: (sources || []).filter(Boolean).map((s) => {
8680
+ var _a3, _b2;
8615
8681
  return {
8616
8682
  id: s.id,
8617
8683
  score: s.score,
8618
- content: s.content,
8619
- metadata: (_a3 = s.metadata) != null ? _a3 : {},
8684
+ content: (_a3 = s == null ? void 0 : s.content) != null ? _a3 : "",
8685
+ metadata: (_b2 = s == null ? void 0 : s.metadata) != null ? _b2 : {},
8620
8686
  namespace: ns
8621
8687
  };
8622
8688
  }),
@@ -9218,6 +9284,7 @@ ${csv.trim()}`);
9218
9284
  // src/core/DatabaseStorage.ts
9219
9285
  var fs = __toESM(require("fs"));
9220
9286
  var path = __toESM(require("path"));
9287
+ var os = __toESM(require("os"));
9221
9288
  var DatabaseStorage = class {
9222
9289
  constructor(config) {
9223
9290
  // Dynamic PG Pool
@@ -9226,7 +9293,8 @@ var DatabaseStorage = class {
9226
9293
  this.mongoClient = null;
9227
9294
  this.mongoDbName = "";
9228
9295
  // Filesystem fallback configuration
9229
- this.fallbackDir = path.join(process.cwd(), ".retrivora");
9296
+ this.isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
9297
+ this.fallbackDir = this.isServerless ? path.join(os.tmpdir(), ".retrivora") : path.join(process.cwd(), ".retrivora");
9230
9298
  this.historyFile = path.join(this.fallbackDir, "history.json");
9231
9299
  this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
9232
9300
  var _a2, _b, _c, _d, _e;
@@ -9356,10 +9424,27 @@ var DatabaseStorage = class {
9356
9424
  }
9357
9425
  // Helper for FS fallback writes
9358
9426
  writeLocalFile(filePath, data) {
9359
- if (!fs.existsSync(this.fallbackDir)) {
9360
- fs.mkdirSync(this.fallbackDir, { recursive: true });
9427
+ try {
9428
+ const dir = path.dirname(filePath);
9429
+ if (!fs.existsSync(dir)) {
9430
+ fs.mkdirSync(dir, { recursive: true });
9431
+ }
9432
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9433
+ } catch (err) {
9434
+ if (err.code === "EROFS") {
9435
+ try {
9436
+ const tmpDir = path.join(os.tmpdir(), ".retrivora");
9437
+ if (!fs.existsSync(tmpDir)) {
9438
+ fs.mkdirSync(tmpDir, { recursive: true });
9439
+ }
9440
+ const tmpFilePath = path.join(tmpDir, path.basename(filePath));
9441
+ fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), "utf-8");
9442
+ } catch (e) {
9443
+ }
9444
+ } else {
9445
+ console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
9446
+ }
9361
9447
  }
9362
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9363
9448
  }
9364
9449
  // ─── Message History Operations ──────────────────────────────────────────
9365
9450
  async saveMessage(sessionId, message) {
@@ -3872,7 +3872,7 @@ var UniversalLLMAdapter = class {
3872
3872
  });
3873
3873
  }
3874
3874
  async chat(messages, context) {
3875
- var _a2, _b;
3875
+ var _a2, _b, _c, _d, _e, _f;
3876
3876
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3877
3877
  const formattedMessages = [
3878
3878
  { role: "system", content: `${this.systemPrompt}
@@ -3897,8 +3897,26 @@ ${context != null ? context : "None"}` },
3897
3897
  temperature: this.temperature
3898
3898
  };
3899
3899
  }
3900
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
3901
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
3902
+ try {
3903
+ const _g2 = globalThis;
3904
+ if (typeof _g2.__retrivoraDispatchChat === "function") {
3905
+ const res = await _g2.__retrivoraDispatchChat({
3906
+ model: this.model,
3907
+ messages: formattedMessages,
3908
+ max_tokens: this.maxTokens,
3909
+ temperature: this.temperature
3910
+ }, this.apiKey);
3911
+ 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) {
3912
+ return res.response.choices[0].message.content;
3913
+ }
3914
+ }
3915
+ } catch (e) {
3916
+ }
3917
+ }
3900
3918
  const { data } = await this.http.post(path2, payload);
3901
- const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
3919
+ const extractPath = (_f = this.opts.responseExtractPath) != null ? _f : "choices[0].message.content";
3902
3920
  const result = resolvePath(data, extractPath);
3903
3921
  if (result === void 0) {
3904
3922
  throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
@@ -3912,7 +3930,7 @@ ${context != null ? context : "None"}` },
3912
3930
  */
3913
3931
  chatStream(messages, context) {
3914
3932
  return __asyncGenerator(this, null, function* () {
3915
- var _a2, _b, _c;
3933
+ var _a2, _b, _c, _d, _e, _f, _g2;
3916
3934
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3917
3935
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
3918
3936
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
@@ -3943,19 +3961,45 @@ ${context != null ? context : "None"}` },
3943
3961
  stream: true
3944
3962
  };
3945
3963
  }
3946
- const response = yield new __await(fetch(url, {
3947
- method: "POST",
3948
- headers: this.resolvedHeaders,
3949
- body: JSON.stringify(payload)
3950
- }));
3951
- if (!response.ok) {
3952
- const errorText = yield new __await(response.text().catch(() => response.statusText));
3953
- throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
3964
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
3965
+ let streamBody = null;
3966
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
3967
+ try {
3968
+ const _g3 = globalThis;
3969
+ if (typeof _g3.__retrivoraDispatchChat === "function") {
3970
+ const res = yield new __await(_g3.__retrivoraDispatchChat({
3971
+ model: this.model,
3972
+ messages: formattedMessages,
3973
+ max_tokens: this.maxTokens,
3974
+ temperature: this.temperature,
3975
+ stream: true
3976
+ }, this.apiKey));
3977
+ if (res == null ? void 0 : res.stream) {
3978
+ streamBody = res.stream;
3979
+ } 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) {
3980
+ yield res.response.choices[0].message.content;
3981
+ return;
3982
+ }
3983
+ }
3984
+ } catch (e) {
3985
+ }
3954
3986
  }
3955
- if (!response.body) {
3956
- throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
3987
+ if (!streamBody) {
3988
+ const response = yield new __await(fetch(url, {
3989
+ method: "POST",
3990
+ headers: this.resolvedHeaders,
3991
+ body: JSON.stringify(payload)
3992
+ }));
3993
+ if (!response.ok) {
3994
+ const errorText = yield new __await(response.text().catch(() => response.statusText));
3995
+ throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
3996
+ }
3997
+ if (!response.body) {
3998
+ throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
3999
+ }
4000
+ streamBody = response.body;
3957
4001
  }
3958
- const reader = response.body.getReader();
4002
+ const reader = streamBody.getReader();
3959
4003
  const decoder = new TextDecoder("utf-8");
3960
4004
  let buffer = "";
3961
4005
  try {
@@ -3964,7 +4008,7 @@ ${context != null ? context : "None"}` },
3964
4008
  if (done) break;
3965
4009
  buffer += decoder.decode(value, { stream: true });
3966
4010
  const lines = buffer.split("\n");
3967
- buffer = (_c = lines.pop()) != null ? _c : "";
4011
+ buffer = (_g2 = lines.pop()) != null ? _g2 : "";
3968
4012
  for (const line of lines) {
3969
4013
  const trimmed = line.trim();
3970
4014
  if (!trimmed || trimmed === "data: [DONE]") continue;
@@ -3992,7 +4036,7 @@ ${context != null ? context : "None"}` },
3992
4036
  });
3993
4037
  }
3994
4038
  async embed(text) {
3995
- var _a2, _b;
4039
+ var _a2, _b, _c, _d;
3996
4040
  const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
3997
4041
  let payload;
3998
4042
  if (this.opts.embedPayloadTemplate) {
@@ -4006,8 +4050,24 @@ ${context != null ? context : "None"}` },
4006
4050
  input: text
4007
4051
  };
4008
4052
  }
4053
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4054
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4055
+ try {
4056
+ const _g2 = globalThis;
4057
+ if (typeof _g2.__retrivoraDispatchEmbedding === "function") {
4058
+ const res = await _g2.__retrivoraDispatchEmbedding({
4059
+ model: this.model,
4060
+ input: text
4061
+ }, this.apiKey);
4062
+ if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
4063
+ return res.data[0].embedding;
4064
+ }
4065
+ }
4066
+ } catch (e) {
4067
+ }
4068
+ }
4009
4069
  const { data } = await this.http.post(path2, payload);
4010
- const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
4070
+ const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
4011
4071
  const vector = resolvePath(data, extractPath);
4012
4072
  if (!Array.isArray(vector)) {
4013
4073
  throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
@@ -8343,8 +8403,11 @@ ${m.content}`).join("\n\n---\n\n");
8343
8403
  fullSources = fullSources.slice(0, topK);
8344
8404
  }
8345
8405
  const rerankMs = performance.now() - rerankStart;
8346
- let context = fullSources.length ? fullSources.map((m, i) => `[Source ${i + 1}]
8347
- ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
8406
+ let context = fullSources.length ? fullSources.map((m, i) => {
8407
+ var _a3;
8408
+ return `[Source ${i + 1}]
8409
+ ${(_a3 = m == null ? void 0 : m.content) != null ? _a3 : ""}`;
8410
+ }).join("\n\n---\n\n") : "No relevant context found.";
8348
8411
  let displayCount = 15;
8349
8412
  if (hasMetadataFilter) {
8350
8413
  displayCount = fullSources.length;
@@ -8428,7 +8491,10 @@ ${context}`;
8428
8491
  if (isSimulatedThinking) {
8429
8492
  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.`)";
8430
8493
  }
8431
- const hardenedHistory = [...history];
8494
+ const hardenedHistory = (Array.isArray(history) ? history : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
8495
+ role: m.role || "user",
8496
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
8497
+ }));
8432
8498
  const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
8433
8499
  const messages = [...hardenedHistory, userQuestion];
8434
8500
  const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
@@ -8575,13 +8641,13 @@ ${context}`;
8575
8641
  rewrittenQuery,
8576
8642
  systemPrompt,
8577
8643
  userPrompt: question + finalRestrictionSuffix,
8578
- chunks: sources.map((s) => {
8579
- var _a3;
8644
+ chunks: (sources || []).filter(Boolean).map((s) => {
8645
+ var _a3, _b2;
8580
8646
  return {
8581
8647
  id: s.id,
8582
8648
  score: s.score,
8583
- content: s.content,
8584
- metadata: (_a3 = s.metadata) != null ? _a3 : {},
8649
+ content: (_a3 = s == null ? void 0 : s.content) != null ? _a3 : "",
8650
+ metadata: (_b2 = s == null ? void 0 : s.metadata) != null ? _b2 : {},
8585
8651
  namespace: ns
8586
8652
  };
8587
8653
  }),
@@ -9183,6 +9249,7 @@ ${csv.trim()}`);
9183
9249
  // src/core/DatabaseStorage.ts
9184
9250
  import * as fs from "fs";
9185
9251
  import * as path from "path";
9252
+ import * as os from "os";
9186
9253
  var DatabaseStorage = class {
9187
9254
  constructor(config) {
9188
9255
  // Dynamic PG Pool
@@ -9191,7 +9258,8 @@ var DatabaseStorage = class {
9191
9258
  this.mongoClient = null;
9192
9259
  this.mongoDbName = "";
9193
9260
  // Filesystem fallback configuration
9194
- this.fallbackDir = path.join(process.cwd(), ".retrivora");
9261
+ this.isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
9262
+ this.fallbackDir = this.isServerless ? path.join(os.tmpdir(), ".retrivora") : path.join(process.cwd(), ".retrivora");
9195
9263
  this.historyFile = path.join(this.fallbackDir, "history.json");
9196
9264
  this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
9197
9265
  var _a2, _b, _c, _d, _e;
@@ -9321,10 +9389,27 @@ var DatabaseStorage = class {
9321
9389
  }
9322
9390
  // Helper for FS fallback writes
9323
9391
  writeLocalFile(filePath, data) {
9324
- if (!fs.existsSync(this.fallbackDir)) {
9325
- fs.mkdirSync(this.fallbackDir, { recursive: true });
9392
+ try {
9393
+ const dir = path.dirname(filePath);
9394
+ if (!fs.existsSync(dir)) {
9395
+ fs.mkdirSync(dir, { recursive: true });
9396
+ }
9397
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9398
+ } catch (err) {
9399
+ if (err.code === "EROFS") {
9400
+ try {
9401
+ const tmpDir = path.join(os.tmpdir(), ".retrivora");
9402
+ if (!fs.existsSync(tmpDir)) {
9403
+ fs.mkdirSync(tmpDir, { recursive: true });
9404
+ }
9405
+ const tmpFilePath = path.join(tmpDir, path.basename(filePath));
9406
+ fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), "utf-8");
9407
+ } catch (e) {
9408
+ }
9409
+ } else {
9410
+ console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
9411
+ }
9326
9412
  }
9327
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9328
9413
  }
9329
9414
  // ─── Message History Operations ──────────────────────────────────────────
9330
9415
  async saveMessage(sessionId, message) {
package/dist/server.js CHANGED
@@ -4007,7 +4007,7 @@ var UniversalLLMAdapter = class {
4007
4007
  });
4008
4008
  }
4009
4009
  async chat(messages, context) {
4010
- var _a2, _b;
4010
+ var _a2, _b, _c, _d, _e, _f;
4011
4011
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4012
4012
  const formattedMessages = [
4013
4013
  { role: "system", content: `${this.systemPrompt}
@@ -4032,8 +4032,26 @@ ${context != null ? context : "None"}` },
4032
4032
  temperature: this.temperature
4033
4033
  };
4034
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
+ }
4035
4053
  const { data } = await this.http.post(path2, payload);
4036
- 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";
4037
4055
  const result = resolvePath(data, extractPath);
4038
4056
  if (result === void 0) {
4039
4057
  throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
@@ -4047,7 +4065,7 @@ ${context != null ? context : "None"}` },
4047
4065
  */
4048
4066
  chatStream(messages, context) {
4049
4067
  return __asyncGenerator(this, null, function* () {
4050
- var _a2, _b, _c;
4068
+ var _a2, _b, _c, _d, _e, _f, _g2;
4051
4069
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
4052
4070
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
4053
4071
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
@@ -4078,19 +4096,45 @@ ${context != null ? context : "None"}` },
4078
4096
  stream: true
4079
4097
  };
4080
4098
  }
4081
- const response = yield new __await(fetch(url, {
4082
- method: "POST",
4083
- headers: this.resolvedHeaders,
4084
- body: JSON.stringify(payload)
4085
- }));
4086
- if (!response.ok) {
4087
- const errorText = yield new __await(response.text().catch(() => response.statusText));
4088
- 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
+ }
4089
4121
  }
4090
- if (!response.body) {
4091
- 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;
4092
4136
  }
4093
- const reader = response.body.getReader();
4137
+ const reader = streamBody.getReader();
4094
4138
  const decoder = new TextDecoder("utf-8");
4095
4139
  let buffer = "";
4096
4140
  try {
@@ -4099,7 +4143,7 @@ ${context != null ? context : "None"}` },
4099
4143
  if (done) break;
4100
4144
  buffer += decoder.decode(value, { stream: true });
4101
4145
  const lines = buffer.split("\n");
4102
- buffer = (_c = lines.pop()) != null ? _c : "";
4146
+ buffer = (_g2 = lines.pop()) != null ? _g2 : "";
4103
4147
  for (const line of lines) {
4104
4148
  const trimmed = line.trim();
4105
4149
  if (!trimmed || trimmed === "data: [DONE]") continue;
@@ -4127,7 +4171,7 @@ ${context != null ? context : "None"}` },
4127
4171
  });
4128
4172
  }
4129
4173
  async embed(text) {
4130
- var _a2, _b;
4174
+ var _a2, _b, _c, _d;
4131
4175
  const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
4132
4176
  let payload;
4133
4177
  if (this.opts.embedPayloadTemplate) {
@@ -4141,8 +4185,24 @@ ${context != null ? context : "None"}` },
4141
4185
  input: text
4142
4186
  };
4143
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
+ }
4144
4204
  const { data } = await this.http.post(path2, payload);
4145
- const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
4205
+ const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
4146
4206
  const vector = resolvePath(data, extractPath);
4147
4207
  if (!Array.isArray(vector)) {
4148
4208
  throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
@@ -8492,8 +8552,11 @@ ${m.content}`).join("\n\n---\n\n");
8492
8552
  fullSources = fullSources.slice(0, topK);
8493
8553
  }
8494
8554
  const rerankMs = performance.now() - rerankStart;
8495
- let context = fullSources.length ? fullSources.map((m, i) => `[Source ${i + 1}]
8496
- ${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.";
8497
8560
  let displayCount = 15;
8498
8561
  if (hasMetadataFilter) {
8499
8562
  displayCount = fullSources.length;
@@ -8577,7 +8640,10 @@ ${context}`;
8577
8640
  if (isSimulatedThinking) {
8578
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.`)";
8579
8642
  }
8580
- 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
+ }));
8581
8647
  const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
8582
8648
  const messages = [...hardenedHistory, userQuestion];
8583
8649
  const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
@@ -8724,13 +8790,13 @@ ${context}`;
8724
8790
  rewrittenQuery,
8725
8791
  systemPrompt,
8726
8792
  userPrompt: question + finalRestrictionSuffix,
8727
- chunks: sources.map((s) => {
8728
- var _a3;
8793
+ chunks: (sources || []).filter(Boolean).map((s) => {
8794
+ var _a3, _b2;
8729
8795
  return {
8730
8796
  id: s.id,
8731
8797
  score: s.score,
8732
- content: s.content,
8733
- 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 : {},
8734
8800
  namespace: ns
8735
8801
  };
8736
8802
  }),
@@ -9631,6 +9697,7 @@ var import_server = require("next/server");
9631
9697
  // src/core/DatabaseStorage.ts
9632
9698
  var fs = __toESM(require("fs"));
9633
9699
  var path = __toESM(require("path"));
9700
+ var os = __toESM(require("os"));
9634
9701
  var DatabaseStorage = class {
9635
9702
  constructor(config) {
9636
9703
  // Dynamic PG Pool
@@ -9639,7 +9706,8 @@ var DatabaseStorage = class {
9639
9706
  this.mongoClient = null;
9640
9707
  this.mongoDbName = "";
9641
9708
  // Filesystem fallback configuration
9642
- 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");
9643
9711
  this.historyFile = path.join(this.fallbackDir, "history.json");
9644
9712
  this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
9645
9713
  var _a2, _b, _c, _d, _e;
@@ -9769,10 +9837,27 @@ var DatabaseStorage = class {
9769
9837
  }
9770
9838
  // Helper for FS fallback writes
9771
9839
  writeLocalFile(filePath, data) {
9772
- if (!fs.existsSync(this.fallbackDir)) {
9773
- 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
+ }
9774
9860
  }
9775
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9776
9861
  }
9777
9862
  // ─── Message History Operations ──────────────────────────────────────────
9778
9863
  async saveMessage(sessionId, message) {
package/dist/server.mjs CHANGED
@@ -3911,7 +3911,7 @@ var UniversalLLMAdapter = class {
3911
3911
  });
3912
3912
  }
3913
3913
  async chat(messages, context) {
3914
- var _a2, _b;
3914
+ var _a2, _b, _c, _d, _e, _f;
3915
3915
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3916
3916
  const formattedMessages = [
3917
3917
  { role: "system", content: `${this.systemPrompt}
@@ -3936,8 +3936,26 @@ ${context != null ? context : "None"}` },
3936
3936
  temperature: this.temperature
3937
3937
  };
3938
3938
  }
3939
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
3940
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
3941
+ try {
3942
+ const _g2 = globalThis;
3943
+ if (typeof _g2.__retrivoraDispatchChat === "function") {
3944
+ const res = await _g2.__retrivoraDispatchChat({
3945
+ model: this.model,
3946
+ messages: formattedMessages,
3947
+ max_tokens: this.maxTokens,
3948
+ temperature: this.temperature
3949
+ }, this.apiKey);
3950
+ 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) {
3951
+ return res.response.choices[0].message.content;
3952
+ }
3953
+ }
3954
+ } catch (e) {
3955
+ }
3956
+ }
3939
3957
  const { data } = await this.http.post(path2, payload);
3940
- const extractPath = (_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content";
3958
+ const extractPath = (_f = this.opts.responseExtractPath) != null ? _f : "choices[0].message.content";
3941
3959
  const result = resolvePath(data, extractPath);
3942
3960
  if (result === void 0) {
3943
3961
  throw new Error(`[UniversalLLMAdapter] Could not extract text from path '${extractPath}' in response.`);
@@ -3951,7 +3969,7 @@ ${context != null ? context : "None"}` },
3951
3969
  */
3952
3970
  chatStream(messages, context) {
3953
3971
  return __asyncGenerator(this, null, function* () {
3954
- var _a2, _b, _c;
3972
+ var _a2, _b, _c, _d, _e, _f, _g2;
3955
3973
  const path2 = (_a2 = this.opts.chatPath) != null ? _a2 : "/chat/completions";
3956
3974
  const url = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
3957
3975
  const extractPath = ((_b = this.opts.responseExtractPath) != null ? _b : "choices[0].message.content").replace("message.content", "delta.content");
@@ -3982,19 +4000,45 @@ ${context != null ? context : "None"}` },
3982
4000
  stream: true
3983
4001
  };
3984
4002
  }
3985
- const response = yield new __await(fetch(url, {
3986
- method: "POST",
3987
- headers: this.resolvedHeaders,
3988
- body: JSON.stringify(payload)
3989
- }));
3990
- if (!response.ok) {
3991
- const errorText = yield new __await(response.text().catch(() => response.statusText));
3992
- throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
4003
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4004
+ let streamBody = null;
4005
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4006
+ try {
4007
+ const _g3 = globalThis;
4008
+ if (typeof _g3.__retrivoraDispatchChat === "function") {
4009
+ const res = yield new __await(_g3.__retrivoraDispatchChat({
4010
+ model: this.model,
4011
+ messages: formattedMessages,
4012
+ max_tokens: this.maxTokens,
4013
+ temperature: this.temperature,
4014
+ stream: true
4015
+ }, this.apiKey));
4016
+ if (res == null ? void 0 : res.stream) {
4017
+ streamBody = res.stream;
4018
+ } 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) {
4019
+ yield res.response.choices[0].message.content;
4020
+ return;
4021
+ }
4022
+ }
4023
+ } catch (e) {
4024
+ }
3993
4025
  }
3994
- if (!response.body) {
3995
- throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
4026
+ if (!streamBody) {
4027
+ const response = yield new __await(fetch(url, {
4028
+ method: "POST",
4029
+ headers: this.resolvedHeaders,
4030
+ body: JSON.stringify(payload)
4031
+ }));
4032
+ if (!response.ok) {
4033
+ const errorText = yield new __await(response.text().catch(() => response.statusText));
4034
+ throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
4035
+ }
4036
+ if (!response.body) {
4037
+ throw new Error("[UniversalLLMAdapter] Response body is null \u2014 server did not send a streaming response.");
4038
+ }
4039
+ streamBody = response.body;
3996
4040
  }
3997
- const reader = response.body.getReader();
4041
+ const reader = streamBody.getReader();
3998
4042
  const decoder = new TextDecoder("utf-8");
3999
4043
  let buffer = "";
4000
4044
  try {
@@ -4003,7 +4047,7 @@ ${context != null ? context : "None"}` },
4003
4047
  if (done) break;
4004
4048
  buffer += decoder.decode(value, { stream: true });
4005
4049
  const lines = buffer.split("\n");
4006
- buffer = (_c = lines.pop()) != null ? _c : "";
4050
+ buffer = (_g2 = lines.pop()) != null ? _g2 : "";
4007
4051
  for (const line of lines) {
4008
4052
  const trimmed = line.trim();
4009
4053
  if (!trimmed || trimmed === "data: [DONE]") continue;
@@ -4031,7 +4075,7 @@ ${context != null ? context : "None"}` },
4031
4075
  });
4032
4076
  }
4033
4077
  async embed(text) {
4034
- var _a2, _b;
4078
+ var _a2, _b, _c, _d;
4035
4079
  const path2 = (_a2 = this.opts.embedPath) != null ? _a2 : "/embeddings";
4036
4080
  let payload;
4037
4081
  if (this.opts.embedPayloadTemplate) {
@@ -4045,8 +4089,24 @@ ${context != null ? context : "None"}` },
4045
4089
  input: text
4046
4090
  };
4047
4091
  }
4092
+ const isSelfHost = this.baseUrl.includes("retrivora.com") || this.baseUrl.includes("localhost");
4093
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
4094
+ try {
4095
+ const _g2 = globalThis;
4096
+ if (typeof _g2.__retrivoraDispatchEmbedding === "function") {
4097
+ const res = await _g2.__retrivoraDispatchEmbedding({
4098
+ model: this.model,
4099
+ input: text
4100
+ }, this.apiKey);
4101
+ if ((_c = (_b = res == null ? void 0 : res.data) == null ? void 0 : _b[0]) == null ? void 0 : _c.embedding) {
4102
+ return res.data[0].embedding;
4103
+ }
4104
+ }
4105
+ } catch (e) {
4106
+ }
4107
+ }
4048
4108
  const { data } = await this.http.post(path2, payload);
4049
- const extractPath = (_b = this.opts.embedExtractPath) != null ? _b : "data[0].embedding";
4109
+ const extractPath = (_d = this.opts.embedExtractPath) != null ? _d : "data[0].embedding";
4050
4110
  const vector = resolvePath(data, extractPath);
4051
4111
  if (!Array.isArray(vector)) {
4052
4112
  throw new Error(`[UniversalLLMAdapter] Expected a number array at '${extractPath}' for embeddings.`);
@@ -8396,8 +8456,11 @@ ${m.content}`).join("\n\n---\n\n");
8396
8456
  fullSources = fullSources.slice(0, topK);
8397
8457
  }
8398
8458
  const rerankMs = performance.now() - rerankStart;
8399
- let context = fullSources.length ? fullSources.map((m, i) => `[Source ${i + 1}]
8400
- ${m.content}`).join("\n\n---\n\n") : "No relevant context found.";
8459
+ let context = fullSources.length ? fullSources.map((m, i) => {
8460
+ var _a3;
8461
+ return `[Source ${i + 1}]
8462
+ ${(_a3 = m == null ? void 0 : m.content) != null ? _a3 : ""}`;
8463
+ }).join("\n\n---\n\n") : "No relevant context found.";
8401
8464
  let displayCount = 15;
8402
8465
  if (hasMetadataFilter) {
8403
8466
  displayCount = fullSources.length;
@@ -8481,7 +8544,10 @@ ${context}`;
8481
8544
  if (isSimulatedThinking) {
8482
8545
  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.`)";
8483
8546
  }
8484
- const hardenedHistory = [...history];
8547
+ const hardenedHistory = (Array.isArray(history) ? history : []).filter((m) => Boolean(m && typeof m === "object")).map((m) => ({
8548
+ role: m.role || "user",
8549
+ content: typeof m.content === "string" ? m.content : m.content != null ? String(m.content) : ""
8550
+ }));
8485
8551
  const userQuestion = { role: "user", content: question + finalRestrictionSuffix };
8486
8552
  const messages = [...hardenedHistory, userQuestion];
8487
8553
  const systemPrompt = (_v = this.config.llm.systemPrompt) != null ? _v : "";
@@ -8628,13 +8694,13 @@ ${context}`;
8628
8694
  rewrittenQuery,
8629
8695
  systemPrompt,
8630
8696
  userPrompt: question + finalRestrictionSuffix,
8631
- chunks: sources.map((s) => {
8632
- var _a3;
8697
+ chunks: (sources || []).filter(Boolean).map((s) => {
8698
+ var _a3, _b2;
8633
8699
  return {
8634
8700
  id: s.id,
8635
8701
  score: s.score,
8636
- content: s.content,
8637
- metadata: (_a3 = s.metadata) != null ? _a3 : {},
8702
+ content: (_a3 = s == null ? void 0 : s.content) != null ? _a3 : "",
8703
+ metadata: (_b2 = s == null ? void 0 : s.metadata) != null ? _b2 : {},
8638
8704
  namespace: ns
8639
8705
  };
8640
8706
  }),
@@ -9535,6 +9601,7 @@ import { NextResponse } from "next/server";
9535
9601
  // src/core/DatabaseStorage.ts
9536
9602
  import * as fs from "fs";
9537
9603
  import * as path from "path";
9604
+ import * as os from "os";
9538
9605
  var DatabaseStorage = class {
9539
9606
  constructor(config) {
9540
9607
  // Dynamic PG Pool
@@ -9543,7 +9610,8 @@ var DatabaseStorage = class {
9543
9610
  this.mongoClient = null;
9544
9611
  this.mongoDbName = "";
9545
9612
  // Filesystem fallback configuration
9546
- this.fallbackDir = path.join(process.cwd(), ".retrivora");
9613
+ this.isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
9614
+ this.fallbackDir = this.isServerless ? path.join(os.tmpdir(), ".retrivora") : path.join(process.cwd(), ".retrivora");
9547
9615
  this.historyFile = path.join(this.fallbackDir, "history.json");
9548
9616
  this.feedbackFile = path.join(this.fallbackDir, "feedback.json");
9549
9617
  var _a2, _b, _c, _d, _e;
@@ -9673,10 +9741,27 @@ var DatabaseStorage = class {
9673
9741
  }
9674
9742
  // Helper for FS fallback writes
9675
9743
  writeLocalFile(filePath, data) {
9676
- if (!fs.existsSync(this.fallbackDir)) {
9677
- fs.mkdirSync(this.fallbackDir, { recursive: true });
9744
+ try {
9745
+ const dir = path.dirname(filePath);
9746
+ if (!fs.existsSync(dir)) {
9747
+ fs.mkdirSync(dir, { recursive: true });
9748
+ }
9749
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9750
+ } catch (err) {
9751
+ if (err.code === "EROFS") {
9752
+ try {
9753
+ const tmpDir = path.join(os.tmpdir(), ".retrivora");
9754
+ if (!fs.existsSync(tmpDir)) {
9755
+ fs.mkdirSync(tmpDir, { recursive: true });
9756
+ }
9757
+ const tmpFilePath = path.join(tmpDir, path.basename(filePath));
9758
+ fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), "utf-8");
9759
+ } catch (e) {
9760
+ }
9761
+ } else {
9762
+ console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
9763
+ }
9678
9764
  }
9679
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
9680
9765
  }
9681
9766
  // ─── Message History Operations ──────────────────────────────────────────
9682
9767
  async saveMessage(sessionId, message) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
4
4
  "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
5
  "author": "Abhinav Alkuchi",
6
6
  "license": "UNLICENSED",
@@ -1,5 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
+ import * as os from 'os';
3
4
  import { RagConfig } from '../config/RagConfig';
4
5
  import { LicenseVerifier } from './LicenseVerifier';
5
6
 
@@ -35,7 +36,10 @@ export class DatabaseStorage {
35
36
  private feedbackTableName: string;
36
37
 
37
38
  // Filesystem fallback configuration
38
- private fallbackDir = path.join(process.cwd(), '.retrivora');
39
+ private isServerless = Boolean(process.env.VERCEL || process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.LAMBDA_TASK_ROOT);
40
+ private fallbackDir = this.isServerless
41
+ ? path.join(os.tmpdir(), '.retrivora')
42
+ : path.join(process.cwd(), '.retrivora');
39
43
  private historyFile = path.join(this.fallbackDir, 'history.json');
40
44
  private feedbackFile = path.join(this.fallbackDir, 'feedback.json');
41
45
 
@@ -191,10 +195,28 @@ export class DatabaseStorage {
191
195
 
192
196
  // Helper for FS fallback writes
193
197
  private writeLocalFile(filePath: string, data: any[]): void {
194
- if (!fs.existsSync(this.fallbackDir)) {
195
- fs.mkdirSync(this.fallbackDir, { recursive: true });
198
+ try {
199
+ const dir = path.dirname(filePath);
200
+ if (!fs.existsSync(dir)) {
201
+ fs.mkdirSync(dir, { recursive: true });
202
+ }
203
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
204
+ } catch (err: any) {
205
+ if (err.code === 'EROFS') {
206
+ try {
207
+ const tmpDir = path.join(os.tmpdir(), '.retrivora');
208
+ if (!fs.existsSync(tmpDir)) {
209
+ fs.mkdirSync(tmpDir, { recursive: true });
210
+ }
211
+ const tmpFilePath = path.join(tmpDir, path.basename(filePath));
212
+ fs.writeFileSync(tmpFilePath, JSON.stringify(data, null, 2), 'utf-8');
213
+ } catch {
214
+ /* ignore write failure in read-only environment */
215
+ }
216
+ } else {
217
+ console.warn(`[DatabaseStorage] Failed to write local fallback file: ${err.message}`);
218
+ }
196
219
  }
197
- fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
198
220
  }
199
221
 
200
222
  // ─── Message History Operations ──────────────────────────────────────────
@@ -608,7 +608,7 @@ You are a helpful assistant. Use the provided context to answer questions accura
608
608
 
609
609
  // 4. Context Augmentation - Review all retrieved/reranked sources for full comprehension
610
610
  let context = fullSources.length
611
- ? fullSources.map((m, i) => `[Source ${i + 1}]\n${m.content}`).join('\n\n---\n\n')
611
+ ? fullSources.map((m, i) => `[Source ${i + 1}]\n${m?.content ?? ''}`).join('\n\n---\n\n')
612
612
  : 'No relevant context found.';
613
613
 
614
614
  // Count the records of relevant sources to pass into the slice dynamically
@@ -722,7 +722,12 @@ You are a helpful assistant. Use the provided context to answer questions accura
722
722
  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.`)';
723
723
  }
724
724
 
725
- const hardenedHistory = [...history];
725
+ const hardenedHistory = (Array.isArray(history) ? history : [])
726
+ .filter((m): m is ChatMessage => Boolean(m && typeof m === 'object'))
727
+ .map((m) => ({
728
+ role: m.role || 'user',
729
+ content: typeof m.content === 'string' ? m.content : (m.content != null ? String(m.content) : ''),
730
+ }));
726
731
  const userQuestion = { role: 'user', content: question + finalRestrictionSuffix } as ChatMessage;
727
732
  const messages: ChatMessage[] = [...hardenedHistory, userQuestion];
728
733
 
@@ -880,11 +885,11 @@ You are a helpful assistant. Use the provided context to answer questions accura
880
885
  rewrittenQuery,
881
886
  systemPrompt,
882
887
  userPrompt: question + finalRestrictionSuffix,
883
- chunks: sources.map((s) => ({
888
+ chunks: (sources || []).filter(Boolean).map((s) => ({
884
889
  id: s.id,
885
890
  score: s.score,
886
- content: s.content,
887
- metadata: s.metadata ?? {},
891
+ content: s?.content ?? '',
892
+ metadata: s?.metadata ?? {},
888
893
  namespace: ns,
889
894
  } satisfies RetrievedChunk)),
890
895
  latency,
@@ -94,6 +94,27 @@ export class UniversalLLMAdapter implements ILLMProvider {
94
94
  };
95
95
  }
96
96
 
97
+ const isSelfHost = this.baseUrl.includes('retrivora.com') || this.baseUrl.includes('localhost');
98
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
99
+ try {
100
+ const _g = globalThis as any;
101
+ if (typeof _g.__retrivoraDispatchChat === 'function') {
102
+ const res = await _g.__retrivoraDispatchChat({
103
+ model: this.model,
104
+ messages: formattedMessages as any,
105
+ max_tokens: this.maxTokens,
106
+ temperature: this.temperature,
107
+ }, this.apiKey);
108
+
109
+ if (res?.response?.choices?.[0]?.message?.content) {
110
+ return res.response.choices[0].message.content;
111
+ }
112
+ }
113
+ } catch {
114
+ /* proceed to HTTP fetch */
115
+ }
116
+ }
117
+
97
118
  const { data } = await this.http.post(path, payload);
98
119
 
99
120
  const extractPath = this.opts.responseExtractPath ?? 'choices[0].message.content';
@@ -145,22 +166,52 @@ export class UniversalLLMAdapter implements ILLMProvider {
145
166
  };
146
167
  }
147
168
 
148
- const response = await fetch(url, {
149
- method: 'POST',
150
- headers: this.resolvedHeaders,
151
- body: JSON.stringify(payload),
152
- });
153
-
154
- if (!response.ok) {
155
- const errorText = await response.text().catch(() => response.statusText);
156
- throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
169
+ const isSelfHost = this.baseUrl.includes('retrivora.com') || this.baseUrl.includes('localhost');
170
+ let streamBody: ReadableStream<Uint8Array> | null = null;
171
+
172
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
173
+ try {
174
+ const _g = globalThis as any;
175
+ if (typeof _g.__retrivoraDispatchChat === 'function') {
176
+ const res = await _g.__retrivoraDispatchChat({
177
+ model: this.model,
178
+ messages: formattedMessages as any,
179
+ max_tokens: this.maxTokens,
180
+ temperature: this.temperature,
181
+ stream: true,
182
+ }, this.apiKey);
183
+
184
+ if (res?.stream) {
185
+ streamBody = res.stream as ReadableStream<Uint8Array>;
186
+ } else if (res?.response?.choices?.[0]?.message?.content) {
187
+ yield res.response.choices[0].message.content;
188
+ return;
189
+ }
190
+ }
191
+ } catch {
192
+ /* proceed to HTTP fetch */
193
+ }
157
194
  }
158
195
 
159
- if (!response.body) {
160
- throw new Error('[UniversalLLMAdapter] Response body is null — server did not send a streaming response.');
196
+ if (!streamBody) {
197
+ const response = await fetch(url, {
198
+ method: 'POST',
199
+ headers: this.resolvedHeaders,
200
+ body: JSON.stringify(payload),
201
+ });
202
+
203
+ if (!response.ok) {
204
+ const errorText = await response.text().catch(() => response.statusText);
205
+ throw new Error(`[UniversalLLMAdapter] Streaming request failed (${response.status}): ${errorText}`);
206
+ }
207
+
208
+ if (!response.body) {
209
+ throw new Error('[UniversalLLMAdapter] Response body is null — server did not send a streaming response.');
210
+ }
211
+ streamBody = response.body;
161
212
  }
162
213
 
163
- const reader = response.body.getReader();
214
+ const reader = streamBody.getReader();
164
215
  const decoder = new TextDecoder('utf-8');
165
216
  let buffer = '';
166
217
 
@@ -218,6 +269,25 @@ export class UniversalLLMAdapter implements ILLMProvider {
218
269
  };
219
270
  }
220
271
 
272
+ const isSelfHost = this.baseUrl.includes('retrivora.com') || this.baseUrl.includes('localhost');
273
+ if (isSelfHost || Boolean(process.env.VERCEL)) {
274
+ try {
275
+ const _g = globalThis as any;
276
+ if (typeof _g.__retrivoraDispatchEmbedding === 'function') {
277
+ const res = await _g.__retrivoraDispatchEmbedding({
278
+ model: this.model,
279
+ input: text,
280
+ }, this.apiKey);
281
+
282
+ if (res?.data?.[0]?.embedding) {
283
+ return res.data[0].embedding;
284
+ }
285
+ }
286
+ } catch {
287
+ /* proceed to HTTP fetch */
288
+ }
289
+ }
290
+
221
291
  const { data } = await this.http.post(path, payload);
222
292
 
223
293
  const extractPath = this.opts.embedExtractPath ?? 'data[0].embedding';