@retrivora-ai/rag-engine 2.2.2 → 2.2.3
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/handlers/index.js +2 -2
- package/dist/handlers/index.mjs +2 -2
- package/dist/index.css +44 -0
- package/dist/index.js +199 -128
- package/dist/index.mjs +224 -153
- package/dist/server.js +2 -2
- package/dist/server.mjs +2 -2
- package/package.json +1 -1
- package/src/components/DocumentUpload.tsx +192 -116
- package/src/handlers/index.ts +1 -1
- package/src/hooks/useRagChat.ts +30 -14
- package/src/index.css +44 -0
package/dist/server.js
CHANGED
|
@@ -5496,7 +5496,7 @@ var ConfigValidator = class {
|
|
|
5496
5496
|
// package.json
|
|
5497
5497
|
var package_default = {
|
|
5498
5498
|
name: "@retrivora-ai/rag-engine",
|
|
5499
|
-
version: "2.2.
|
|
5499
|
+
version: "2.2.3",
|
|
5500
5500
|
description: "Retrivora AI is a plug-and-play AI engine for RAG chat experiences \u2014 generic vector DB + LLM provider, embeddable or standalone.",
|
|
5501
5501
|
author: "Abhinav Alkuchi",
|
|
5502
5502
|
license: "UNLICENSED",
|
|
@@ -11354,7 +11354,7 @@ function reportTelemetry(req, plugin, action, status, details, trace) {
|
|
|
11354
11354
|
const licenseKey = config.licenseKey || process.env.RAG_LICENSE_KEY || process.env.RETRIVORA_LICENSE_KEY || process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY;
|
|
11355
11355
|
const telemetryConfig = config.telemetry;
|
|
11356
11356
|
const enabled = (_a2 = telemetryConfig == null ? void 0 : telemetryConfig.enabled) != null ? _a2 : Boolean(licenseKey);
|
|
11357
|
-
const defaultUrl = process.env.NODE_ENV === "development" && !process.env.TELEMETRY_URL && !process.env.NEXT_PUBLIC_TELEMETRY_URL ? "http://localhost:3001/api/telemetry" : "https://retrivora.com/api/telemetry";
|
|
11357
|
+
const defaultUrl = process.env.NODE_ENV === "development" && !process.env.TELEMETRY_URL && !process.env.NEXT_PUBLIC_TELEMETRY_URL ? "http://localhost:3001/api/telemetry" : "https://www.retrivora.com/api/telemetry";
|
|
11358
11358
|
const telemetryUrl = (telemetryConfig == null ? void 0 : telemetryConfig.url) || process.env.TELEMETRY_URL || process.env.NEXT_PUBLIC_TELEMETRY_URL || defaultUrl;
|
|
11359
11359
|
const host = req.headers.get("host") || "localhost";
|
|
11360
11360
|
const userAgent = req.headers.get("user-agent") || `Retrivora-SDK/${SDK_VERSION}`;
|
package/dist/server.mjs
CHANGED
|
@@ -5399,7 +5399,7 @@ var ConfigValidator = class {
|
|
|
5399
5399
|
// package.json
|
|
5400
5400
|
var package_default = {
|
|
5401
5401
|
name: "@retrivora-ai/rag-engine",
|
|
5402
|
-
version: "2.2.
|
|
5402
|
+
version: "2.2.3",
|
|
5403
5403
|
description: "Retrivora AI is a plug-and-play AI engine for RAG chat experiences \u2014 generic vector DB + LLM provider, embeddable or standalone.",
|
|
5404
5404
|
author: "Abhinav Alkuchi",
|
|
5405
5405
|
license: "UNLICENSED",
|
|
@@ -11257,7 +11257,7 @@ function reportTelemetry(req, plugin, action, status, details, trace) {
|
|
|
11257
11257
|
const licenseKey = config.licenseKey || process.env.RAG_LICENSE_KEY || process.env.RETRIVORA_LICENSE_KEY || process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY;
|
|
11258
11258
|
const telemetryConfig = config.telemetry;
|
|
11259
11259
|
const enabled = (_a2 = telemetryConfig == null ? void 0 : telemetryConfig.enabled) != null ? _a2 : Boolean(licenseKey);
|
|
11260
|
-
const defaultUrl = process.env.NODE_ENV === "development" && !process.env.TELEMETRY_URL && !process.env.NEXT_PUBLIC_TELEMETRY_URL ? "http://localhost:3001/api/telemetry" : "https://retrivora.com/api/telemetry";
|
|
11260
|
+
const defaultUrl = process.env.NODE_ENV === "development" && !process.env.TELEMETRY_URL && !process.env.NEXT_PUBLIC_TELEMETRY_URL ? "http://localhost:3001/api/telemetry" : "https://www.retrivora.com/api/telemetry";
|
|
11261
11261
|
const telemetryUrl = (telemetryConfig == null ? void 0 : telemetryConfig.url) || process.env.TELEMETRY_URL || process.env.NEXT_PUBLIC_TELEMETRY_URL || defaultUrl;
|
|
11262
11262
|
const host = req.headers.get("host") || "localhost";
|
|
11263
11263
|
const userAgent = req.headers.get("user-agent") || `Retrivora-SDK/${SDK_VERSION}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
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,9 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState, useRef } from 'react';
|
|
4
|
-
import { Upload, File, X, CheckCircle, AlertCircle, Loader2 } from 'lucide-react';
|
|
4
|
+
import { Upload, File, X, CheckCircle, AlertCircle, Loader2, CheckCircle2, Sparkles, Files } from 'lucide-react';
|
|
5
5
|
import { useConfig } from './ConfigProvider';
|
|
6
|
-
|
|
7
6
|
import { DocumentUploadProps } from '../types';
|
|
8
7
|
|
|
9
8
|
interface FileState {
|
|
@@ -18,6 +17,14 @@ export function DocumentUpload({ namespace, uploadUrl, retrivoraApiBase, onUploa
|
|
|
18
17
|
const [isDragging, setIsDragging] = useState(false);
|
|
19
18
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
20
19
|
|
|
20
|
+
// Success Modal State
|
|
21
|
+
const [showSuccessModal, setShowSuccessModal] = useState(false);
|
|
22
|
+
const [lastUploadSummary, setLastUploadSummary] = useState<{
|
|
23
|
+
totalFilesCount: number;
|
|
24
|
+
filesUploaded: string[];
|
|
25
|
+
namespaceUsed: string;
|
|
26
|
+
} | null>(null);
|
|
27
|
+
|
|
21
28
|
const isFreeTier = !projectId || projectId.includes('free') || projectId.includes('demo');
|
|
22
29
|
|
|
23
30
|
const MULTI_DIMENSION_MODELS: Record<string, number[]> = {
|
|
@@ -30,7 +37,6 @@ export function DocumentUpload({ namespace, uploadUrl, retrivoraApiBase, onUploa
|
|
|
30
37
|
const currentModel = embedding?.model || 'unknown';
|
|
31
38
|
const defaultDimension = isFreeTier ? 768 : (embedding?.dimensions || 1536);
|
|
32
39
|
|
|
33
|
-
// Provide specific dimensions for known models, otherwise provide common ones
|
|
34
40
|
let availableDimensions = isFreeTier
|
|
35
41
|
? [768]
|
|
36
42
|
: Object.entries(MULTI_DIMENSION_MODELS).find(([k]) => currentModel.includes(k))?.[1];
|
|
@@ -41,7 +47,6 @@ export function DocumentUpload({ namespace, uploadUrl, retrivoraApiBase, onUploa
|
|
|
41
47
|
: [...COMMON_DIMENSIONS, defaultDimension].sort((a, b) => a - b);
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
const isChangeable = !isFreeTier;
|
|
45
50
|
const [dimension, setDimension] = useState(defaultDimension);
|
|
46
51
|
|
|
47
52
|
const addFiles = (files: File[]) => {
|
|
@@ -80,12 +85,11 @@ export function DocumentUpload({ namespace, uploadUrl, retrivoraApiBase, onUploa
|
|
|
80
85
|
const idleFiles = fileStates.filter(s => s.status === 'idle');
|
|
81
86
|
if (idleFiles.length === 0) return;
|
|
82
87
|
|
|
83
|
-
// Update status to uploading
|
|
84
88
|
setFileStates(prev => prev.map(s => s.status === 'idle' ? { ...s, status: 'uploading' } : s));
|
|
85
89
|
|
|
86
90
|
const formData = new FormData();
|
|
87
91
|
idleFiles.forEach(s => formData.append('files', s.file));
|
|
88
|
-
const targetNs = namespace || projectId;
|
|
92
|
+
const targetNs = namespace || projectId || 'default';
|
|
89
93
|
if (targetNs) formData.append('namespace', targetNs);
|
|
90
94
|
formData.append('dimension', dimension.toString());
|
|
91
95
|
|
|
@@ -100,7 +104,6 @@ export function DocumentUpload({ namespace, uploadUrl, retrivoraApiBase, onUploa
|
|
|
100
104
|
body: formData,
|
|
101
105
|
});
|
|
102
106
|
|
|
103
|
-
// Fallback resolution: if primaryUrl returns 404 and is not '/api/upload', try '/api/upload'
|
|
104
107
|
if (response.status === 404 && primaryUrl !== '/api/upload') {
|
|
105
108
|
console.warn(`[Retrivora SDK] ⚠️ Upload endpoint "${primaryUrl}" returned 404. Retrying fallback to "/api/upload"...`);
|
|
106
109
|
response = await fetch('/api/upload', {
|
|
@@ -116,6 +119,15 @@ export function DocumentUpload({ namespace, uploadUrl, retrivoraApiBase, onUploa
|
|
|
116
119
|
console.log(`[Retrivora SDK] ✅ Upload response success:`, result);
|
|
117
120
|
|
|
118
121
|
setFileStates(prev => prev.map(s => s.status === 'uploading' ? { ...s, status: 'success' } : s));
|
|
122
|
+
|
|
123
|
+
const updatedSuccessCount = fileStates.length;
|
|
124
|
+
setLastUploadSummary({
|
|
125
|
+
totalFilesCount: updatedSuccessCount,
|
|
126
|
+
filesUploaded: idleFiles.map(s => s.file.name),
|
|
127
|
+
namespaceUsed: targetNs,
|
|
128
|
+
});
|
|
129
|
+
setShowSuccessModal(true);
|
|
130
|
+
|
|
119
131
|
if (onUploadComplete) onUploadComplete(result);
|
|
120
132
|
} catch (err: any) {
|
|
121
133
|
const message = err instanceof Error ? err.message : 'Upload failed';
|
|
@@ -128,127 +140,191 @@ export function DocumentUpload({ namespace, uploadUrl, retrivoraApiBase, onUploa
|
|
|
128
140
|
const hasIdle = fileStates.some(s => s.status === 'idle');
|
|
129
141
|
|
|
130
142
|
return (
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<div
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
className="px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50"
|
|
157
|
-
style={{
|
|
158
|
-
backgroundColor: `${ui.primaryColor}15`,
|
|
159
|
-
color: ui.primaryColor,
|
|
160
|
-
border: `1px solid ${ui.primaryColor}30`
|
|
161
|
-
}}
|
|
162
|
-
>
|
|
163
|
-
Select Files
|
|
164
|
-
</button>
|
|
165
|
-
<input
|
|
166
|
-
ref={fileInputRef}
|
|
167
|
-
type="file"
|
|
168
|
-
multiple
|
|
169
|
-
onChange={onFileSelect}
|
|
170
|
-
className="hidden"
|
|
171
|
-
accept=".pdf,.docx,.doc,.xlsx,.xls,.csv,.txt,.md,.json"
|
|
172
|
-
/>
|
|
173
|
-
|
|
174
|
-
<div className="mt-6 flex flex-col items-center gap-2 text-sm">
|
|
175
|
-
<label className="text-slate-600 dark:text-white/60 font-medium">Embedding Dimension</label>
|
|
176
|
-
<select
|
|
177
|
-
value={dimension}
|
|
178
|
-
onChange={(e) => setDimension(Number(e.target.value))}
|
|
143
|
+
<>
|
|
144
|
+
<div
|
|
145
|
+
className={`p-6 border-2 border-dashed transition-all duration-300 rounded-2xl ${
|
|
146
|
+
isDragging
|
|
147
|
+
? 'border-emerald-500 bg-emerald-50/50 dark:bg-emerald-500/5'
|
|
148
|
+
: 'border-slate-200 dark:border-white/10 bg-white dark:bg-white/5'
|
|
149
|
+
} ${className}`}
|
|
150
|
+
onDragOver={onDragOver}
|
|
151
|
+
onDragLeave={onDragLeave}
|
|
152
|
+
onDrop={onDrop}
|
|
153
|
+
>
|
|
154
|
+
<div className="flex flex-col items-center justify-center text-center">
|
|
155
|
+
<div
|
|
156
|
+
className="w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm"
|
|
157
|
+
style={{ background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }}
|
|
158
|
+
>
|
|
159
|
+
<Upload className="w-6 h-6" style={{ color: ui.primaryColor }} />
|
|
160
|
+
</div>
|
|
161
|
+
<h3 className="text-slate-900 dark:text-white font-semibold mb-1">Upload Documents</h3>
|
|
162
|
+
<p className="text-slate-500 dark:text-white/40 text-sm mb-6 max-w-xs">
|
|
163
|
+
Drag and drop PDF, DOCX, Excel (.xlsx, .xls), CSV, TXT, or JSON files to train your AI on your own data.
|
|
164
|
+
</p>
|
|
165
|
+
|
|
166
|
+
<button
|
|
167
|
+
onClick={() => fileInputRef.current?.click()}
|
|
179
168
|
disabled={isUploading}
|
|
180
|
-
className="px-
|
|
169
|
+
className="px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50"
|
|
170
|
+
style={{
|
|
171
|
+
backgroundColor: `${ui.primaryColor}15`,
|
|
172
|
+
color: ui.primaryColor,
|
|
173
|
+
border: `1px solid ${ui.primaryColor}30`
|
|
174
|
+
}}
|
|
181
175
|
>
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
176
|
+
Select Files
|
|
177
|
+
</button>
|
|
178
|
+
<input
|
|
179
|
+
ref={fileInputRef}
|
|
180
|
+
type="file"
|
|
181
|
+
multiple
|
|
182
|
+
onChange={onFileSelect}
|
|
183
|
+
className="hidden"
|
|
184
|
+
accept=".pdf,.docx,.doc,.xlsx,.xls,.csv,.txt,.md,.json"
|
|
185
|
+
/>
|
|
186
|
+
|
|
187
|
+
<div className="mt-6 flex flex-col items-center gap-2 text-sm">
|
|
188
|
+
<label className="text-slate-600 dark:text-white/60 font-medium">Embedding Dimension</label>
|
|
189
|
+
<select
|
|
190
|
+
value={dimension}
|
|
191
|
+
onChange={(e) => setDimension(Number(e.target.value))}
|
|
192
|
+
disabled={isUploading}
|
|
193
|
+
className="px-3 py-1.5 rounded-lg bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 outline-none focus:border-emerald-500 transition-colors text-sm font-medium cursor-pointer"
|
|
194
|
+
>
|
|
195
|
+
{COMMON_DIMENSIONS.map(dim => {
|
|
196
|
+
const isDisabled = isFreeTier && dim !== 768;
|
|
197
|
+
return (
|
|
198
|
+
<option
|
|
199
|
+
key={dim}
|
|
200
|
+
value={dim}
|
|
201
|
+
disabled={isDisabled}
|
|
202
|
+
className="bg-white dark:bg-slate-900 text-slate-800 dark:text-white disabled:text-slate-400 dark:disabled:text-white/30"
|
|
203
|
+
>
|
|
204
|
+
{dim}
|
|
205
|
+
</option>
|
|
206
|
+
);
|
|
207
|
+
})}
|
|
208
|
+
</select>
|
|
209
|
+
</div>
|
|
196
210
|
</div>
|
|
197
|
-
</div>
|
|
198
211
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
{fileStates.length > 0 && (
|
|
213
|
+
<div className="mt-8 space-y-3">
|
|
214
|
+
{fileStates.map((state, i) => (
|
|
215
|
+
<div
|
|
216
|
+
key={i}
|
|
217
|
+
className="flex items-center justify-between p-3 rounded-xl bg-slate-50 dark:bg-white/5 border border-slate-100 dark:border-white/10"
|
|
218
|
+
>
|
|
219
|
+
<div className="flex items-center gap-3 overflow-hidden">
|
|
220
|
+
<div className="w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm">
|
|
221
|
+
<File className="w-4 h-4 text-slate-400" />
|
|
222
|
+
</div>
|
|
223
|
+
<div className="truncate">
|
|
224
|
+
<p className="text-xs font-medium text-slate-700 dark:text-white/80 truncate">
|
|
225
|
+
{state.file.name}
|
|
226
|
+
</p>
|
|
227
|
+
<p className="text-[10px] text-slate-400">
|
|
228
|
+
{(state.file.size / 1024).toFixed(1)} KB
|
|
229
|
+
</p>
|
|
230
|
+
</div>
|
|
209
231
|
</div>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
{
|
|
216
|
-
|
|
232
|
+
|
|
233
|
+
<div className="flex items-center gap-2">
|
|
234
|
+
{state.status === 'uploading' && <Loader2 className="w-4 h-4 text-slate-400 animate-spin" />}
|
|
235
|
+
{state.status === 'success' && <CheckCircle className="w-4 h-4 text-emerald-500" />}
|
|
236
|
+
{state.status === 'error' && (
|
|
237
|
+
<div title={state.error}>
|
|
238
|
+
<AlertCircle className="w-4 h-4 text-rose-500" />
|
|
239
|
+
</div>
|
|
240
|
+
)}
|
|
241
|
+
|
|
242
|
+
{state.status !== 'uploading' && (
|
|
243
|
+
<button
|
|
244
|
+
onClick={() => removeFile(i)}
|
|
245
|
+
className="p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
|
|
246
|
+
>
|
|
247
|
+
<X className="w-3.5 h-3.5 text-slate-400" />
|
|
248
|
+
</button>
|
|
249
|
+
)}
|
|
217
250
|
</div>
|
|
218
251
|
</div>
|
|
252
|
+
))}
|
|
253
|
+
|
|
254
|
+
{hasIdle && !isUploading && (
|
|
255
|
+
<button
|
|
256
|
+
onClick={uploadFiles}
|
|
257
|
+
className="w-full mt-4 py-2.5 rounded-xl text-sm font-semibold text-white shadow-lg transition-all hover:scale-[1.02] active:scale-[0.98]"
|
|
258
|
+
style={{ background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }}
|
|
259
|
+
>
|
|
260
|
+
Start Upload
|
|
261
|
+
</button>
|
|
262
|
+
)}
|
|
263
|
+
</div>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
219
266
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)}
|
|
228
|
-
|
|
229
|
-
{state.status !== 'uploading' && (
|
|
230
|
-
<button
|
|
231
|
-
onClick={() => removeFile(i)}
|
|
232
|
-
className="p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
|
|
233
|
-
>
|
|
234
|
-
<X className="w-3.5 h-3.5 text-slate-400" />
|
|
235
|
-
</button>
|
|
236
|
-
)}
|
|
267
|
+
{/* ── Upload Successful Modal Popup ────────────────────────────────────────── */}
|
|
268
|
+
{showSuccessModal && lastUploadSummary && (
|
|
269
|
+
<div className="fixed inset-0 bg-slate-950/70 backdrop-blur-sm z-50 flex items-center justify-center p-4 animate-fade-in">
|
|
270
|
+
<div className="bg-white dark:bg-slate-900 rounded-3xl border border-emerald-100 dark:border-emerald-500/20 p-6 max-w-sm w-full shadow-2xl space-y-4 text-slate-900 dark:text-white relative overflow-hidden animate-scale-up">
|
|
271
|
+
<div className="flex flex-col items-center text-center space-y-2">
|
|
272
|
+
<div className="w-14 h-14 rounded-2xl bg-emerald-50 dark:bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 flex items-center justify-center border border-emerald-100 dark:border-emerald-500/20 shadow-sm">
|
|
273
|
+
<CheckCircle2 className="w-8 h-8" />
|
|
237
274
|
</div>
|
|
275
|
+
<h3 className="text-xl font-extrabold tracking-tight">Upload Successful! 🎉</h3>
|
|
276
|
+
<p className="text-xs text-slate-500 dark:text-white/60 font-medium">
|
|
277
|
+
Your files have been chunked, vectorized, and ingested into Pinecone vector index.
|
|
278
|
+
</p>
|
|
238
279
|
</div>
|
|
239
|
-
))}
|
|
240
280
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
281
|
+
<div className="p-4 rounded-2xl bg-slate-50 dark:bg-white/5 border border-slate-100 dark:border-white/10 space-y-3">
|
|
282
|
+
<div className="flex items-center justify-between text-xs font-semibold text-slate-700 dark:text-white/90">
|
|
283
|
+
<span className="flex items-center gap-1.5 text-slate-500 dark:text-white/60">
|
|
284
|
+
<Files className="w-4 h-4 text-emerald-500" /> Total Files Ingested:
|
|
285
|
+
</span>
|
|
286
|
+
<span className="font-extrabold font-mono text-emerald-600 dark:text-emerald-400 text-sm">
|
|
287
|
+
{lastUploadSummary.totalFilesCount} Files
|
|
288
|
+
</span>
|
|
289
|
+
</div>
|
|
290
|
+
<div className="flex items-center justify-between text-xs font-semibold text-slate-700 dark:text-white/90">
|
|
291
|
+
<span className="text-slate-500 dark:text-white/60">Target Namespace:</span>
|
|
292
|
+
<span className="font-mono text-[11px] px-2 py-0.5 rounded bg-slate-200 dark:bg-white/10 text-slate-800 dark:text-white">
|
|
293
|
+
{lastUploadSummary.namespaceUsed}
|
|
294
|
+
</span>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div className="max-h-32 overflow-y-auto space-y-1.5 pr-1 scrollbar-thin">
|
|
299
|
+
{lastUploadSummary.filesUploaded.map((fn, idx) => (
|
|
300
|
+
<div key={idx} className="flex items-center justify-between text-xs p-2 rounded-lg bg-emerald-50/50 dark:bg-emerald-500/5 text-emerald-900 dark:text-emerald-300 border border-emerald-100/50 dark:border-emerald-500/10">
|
|
301
|
+
<span className="truncate font-medium">{fn}</span>
|
|
302
|
+
<Sparkles className="w-3.5 h-3.5 text-emerald-500 shrink-0 ml-2" />
|
|
303
|
+
</div>
|
|
304
|
+
))}
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div className="flex gap-2.5 pt-1">
|
|
308
|
+
<button
|
|
309
|
+
onClick={() => {
|
|
310
|
+
setShowSuccessModal(false);
|
|
311
|
+
setFileStates([]);
|
|
312
|
+
}}
|
|
313
|
+
className="flex-1 py-2.5 rounded-xl text-xs font-bold bg-slate-100 dark:bg-white/10 hover:bg-slate-200 dark:hover:bg-white/20 text-slate-700 dark:text-white transition-all cursor-pointer"
|
|
314
|
+
>
|
|
315
|
+
Clear & Reset
|
|
316
|
+
</button>
|
|
317
|
+
<button
|
|
318
|
+
onClick={() => setShowSuccessModal(false)}
|
|
319
|
+
className="flex-1 py-2.5 rounded-xl text-xs font-bold text-white shadow-md transition-all cursor-pointer"
|
|
320
|
+
style={{ backgroundColor: ui.primaryColor }}
|
|
321
|
+
>
|
|
322
|
+
Done
|
|
323
|
+
</button>
|
|
324
|
+
</div>
|
|
325
|
+
</div>
|
|
250
326
|
</div>
|
|
251
327
|
)}
|
|
252
|
-
|
|
328
|
+
</>
|
|
253
329
|
);
|
|
254
330
|
}
|
package/src/handlers/index.ts
CHANGED
|
@@ -169,7 +169,7 @@ function reportTelemetry(
|
|
|
169
169
|
const enabled = telemetryConfig?.enabled ?? Boolean(licenseKey);
|
|
170
170
|
const defaultUrl = process.env.NODE_ENV === 'development' && !process.env.TELEMETRY_URL && !process.env.NEXT_PUBLIC_TELEMETRY_URL
|
|
171
171
|
? 'http://localhost:3001/api/telemetry'
|
|
172
|
-
: 'https://retrivora.com/api/telemetry';
|
|
172
|
+
: 'https://www.retrivora.com/api/telemetry';
|
|
173
173
|
|
|
174
174
|
const telemetryUrl = telemetryConfig?.url || process.env.TELEMETRY_URL || process.env.NEXT_PUBLIC_TELEMETRY_URL || defaultUrl;
|
|
175
175
|
const host = req.headers.get('host') || 'localhost';
|
package/src/hooks/useRagChat.ts
CHANGED
|
@@ -62,13 +62,14 @@ export function useRagChat(
|
|
|
62
62
|
options: UseRagChatOptions = {}
|
|
63
63
|
): UseRagChatReturn {
|
|
64
64
|
const {
|
|
65
|
-
apiUrl
|
|
65
|
+
apiUrl,
|
|
66
66
|
retrivoraApiBase = '/api/retrivora',
|
|
67
67
|
namespace,
|
|
68
68
|
persist = true,
|
|
69
69
|
onReply,
|
|
70
70
|
onError,
|
|
71
71
|
} = options;
|
|
72
|
+
const primaryUrl = apiUrl || `${retrivoraApiBase.replace(/\/$/, '')}/chat`;
|
|
72
73
|
const storageKey = `rag_chat_${projectId}`;
|
|
73
74
|
|
|
74
75
|
const [messages, setMessages] = useStoredMessages<RagMessage>(storageKey, persist);
|
|
@@ -119,23 +120,38 @@ export function useRagChat(
|
|
|
119
120
|
const history = messagesRef.current.map(({ role, content }) => ({ role, content }));
|
|
120
121
|
const assistantMessageId = `assistant_${Date.now()}`;
|
|
121
122
|
|
|
122
|
-
const
|
|
123
|
+
const payloadBody = JSON.stringify({
|
|
124
|
+
message: trimmed,
|
|
125
|
+
history,
|
|
126
|
+
namespace: namespace ?? projectId,
|
|
127
|
+
sessionId: options.sessionId || 'default',
|
|
128
|
+
messageId: assistantMessageId,
|
|
129
|
+
userMessageId: userMsgId,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const fetchHeaders = {
|
|
133
|
+
'Content-Type': 'application/json',
|
|
134
|
+
...(options.headers || {})
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
let response = await fetch(primaryUrl, {
|
|
123
138
|
method: 'POST',
|
|
124
|
-
headers:
|
|
125
|
-
'Content-Type': 'application/json',
|
|
126
|
-
...(options.headers || {})
|
|
127
|
-
},
|
|
139
|
+
headers: fetchHeaders,
|
|
128
140
|
signal: controller.signal,
|
|
129
|
-
body:
|
|
130
|
-
message: trimmed,
|
|
131
|
-
history,
|
|
132
|
-
namespace: namespace ?? projectId,
|
|
133
|
-
sessionId: options.sessionId || 'default',
|
|
134
|
-
messageId: assistantMessageId,
|
|
135
|
-
userMessageId: userMsgId,
|
|
136
|
-
}),
|
|
141
|
+
body: payloadBody,
|
|
137
142
|
});
|
|
138
143
|
|
|
144
|
+
// Dynamic Fallback: If primaryUrl (e.g. /api/retrivora/chat) returns 404, retry fallback to /api/chat
|
|
145
|
+
if (response.status === 404 && !apiUrl && primaryUrl !== '/api/chat') {
|
|
146
|
+
console.warn(`[Retrivora SDK] ⚠️ Chat endpoint "${primaryUrl}" returned 404. Retrying fallback to "/api/chat"...`);
|
|
147
|
+
response = await fetch('/api/chat', {
|
|
148
|
+
method: 'POST',
|
|
149
|
+
headers: fetchHeaders,
|
|
150
|
+
signal: controller.signal,
|
|
151
|
+
body: payloadBody,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
139
155
|
if (!response.ok) {
|
|
140
156
|
const errorData = await response.json().catch(() => ({}));
|
|
141
157
|
throw new Error(errorData.error || `Server returned ${response.status}`);
|
package/src/index.css
CHANGED
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
--color-white: #fff;
|
|
113
113
|
--spacing: 0.25rem;
|
|
114
114
|
--container-xs: 20rem;
|
|
115
|
+
--container-sm: 24rem;
|
|
115
116
|
--container-md: 28rem;
|
|
116
117
|
--container-2xl: 42rem;
|
|
117
118
|
--container-4xl: 56rem;
|
|
@@ -123,11 +124,14 @@
|
|
|
123
124
|
--text-base--line-height: calc(1.5 / 1);
|
|
124
125
|
--text-lg: 1.125rem;
|
|
125
126
|
--text-lg--line-height: calc(1.75 / 1.125);
|
|
127
|
+
--text-xl: 1.25rem;
|
|
128
|
+
--text-xl--line-height: calc(1.75 / 1.25);
|
|
126
129
|
--text-3xl: 1.875rem;
|
|
127
130
|
--text-3xl--line-height: calc(2.25 / 1.875);
|
|
128
131
|
--font-weight-medium: 500;
|
|
129
132
|
--font-weight-semibold: 600;
|
|
130
133
|
--font-weight-bold: 700;
|
|
134
|
+
--font-weight-extrabold: 800;
|
|
131
135
|
--tracking-tight: -0.025em;
|
|
132
136
|
--tracking-wider: 0.05em;
|
|
133
137
|
--tracking-widest: 0.1em;
|
|
@@ -759,6 +763,9 @@
|
|
|
759
763
|
.max-w-none {
|
|
760
764
|
max-width: none;
|
|
761
765
|
}
|
|
766
|
+
.max-w-sm {
|
|
767
|
+
max-width: var(--container-sm);
|
|
768
|
+
}
|
|
762
769
|
.max-w-xs {
|
|
763
770
|
max-width: var(--container-xs);
|
|
764
771
|
}
|
|
@@ -777,6 +784,9 @@
|
|
|
777
784
|
.flex-shrink-0 {
|
|
778
785
|
flex-shrink: 0;
|
|
779
786
|
}
|
|
787
|
+
.shrink-0 {
|
|
788
|
+
flex-shrink: 0;
|
|
789
|
+
}
|
|
780
790
|
.flex-grow {
|
|
781
791
|
flex-grow: 1;
|
|
782
792
|
}
|
|
@@ -930,6 +940,9 @@
|
|
|
930
940
|
.gap-2 {
|
|
931
941
|
gap: calc(var(--spacing) * 2);
|
|
932
942
|
}
|
|
943
|
+
.gap-2\.5 {
|
|
944
|
+
gap: calc(var(--spacing) * 2.5);
|
|
945
|
+
}
|
|
933
946
|
.gap-3 {
|
|
934
947
|
gap: calc(var(--spacing) * 3);
|
|
935
948
|
}
|
|
@@ -1111,6 +1124,12 @@
|
|
|
1111
1124
|
.border-emerald-100 {
|
|
1112
1125
|
border-color: var(--color-emerald-100);
|
|
1113
1126
|
}
|
|
1127
|
+
.border-emerald-100\/50 {
|
|
1128
|
+
border-color: color-mix(in srgb, oklch(95% 0.052 163.051) 50%, transparent);
|
|
1129
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
1130
|
+
border-color: color-mix(in oklab, var(--color-emerald-100) 50%, transparent);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1114
1133
|
.border-emerald-200 {
|
|
1115
1134
|
border-color: var(--color-emerald-200);
|
|
1116
1135
|
}
|
|
@@ -1363,6 +1382,12 @@
|
|
|
1363
1382
|
.bg-slate-950 {
|
|
1364
1383
|
background-color: var(--color-slate-950);
|
|
1365
1384
|
}
|
|
1385
|
+
.bg-slate-950\/70 {
|
|
1386
|
+
background-color: color-mix(in srgb, oklch(12.9% 0.042 264.695) 70%, transparent);
|
|
1387
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
1388
|
+
background-color: color-mix(in oklab, var(--color-slate-950) 70%, transparent);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1366
1391
|
.bg-transparent {
|
|
1367
1392
|
background-color: transparent;
|
|
1368
1393
|
}
|
|
@@ -1520,6 +1545,10 @@
|
|
|
1520
1545
|
font-size: var(--text-sm);
|
|
1521
1546
|
line-height: var(--tw-leading, var(--text-sm--line-height));
|
|
1522
1547
|
}
|
|
1548
|
+
.text-xl {
|
|
1549
|
+
font-size: var(--text-xl);
|
|
1550
|
+
line-height: var(--tw-leading, var(--text-xl--line-height));
|
|
1551
|
+
}
|
|
1523
1552
|
.text-xs {
|
|
1524
1553
|
font-size: var(--text-xs);
|
|
1525
1554
|
line-height: var(--tw-leading, var(--text-xs--line-height));
|
|
@@ -1551,6 +1580,10 @@
|
|
|
1551
1580
|
--tw-font-weight: var(--font-weight-bold);
|
|
1552
1581
|
font-weight: var(--font-weight-bold);
|
|
1553
1582
|
}
|
|
1583
|
+
.font-extrabold {
|
|
1584
|
+
--tw-font-weight: var(--font-weight-extrabold);
|
|
1585
|
+
font-weight: var(--font-weight-extrabold);
|
|
1586
|
+
}
|
|
1554
1587
|
.font-medium {
|
|
1555
1588
|
--tw-font-weight: var(--font-weight-medium);
|
|
1556
1589
|
font-weight: var(--font-weight-medium);
|
|
@@ -1649,6 +1682,9 @@
|
|
|
1649
1682
|
.text-emerald-800 {
|
|
1650
1683
|
color: var(--color-emerald-800);
|
|
1651
1684
|
}
|
|
1685
|
+
.text-emerald-900 {
|
|
1686
|
+
color: var(--color-emerald-900);
|
|
1687
|
+
}
|
|
1652
1688
|
.text-emerald-950 {
|
|
1653
1689
|
color: var(--color-emerald-950);
|
|
1654
1690
|
}
|
|
@@ -2363,6 +2399,14 @@
|
|
|
2363
2399
|
}
|
|
2364
2400
|
}
|
|
2365
2401
|
}
|
|
2402
|
+
.dark\:border-emerald-500\/10 {
|
|
2403
|
+
@media (prefers-color-scheme: dark) {
|
|
2404
|
+
border-color: color-mix(in srgb, oklch(69.6% 0.17 162.48) 10%, transparent);
|
|
2405
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2406
|
+
border-color: color-mix(in oklab, var(--color-emerald-500) 10%, transparent);
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2366
2410
|
.dark\:border-emerald-500\/20 {
|
|
2367
2411
|
@media (prefers-color-scheme: dark) {
|
|
2368
2412
|
border-color: color-mix(in srgb, oklch(69.6% 0.17 162.48) 20%, transparent);
|