@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 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
- ## 📁 **NEW: File Management**
89
+ ## 🏷️ **NEW: Custom Scopes (Zero Config)**
90
90
 
91
- Now users can manage their uploaded files directly:
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
@@ -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, err_4, error_4;
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
- err_4 = _a.sent();
322
- error_4 = {
377
+ err_5 = _a.sent();
378
+ error_5 = {
323
379
  code: "QUERY_FAILED",
324
380
  message: "Failed to get answer with citations",
325
- details: err_4,
381
+ details: err_5,
326
382
  };
327
- setError(error_4);
328
- throw error_4;
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, err_5, errorMessage, newToken, result, refreshError_2, error_5;
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
- err_5 = _a.sent();
350
- errorMessage = err_5 instanceof Error ? err_5.message : String(err_5);
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
- refreshError_2 = _a.sent();
375
- console.error("❌ Token refresh failed:", refreshError_2);
430
+ refreshError_3 = _a.sent();
431
+ console.error("❌ Token refresh failed:", refreshError_3);
376
432
  return [3 /*break*/, 9];
377
433
  case 9:
378
- error_5 = {
434
+ error_6 = {
379
435
  code: "UPLOAD_FAILED",
380
436
  message: "Failed to upload file",
381
- details: err_5,
437
+ details: err_6,
382
438
  };
383
- setError(error_5);
384
- throw error_5;
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, err_6, errorMessage, newToken, result, refreshError_3, error_6;
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
- err_6 = _a.sent();
406
- errorMessage = err_6 instanceof Error ? err_6.message : String(err_6);
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
- refreshError_3 = _a.sent();
431
- console.error("❌ Token refresh failed during bulk upload:", refreshError_3);
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
- error_6 = {
490
+ error_7 = {
435
491
  code: "BULK_UPLOAD_FAILED",
436
492
  message: "Failed to upload files",
437
- details: err_6,
493
+ details: err_7,
438
494
  };
439
- setError(error_6);
440
- throw error_6;
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, err_7, errorMessage, newToken, result, refreshError_4, error_7;
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
- err_7 = _a.sent();
462
- errorMessage = err_7 instanceof Error ? err_7.message : String(err_7);
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
- refreshError_4 = _a.sent();
487
- console.error("❌ Token refresh failed:", refreshError_4);
542
+ refreshError_5 = _a.sent();
543
+ console.error("❌ Token refresh failed:", refreshError_5);
488
544
  return [3 /*break*/, 9];
489
545
  case 9:
490
- error_7 = {
546
+ error_8 = {
491
547
  code: "LIST_FILES_FAILED",
492
548
  message: "Failed to list files",
493
- details: err_7,
549
+ details: err_8,
494
550
  };
495
- setError(error_7);
496
- throw error_7;
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, err_8, errorMessage, newToken, result, refreshError_5, error_8;
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
- err_8 = _a.sent();
518
- errorMessage = err_8 instanceof Error ? err_8.message : String(err_8);
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
- refreshError_5 = _a.sent();
543
- console.error("❌ Token refresh failed:", refreshError_5);
598
+ refreshError_6 = _a.sent();
599
+ console.error("❌ Token refresh failed:", refreshError_6);
544
600
  return [3 /*break*/, 9];
545
601
  case 9:
546
- error_8 = {
602
+ error_9 = {
547
603
  code: "DELETE_FILE_FAILED",
548
604
  message: "Failed to delete file",
549
- details: err_8,
605
+ details: err_9,
550
606
  };
551
- setError(error_8);
552
- throw error_8;
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, err_9;
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
- err_9 = _a.sent();
644
+ err_10 = _a.sent();
589
645
  // Error is already set by askWithCitations
590
- console.error("Failed to send message:", err_9);
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 _h = useTrainly(), messages = _h.messages, sendMessage = _h.sendMessage, isLoading = _h.isLoading, error = _h.error, clearError = _h.clearError;
45
- var _j = React.useState(""), input = _j[0], setInput = _j[1];
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, , 4]);
72
- return [4 /*yield*/, sendMessage(messageContent)];
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
- onMessage === null || onMessage === void 0 ? void 0 : onMessage({ role: "user", content: messageContent });
76
- return [3 /*break*/, 4];
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*/, 4];
81
- case 4: return [2 /*return*/];
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.0",
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",