@trainly/react 1.4.0 → 1.4.2
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/README.md +112 -2
- package/dist/TrainlyProvider.js +102 -45
- package/dist/api/TrainlyClient.d.ts +1 -0
- package/dist/api/TrainlyClient.js +11 -5
- package/dist/components/TrainlyChat.d.ts +2 -1
- package/dist/components/TrainlyChat.js +48 -11
- package/dist/types.d.ts +7 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -86,9 +86,58 @@ function MyComponent() {
|
|
|
86
86
|
- ✅ **Zero Migration**: Works with your existing OAuth setup
|
|
87
87
|
- ✅ **Simple Integration**: Just add `appId` and use `connectWithOAuthToken()`
|
|
88
88
|
|
|
89
|
-
##
|
|
89
|
+
## 🏷️ **NEW: Custom Scopes (Zero Config)**
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Tag your documents with custom attributes for powerful filtering and organization:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import { useTrainly } from "@trainly/react";
|
|
95
|
+
|
|
96
|
+
function MyApp() {
|
|
97
|
+
const { upload, ask } = useTrainly();
|
|
98
|
+
|
|
99
|
+
// 1. Upload with scopes - use any keys you want!
|
|
100
|
+
await upload(file, {
|
|
101
|
+
playlist_id: "xyz123",
|
|
102
|
+
workspace_id: "acme_corp",
|
|
103
|
+
project: "alpha",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// 2. Query with scope filters - only get results from matching documents
|
|
107
|
+
const answer = await ask("What are the key features?", {
|
|
108
|
+
scope_filters: { playlist_id: "xyz123" },
|
|
109
|
+
});
|
|
110
|
+
// ☝️ Only searches documents with playlist_id="xyz123"
|
|
111
|
+
|
|
112
|
+
// Query with multiple filters
|
|
113
|
+
const answer2 = await ask("Show me updates", {
|
|
114
|
+
scope_filters: {
|
|
115
|
+
workspace_id: "acme_corp",
|
|
116
|
+
project: "alpha",
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
// ☝️ Only searches documents matching ALL specified scopes
|
|
120
|
+
|
|
121
|
+
// Query everything (no filters)
|
|
122
|
+
const answer3 = await ask("What do I have?");
|
|
123
|
+
// ☝️ Searches ALL user's documents
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**No setup required!** Just pass any key-value pairs - perfect for multi-tenant apps, playlist systems, workspace organization, and more.
|
|
128
|
+
|
|
129
|
+
**Use Cases:**
|
|
130
|
+
|
|
131
|
+
- 🎵 **Playlist Apps**: Filter by `playlist_id` to query specific playlists
|
|
132
|
+
- 🏢 **Multi-Tenant SaaS**: Filter by `tenant_id` or `workspace_id`
|
|
133
|
+
- 📁 **Project Management**: Filter by `project_id` or `team_id`
|
|
134
|
+
- 👥 **User Segmentation**: Filter by `user_tier`, `department`, etc.
|
|
135
|
+
|
|
136
|
+
[📖 Full Scopes Guide](./SCOPES_GUIDE.md)
|
|
137
|
+
|
|
138
|
+
## 📁 **File Management**
|
|
139
|
+
|
|
140
|
+
Users can manage their uploaded files directly:
|
|
92
141
|
|
|
93
142
|
```tsx
|
|
94
143
|
import { useTrainly, TrainlyFileManager } from "@trainly/react";
|
|
@@ -142,6 +191,67 @@ function MyApp() {
|
|
|
142
191
|
- 📋 **List Files**: View all uploaded documents with metadata
|
|
143
192
|
- 🗑️ **Delete Files**: Remove files and free up storage space
|
|
144
193
|
- 📊 **Storage Analytics**: Track file sizes and storage usage
|
|
194
|
+
|
|
195
|
+
## 🏷️ **NEW in v1.4.0: Custom Scopes**
|
|
196
|
+
|
|
197
|
+
Tag your documents with custom attributes for powerful data segmentation!
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { useTrainly } from "@trainly/react";
|
|
201
|
+
|
|
202
|
+
function PlaylistUploader({ playlistId }) {
|
|
203
|
+
const { upload } = useTrainly();
|
|
204
|
+
|
|
205
|
+
const handleUpload = async (file: File) => {
|
|
206
|
+
// Upload with custom scope values
|
|
207
|
+
await upload(file, {
|
|
208
|
+
playlist_id: playlistId,
|
|
209
|
+
user_id: currentUser.id,
|
|
210
|
+
is_public: false,
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<input type="file" onChange={(e) => handleUpload(e.target.files[0])} />
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Scope Features
|
|
221
|
+
|
|
222
|
+
- 🎯 **Data Segmentation**: Keep playlists, workspaces, or projects separate
|
|
223
|
+
- ⚡ **Faster Queries**: Filter at database level before vector search
|
|
224
|
+
- 🔒 **Complete Isolation**: Multi-tenant apps with full data privacy
|
|
225
|
+
- 🎨 **Flexible**: Define any custom attributes you need
|
|
226
|
+
|
|
227
|
+
### With TrainlyUpload Component
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
<TrainlyUpload
|
|
231
|
+
variant="drag-drop"
|
|
232
|
+
scopeValues={{
|
|
233
|
+
playlist_id: "playlist_123",
|
|
234
|
+
workspace_id: "workspace_456",
|
|
235
|
+
}}
|
|
236
|
+
onUpload={(files) => console.log("Uploaded with scopes!")}
|
|
237
|
+
/>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Complete Documentation
|
|
241
|
+
|
|
242
|
+
See **[SCOPES_GUIDE.md](./SCOPES_GUIDE.md)** for:
|
|
243
|
+
|
|
244
|
+
- Complete API reference
|
|
245
|
+
- Real-world examples
|
|
246
|
+
- Advanced patterns
|
|
247
|
+
- Testing & debugging
|
|
248
|
+
- Migration guide
|
|
249
|
+
|
|
250
|
+
**Quick Reference:**
|
|
251
|
+
|
|
252
|
+
- `upload(file, scopeValues)` - Upload with scopes
|
|
253
|
+
- `bulkUploadFiles(files, scopeValues)` - Bulk upload with scopes
|
|
254
|
+
- `<TrainlyUpload scopeValues={{...}} />` - Component with scopes
|
|
145
255
|
- 🔄 **Auto-Refresh**: File list updates after uploads/deletions
|
|
146
256
|
- 🎨 **Pre-built UI**: `TrainlyFileManager` component with styling
|
|
147
257
|
- 🔒 **Privacy-First**: Only works in V1 mode with OAuth authentication
|
package/dist/TrainlyProvider.js
CHANGED
|
@@ -302,8 +302,64 @@ export function TrainlyProvider(_a) {
|
|
|
302
302
|
}
|
|
303
303
|
});
|
|
304
304
|
}); };
|
|
305
|
+
var askWithOptions = function (question, options) { return __awaiter(_this, void 0, void 0, function () {
|
|
306
|
+
var response, err_4, errorMessage, newToken, response, refreshError_2, error_4;
|
|
307
|
+
return __generator(this, function (_a) {
|
|
308
|
+
switch (_a.label) {
|
|
309
|
+
case 0:
|
|
310
|
+
_a.trys.push([0, 2, 10, 11]);
|
|
311
|
+
setIsLoading(true);
|
|
312
|
+
setError(null);
|
|
313
|
+
return [4 /*yield*/, client.ask(question, options || {})];
|
|
314
|
+
case 1:
|
|
315
|
+
response = _a.sent();
|
|
316
|
+
return [2 /*return*/, response];
|
|
317
|
+
case 2:
|
|
318
|
+
err_4 = _a.sent();
|
|
319
|
+
errorMessage = err_4 instanceof Error ? err_4.message : String(err_4);
|
|
320
|
+
if (!(getToken &&
|
|
321
|
+
appId &&
|
|
322
|
+
(errorMessage.includes("401") ||
|
|
323
|
+
errorMessage.includes("authentication") ||
|
|
324
|
+
errorMessage.includes("Unauthorized")))) return [3 /*break*/, 9];
|
|
325
|
+
_a.label = 3;
|
|
326
|
+
case 3:
|
|
327
|
+
_a.trys.push([3, 8, , 9]);
|
|
328
|
+
console.log("🔄 Token expired, refreshing...");
|
|
329
|
+
return [4 /*yield*/, getToken()];
|
|
330
|
+
case 4:
|
|
331
|
+
newToken = _a.sent();
|
|
332
|
+
if (!newToken) return [3 /*break*/, 7];
|
|
333
|
+
return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
|
|
334
|
+
case 5:
|
|
335
|
+
_a.sent();
|
|
336
|
+
return [4 /*yield*/, client.ask(question, options || {})];
|
|
337
|
+
case 6:
|
|
338
|
+
response = _a.sent();
|
|
339
|
+
console.log("✅ Query succeeded after token refresh");
|
|
340
|
+
return [2 /*return*/, response];
|
|
341
|
+
case 7: return [3 /*break*/, 9];
|
|
342
|
+
case 8:
|
|
343
|
+
refreshError_2 = _a.sent();
|
|
344
|
+
console.error("❌ Token refresh failed:", refreshError_2);
|
|
345
|
+
return [3 /*break*/, 9];
|
|
346
|
+
case 9:
|
|
347
|
+
error_4 = {
|
|
348
|
+
code: "QUERY_FAILED",
|
|
349
|
+
message: "Failed to get answer",
|
|
350
|
+
details: err_4,
|
|
351
|
+
};
|
|
352
|
+
setError(error_4);
|
|
353
|
+
throw error_4;
|
|
354
|
+
case 10:
|
|
355
|
+
setIsLoading(false);
|
|
356
|
+
return [7 /*endfinally*/];
|
|
357
|
+
case 11: return [2 /*return*/];
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
}); };
|
|
305
361
|
var askWithCitations = function (question) { return __awaiter(_this, void 0, void 0, function () {
|
|
306
|
-
var response,
|
|
362
|
+
var response, err_5, error_5;
|
|
307
363
|
return __generator(this, function (_a) {
|
|
308
364
|
switch (_a.label) {
|
|
309
365
|
case 0:
|
|
@@ -318,14 +374,14 @@ export function TrainlyProvider(_a) {
|
|
|
318
374
|
citations: response.citations || [],
|
|
319
375
|
}];
|
|
320
376
|
case 2:
|
|
321
|
-
|
|
322
|
-
|
|
377
|
+
err_5 = _a.sent();
|
|
378
|
+
error_5 = {
|
|
323
379
|
code: "QUERY_FAILED",
|
|
324
380
|
message: "Failed to get answer with citations",
|
|
325
|
-
details:
|
|
381
|
+
details: err_5,
|
|
326
382
|
};
|
|
327
|
-
setError(
|
|
328
|
-
throw
|
|
383
|
+
setError(error_5);
|
|
384
|
+
throw error_5;
|
|
329
385
|
case 3:
|
|
330
386
|
setIsLoading(false);
|
|
331
387
|
return [7 /*endfinally*/];
|
|
@@ -334,7 +390,7 @@ export function TrainlyProvider(_a) {
|
|
|
334
390
|
});
|
|
335
391
|
}); };
|
|
336
392
|
var upload = function (file, scopeValues) { return __awaiter(_this, void 0, void 0, function () {
|
|
337
|
-
var result,
|
|
393
|
+
var result, err_6, errorMessage, newToken, result, refreshError_3, error_6;
|
|
338
394
|
return __generator(this, function (_a) {
|
|
339
395
|
switch (_a.label) {
|
|
340
396
|
case 0:
|
|
@@ -346,8 +402,8 @@ export function TrainlyProvider(_a) {
|
|
|
346
402
|
result = _a.sent();
|
|
347
403
|
return [2 /*return*/, result];
|
|
348
404
|
case 2:
|
|
349
|
-
|
|
350
|
-
errorMessage =
|
|
405
|
+
err_6 = _a.sent();
|
|
406
|
+
errorMessage = err_6 instanceof Error ? err_6.message : String(err_6);
|
|
351
407
|
if (!(getToken &&
|
|
352
408
|
appId &&
|
|
353
409
|
(errorMessage.includes("401") ||
|
|
@@ -371,17 +427,17 @@ export function TrainlyProvider(_a) {
|
|
|
371
427
|
return [2 /*return*/, result];
|
|
372
428
|
case 7: return [3 /*break*/, 9];
|
|
373
429
|
case 8:
|
|
374
|
-
|
|
375
|
-
console.error("❌ Token refresh failed:",
|
|
430
|
+
refreshError_3 = _a.sent();
|
|
431
|
+
console.error("❌ Token refresh failed:", refreshError_3);
|
|
376
432
|
return [3 /*break*/, 9];
|
|
377
433
|
case 9:
|
|
378
|
-
|
|
434
|
+
error_6 = {
|
|
379
435
|
code: "UPLOAD_FAILED",
|
|
380
436
|
message: "Failed to upload file",
|
|
381
|
-
details:
|
|
437
|
+
details: err_6,
|
|
382
438
|
};
|
|
383
|
-
setError(
|
|
384
|
-
throw
|
|
439
|
+
setError(error_6);
|
|
440
|
+
throw error_6;
|
|
385
441
|
case 10:
|
|
386
442
|
setIsLoading(false);
|
|
387
443
|
return [7 /*endfinally*/];
|
|
@@ -390,7 +446,7 @@ export function TrainlyProvider(_a) {
|
|
|
390
446
|
});
|
|
391
447
|
}); };
|
|
392
448
|
var bulkUploadFiles = function (files, scopeValues) { return __awaiter(_this, void 0, void 0, function () {
|
|
393
|
-
var result,
|
|
449
|
+
var result, err_7, errorMessage, newToken, result, refreshError_4, error_7;
|
|
394
450
|
return __generator(this, function (_a) {
|
|
395
451
|
switch (_a.label) {
|
|
396
452
|
case 0:
|
|
@@ -402,8 +458,8 @@ export function TrainlyProvider(_a) {
|
|
|
402
458
|
result = _a.sent();
|
|
403
459
|
return [2 /*return*/, result];
|
|
404
460
|
case 2:
|
|
405
|
-
|
|
406
|
-
errorMessage =
|
|
461
|
+
err_7 = _a.sent();
|
|
462
|
+
errorMessage = err_7 instanceof Error ? err_7.message : String(err_7);
|
|
407
463
|
if (!(getToken &&
|
|
408
464
|
appId &&
|
|
409
465
|
(errorMessage.includes("401") ||
|
|
@@ -427,17 +483,17 @@ export function TrainlyProvider(_a) {
|
|
|
427
483
|
return [2 /*return*/, result];
|
|
428
484
|
case 7: return [3 /*break*/, 9];
|
|
429
485
|
case 8:
|
|
430
|
-
|
|
431
|
-
console.error("❌ Token refresh failed during bulk upload:",
|
|
486
|
+
refreshError_4 = _a.sent();
|
|
487
|
+
console.error("❌ Token refresh failed during bulk upload:", refreshError_4);
|
|
432
488
|
return [3 /*break*/, 9];
|
|
433
489
|
case 9:
|
|
434
|
-
|
|
490
|
+
error_7 = {
|
|
435
491
|
code: "BULK_UPLOAD_FAILED",
|
|
436
492
|
message: "Failed to upload files",
|
|
437
|
-
details:
|
|
493
|
+
details: err_7,
|
|
438
494
|
};
|
|
439
|
-
setError(
|
|
440
|
-
throw
|
|
495
|
+
setError(error_7);
|
|
496
|
+
throw error_7;
|
|
441
497
|
case 10:
|
|
442
498
|
setIsLoading(false);
|
|
443
499
|
return [7 /*endfinally*/];
|
|
@@ -446,7 +502,7 @@ export function TrainlyProvider(_a) {
|
|
|
446
502
|
});
|
|
447
503
|
}); };
|
|
448
504
|
var listFiles = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
449
|
-
var result,
|
|
505
|
+
var result, err_8, errorMessage, newToken, result, refreshError_5, error_8;
|
|
450
506
|
return __generator(this, function (_a) {
|
|
451
507
|
switch (_a.label) {
|
|
452
508
|
case 0:
|
|
@@ -458,8 +514,8 @@ export function TrainlyProvider(_a) {
|
|
|
458
514
|
result = _a.sent();
|
|
459
515
|
return [2 /*return*/, result];
|
|
460
516
|
case 2:
|
|
461
|
-
|
|
462
|
-
errorMessage =
|
|
517
|
+
err_8 = _a.sent();
|
|
518
|
+
errorMessage = err_8 instanceof Error ? err_8.message : String(err_8);
|
|
463
519
|
if (!(getToken &&
|
|
464
520
|
appId &&
|
|
465
521
|
(errorMessage.includes("401") ||
|
|
@@ -483,17 +539,17 @@ export function TrainlyProvider(_a) {
|
|
|
483
539
|
return [2 /*return*/, result];
|
|
484
540
|
case 7: return [3 /*break*/, 9];
|
|
485
541
|
case 8:
|
|
486
|
-
|
|
487
|
-
console.error("❌ Token refresh failed:",
|
|
542
|
+
refreshError_5 = _a.sent();
|
|
543
|
+
console.error("❌ Token refresh failed:", refreshError_5);
|
|
488
544
|
return [3 /*break*/, 9];
|
|
489
545
|
case 9:
|
|
490
|
-
|
|
546
|
+
error_8 = {
|
|
491
547
|
code: "LIST_FILES_FAILED",
|
|
492
548
|
message: "Failed to list files",
|
|
493
|
-
details:
|
|
549
|
+
details: err_8,
|
|
494
550
|
};
|
|
495
|
-
setError(
|
|
496
|
-
throw
|
|
551
|
+
setError(error_8);
|
|
552
|
+
throw error_8;
|
|
497
553
|
case 10:
|
|
498
554
|
setIsLoading(false);
|
|
499
555
|
return [7 /*endfinally*/];
|
|
@@ -502,7 +558,7 @@ export function TrainlyProvider(_a) {
|
|
|
502
558
|
});
|
|
503
559
|
}); };
|
|
504
560
|
var deleteFile = function (fileId) { return __awaiter(_this, void 0, void 0, function () {
|
|
505
|
-
var result,
|
|
561
|
+
var result, err_9, errorMessage, newToken, result, refreshError_6, error_9;
|
|
506
562
|
return __generator(this, function (_a) {
|
|
507
563
|
switch (_a.label) {
|
|
508
564
|
case 0:
|
|
@@ -514,8 +570,8 @@ export function TrainlyProvider(_a) {
|
|
|
514
570
|
result = _a.sent();
|
|
515
571
|
return [2 /*return*/, result];
|
|
516
572
|
case 2:
|
|
517
|
-
|
|
518
|
-
errorMessage =
|
|
573
|
+
err_9 = _a.sent();
|
|
574
|
+
errorMessage = err_9 instanceof Error ? err_9.message : String(err_9);
|
|
519
575
|
if (!(getToken &&
|
|
520
576
|
appId &&
|
|
521
577
|
(errorMessage.includes("401") ||
|
|
@@ -539,17 +595,17 @@ export function TrainlyProvider(_a) {
|
|
|
539
595
|
return [2 /*return*/, result];
|
|
540
596
|
case 7: return [3 /*break*/, 9];
|
|
541
597
|
case 8:
|
|
542
|
-
|
|
543
|
-
console.error("❌ Token refresh failed:",
|
|
598
|
+
refreshError_6 = _a.sent();
|
|
599
|
+
console.error("❌ Token refresh failed:", refreshError_6);
|
|
544
600
|
return [3 /*break*/, 9];
|
|
545
601
|
case 9:
|
|
546
|
-
|
|
602
|
+
error_9 = {
|
|
547
603
|
code: "DELETE_FILE_FAILED",
|
|
548
604
|
message: "Failed to delete file",
|
|
549
|
-
details:
|
|
605
|
+
details: err_9,
|
|
550
606
|
};
|
|
551
|
-
setError(
|
|
552
|
-
throw
|
|
607
|
+
setError(error_9);
|
|
608
|
+
throw error_9;
|
|
553
609
|
case 10:
|
|
554
610
|
setIsLoading(false);
|
|
555
611
|
return [7 /*endfinally*/];
|
|
@@ -558,7 +614,7 @@ export function TrainlyProvider(_a) {
|
|
|
558
614
|
});
|
|
559
615
|
}); };
|
|
560
616
|
var sendMessage = function (content) { return __awaiter(_this, void 0, void 0, function () {
|
|
561
|
-
var userMessage, response, assistantMessage_1,
|
|
617
|
+
var userMessage, response, assistantMessage_1, err_10;
|
|
562
618
|
return __generator(this, function (_a) {
|
|
563
619
|
switch (_a.label) {
|
|
564
620
|
case 0:
|
|
@@ -585,9 +641,9 @@ export function TrainlyProvider(_a) {
|
|
|
585
641
|
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
|
|
586
642
|
return [3 /*break*/, 4];
|
|
587
643
|
case 3:
|
|
588
|
-
|
|
644
|
+
err_10 = _a.sent();
|
|
589
645
|
// Error is already set by askWithCitations
|
|
590
|
-
console.error("Failed to send message:",
|
|
646
|
+
console.error("Failed to send message:", err_10);
|
|
591
647
|
return [3 /*break*/, 4];
|
|
592
648
|
case 4: return [2 /*return*/];
|
|
593
649
|
}
|
|
@@ -599,6 +655,7 @@ export function TrainlyProvider(_a) {
|
|
|
599
655
|
var value = {
|
|
600
656
|
ask: ask,
|
|
601
657
|
askWithCitations: askWithCitations,
|
|
658
|
+
askWithOptions: askWithOptions, // NEW: Ask with scope filters and options
|
|
602
659
|
upload: upload,
|
|
603
660
|
bulkUploadFiles: bulkUploadFiles, // NEW: Bulk file upload method
|
|
604
661
|
listFiles: listFiles, // NEW: File management methods
|
|
@@ -17,6 +17,7 @@ export declare class TrainlyClient {
|
|
|
17
17
|
connect(): Promise<void>;
|
|
18
18
|
ask(question: string, options?: {
|
|
19
19
|
includeCitations?: boolean;
|
|
20
|
+
scope_filters?: Record<string, string | number | boolean>;
|
|
20
21
|
}): Promise<QueryResponse>;
|
|
21
22
|
upload(file: File, scopeValues?: Record<string, string | number | boolean>): Promise<UploadResult>;
|
|
22
23
|
bulkUploadFiles(files: File[], scopeValues?: Record<string, string | number | boolean>): Promise<BulkUploadResult>;
|
|
@@ -142,7 +142,7 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
142
142
|
};
|
|
143
143
|
TrainlyClient.prototype.ask = function (question_1) {
|
|
144
144
|
return __awaiter(this, arguments, void 0, function (question, options) {
|
|
145
|
-
var response_1, error, data_1, url, headers, body, response, error, data;
|
|
145
|
+
var params, response_1, error, data_1, url, headers, body, response, error, data;
|
|
146
146
|
if (options === void 0) { options = {}; }
|
|
147
147
|
return __generator(this, function (_a) {
|
|
148
148
|
switch (_a.label) {
|
|
@@ -151,6 +151,15 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
151
151
|
throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
|
|
152
152
|
}
|
|
153
153
|
if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
|
|
154
|
+
params = {
|
|
155
|
+
messages: JSON.stringify([{ role: "user", content: question }]),
|
|
156
|
+
response_tokens: "150",
|
|
157
|
+
};
|
|
158
|
+
// Add scope filters if provided
|
|
159
|
+
if (options.scope_filters &&
|
|
160
|
+
Object.keys(options.scope_filters).length > 0) {
|
|
161
|
+
params.scope_filters = JSON.stringify(options.scope_filters);
|
|
162
|
+
}
|
|
154
163
|
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/query"), {
|
|
155
164
|
method: "POST",
|
|
156
165
|
headers: {
|
|
@@ -158,10 +167,7 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
158
167
|
"X-App-ID": this.config.appId,
|
|
159
168
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
160
169
|
},
|
|
161
|
-
body: new URLSearchParams(
|
|
162
|
-
messages: JSON.stringify([{ role: "user", content: question }]),
|
|
163
|
-
response_tokens: "150",
|
|
164
|
-
}),
|
|
170
|
+
body: new URLSearchParams(params),
|
|
165
171
|
})];
|
|
166
172
|
case 1:
|
|
167
173
|
response_1 = _a.sent();
|
|
@@ -5,10 +5,11 @@ export interface TrainlyChatProps {
|
|
|
5
5
|
showCitations?: boolean;
|
|
6
6
|
enableFileUpload?: boolean;
|
|
7
7
|
theme?: "light" | "dark" | "auto";
|
|
8
|
+
scopeFilters?: Record<string, string | number | boolean>;
|
|
8
9
|
onMessage?: (message: {
|
|
9
10
|
role: "user" | "assistant";
|
|
10
11
|
content: string;
|
|
11
12
|
}) => void;
|
|
12
13
|
onError?: (error: string) => void;
|
|
13
14
|
}
|
|
14
|
-
export declare function TrainlyChat({ height, className, placeholder, showCitations, enableFileUpload, theme, onMessage, onError, }: TrainlyChatProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function TrainlyChat({ height, className, placeholder, showCitations, enableFileUpload, theme, scopeFilters, onMessage, onError, }: TrainlyChatProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -35,14 +35,26 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
35
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
39
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
40
|
+
if (ar || !(i in from)) {
|
|
41
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
42
|
+
ar[i] = from[i];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
46
|
+
};
|
|
38
47
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
39
48
|
import * as React from "react";
|
|
40
49
|
import { useTrainly } from "../useTrainly";
|
|
41
50
|
export function TrainlyChat(_a) {
|
|
42
51
|
var _this = this;
|
|
43
|
-
var _b = _a.height, height = _b === void 0 ? "400px" : _b, _c = _a.className, className = _c === void 0 ? "" : _c, _d = _a.placeholder, placeholder = _d === void 0 ? "Ask me anything..." : _d, _e = _a.showCitations, showCitations = _e === void 0 ? true : _e, _f = _a.enableFileUpload, enableFileUpload = _f === void 0 ? true : _f, _g = _a.theme, theme = _g === void 0 ? "auto" : _g, onMessage = _a.onMessage, onError = _a.onError;
|
|
44
|
-
var
|
|
45
|
-
var
|
|
52
|
+
var _b = _a.height, height = _b === void 0 ? "400px" : _b, _c = _a.className, className = _c === void 0 ? "" : _c, _d = _a.placeholder, placeholder = _d === void 0 ? "Ask me anything..." : _d, _e = _a.showCitations, showCitations = _e === void 0 ? true : _e, _f = _a.enableFileUpload, enableFileUpload = _f === void 0 ? true : _f, _g = _a.theme, theme = _g === void 0 ? "auto" : _g, scopeFilters = _a.scopeFilters, onMessage = _a.onMessage, onError = _a.onError;
|
|
53
|
+
var askWithOptions = useTrainly().askWithOptions;
|
|
54
|
+
var _h = React.useState(""), input = _h[0], setInput = _h[1];
|
|
55
|
+
var _j = React.useState([]), messages = _j[0], setMessages = _j[1];
|
|
56
|
+
var _k = React.useState(false), isLoading = _k[0], setIsLoading = _k[1];
|
|
57
|
+
var _l = React.useState(null), error = _l[0], setError = _l[1];
|
|
46
58
|
var messagesEndRef = React.useRef(null);
|
|
47
59
|
var fileInputRef = React.useRef(null);
|
|
48
60
|
// Auto-scroll to bottom
|
|
@@ -56,8 +68,9 @@ export function TrainlyChat(_a) {
|
|
|
56
68
|
onError(error.message);
|
|
57
69
|
}
|
|
58
70
|
}, [error, onError]);
|
|
71
|
+
var clearError = function () { return setError(null); };
|
|
59
72
|
var handleSubmit = function (e) { return __awaiter(_this, void 0, void 0, function () {
|
|
60
|
-
var messageContent, err_1;
|
|
73
|
+
var messageContent, userMessage, response, assistantMessage_1, err_1, errorMsg;
|
|
61
74
|
return __generator(this, function (_a) {
|
|
62
75
|
switch (_a.label) {
|
|
63
76
|
case 0:
|
|
@@ -66,19 +79,43 @@ export function TrainlyChat(_a) {
|
|
|
66
79
|
return [2 /*return*/];
|
|
67
80
|
messageContent = input.trim();
|
|
68
81
|
setInput("");
|
|
82
|
+
setError(null);
|
|
83
|
+
userMessage = {
|
|
84
|
+
id: Date.now().toString(),
|
|
85
|
+
role: "user",
|
|
86
|
+
content: messageContent,
|
|
87
|
+
};
|
|
88
|
+
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [userMessage], false); });
|
|
89
|
+
onMessage === null || onMessage === void 0 ? void 0 : onMessage(userMessage);
|
|
90
|
+
setIsLoading(true);
|
|
69
91
|
_a.label = 1;
|
|
70
92
|
case 1:
|
|
71
|
-
_a.trys.push([1, 3, ,
|
|
72
|
-
return [4 /*yield*/,
|
|
93
|
+
_a.trys.push([1, 3, 4, 5]);
|
|
94
|
+
return [4 /*yield*/, askWithOptions(messageContent, {
|
|
95
|
+
includeCitations: showCitations,
|
|
96
|
+
scope_filters: scopeFilters,
|
|
97
|
+
})];
|
|
73
98
|
case 2:
|
|
74
|
-
_a.sent();
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
response = _a.sent();
|
|
100
|
+
assistantMessage_1 = {
|
|
101
|
+
id: (Date.now() + 1).toString(),
|
|
102
|
+
role: "assistant",
|
|
103
|
+
content: response.answer,
|
|
104
|
+
citations: response.citations,
|
|
105
|
+
};
|
|
106
|
+
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
|
|
107
|
+
onMessage === null || onMessage === void 0 ? void 0 : onMessage(assistantMessage_1);
|
|
108
|
+
return [3 /*break*/, 5];
|
|
77
109
|
case 3:
|
|
78
110
|
err_1 = _a.sent();
|
|
111
|
+
errorMsg = err_1 instanceof Error ? err_1.message : "Failed to send message";
|
|
112
|
+
setError({ message: errorMsg });
|
|
79
113
|
console.error("Failed to send message:", err_1);
|
|
80
|
-
return [3 /*break*/,
|
|
81
|
-
case 4:
|
|
114
|
+
return [3 /*break*/, 5];
|
|
115
|
+
case 4:
|
|
116
|
+
setIsLoading(false);
|
|
117
|
+
return [7 /*endfinally*/];
|
|
118
|
+
case 5: return [2 /*return*/];
|
|
82
119
|
}
|
|
83
120
|
});
|
|
84
121
|
}); };
|
package/dist/types.d.ts
CHANGED
|
@@ -93,6 +93,13 @@ export interface TrainlyContextValue {
|
|
|
93
93
|
answer: string;
|
|
94
94
|
citations: Citation[];
|
|
95
95
|
}>;
|
|
96
|
+
askWithOptions: (question: string, options?: {
|
|
97
|
+
includeCitations?: boolean;
|
|
98
|
+
scope_filters?: Record<string, string | number | boolean>;
|
|
99
|
+
}) => Promise<{
|
|
100
|
+
answer: string;
|
|
101
|
+
citations?: Citation[];
|
|
102
|
+
}>;
|
|
96
103
|
upload: (file: File, scopeValues?: Record<string, string | number | boolean>) => Promise<UploadResult>;
|
|
97
104
|
listFiles: () => Promise<FileListResult>;
|
|
98
105
|
deleteFile: (fileId: string) => Promise<FileDeleteResult>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainly/react",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "Dead simple RAG integration for React apps with OAuth authentication",
|
|
3
|
+
"version": "1.4.2",
|
|
4
|
+
"description": "Dead simple RAG integration for React apps with OAuth authentication and custom scopes",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -28,7 +28,11 @@
|
|
|
28
28
|
"document-chat",
|
|
29
29
|
"file-management",
|
|
30
30
|
"file-upload",
|
|
31
|
-
"file-deletion"
|
|
31
|
+
"file-deletion",
|
|
32
|
+
"custom-scopes",
|
|
33
|
+
"scope-filtering",
|
|
34
|
+
"data-segmentation",
|
|
35
|
+
"multi-tenant"
|
|
32
36
|
],
|
|
33
37
|
"author": "Trainly",
|
|
34
38
|
"license": "MIT",
|