@trainly/react 1.3.1 → 1.4.1
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
|
-
##
|
|
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
|
@@ -333,7 +333,7 @@ export function TrainlyProvider(_a) {
|
|
|
333
333
|
}
|
|
334
334
|
});
|
|
335
335
|
}); };
|
|
336
|
-
var upload = function (file) { return __awaiter(_this, void 0, void 0, function () {
|
|
336
|
+
var upload = function (file, scopeValues) { return __awaiter(_this, void 0, void 0, function () {
|
|
337
337
|
var result, err_5, errorMessage, newToken, result, refreshError_2, error_5;
|
|
338
338
|
return __generator(this, function (_a) {
|
|
339
339
|
switch (_a.label) {
|
|
@@ -341,7 +341,7 @@ export function TrainlyProvider(_a) {
|
|
|
341
341
|
_a.trys.push([0, 2, 10, 11]);
|
|
342
342
|
setIsLoading(true);
|
|
343
343
|
setError(null);
|
|
344
|
-
return [4 /*yield*/, client.upload(file)];
|
|
344
|
+
return [4 /*yield*/, client.upload(file, scopeValues)];
|
|
345
345
|
case 1:
|
|
346
346
|
result = _a.sent();
|
|
347
347
|
return [2 /*return*/, result];
|
|
@@ -364,7 +364,7 @@ export function TrainlyProvider(_a) {
|
|
|
364
364
|
return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
|
|
365
365
|
case 5:
|
|
366
366
|
_a.sent();
|
|
367
|
-
return [4 /*yield*/, client.upload(file)];
|
|
367
|
+
return [4 /*yield*/, client.upload(file, scopeValues)];
|
|
368
368
|
case 6:
|
|
369
369
|
result = _a.sent();
|
|
370
370
|
console.log("✅ Upload succeeded after token refresh");
|
|
@@ -389,7 +389,7 @@ export function TrainlyProvider(_a) {
|
|
|
389
389
|
}
|
|
390
390
|
});
|
|
391
391
|
}); };
|
|
392
|
-
var bulkUploadFiles = function (files) { return __awaiter(_this, void 0, void 0, function () {
|
|
392
|
+
var bulkUploadFiles = function (files, scopeValues) { return __awaiter(_this, void 0, void 0, function () {
|
|
393
393
|
var result, err_6, errorMessage, newToken, result, refreshError_3, error_6;
|
|
394
394
|
return __generator(this, function (_a) {
|
|
395
395
|
switch (_a.label) {
|
|
@@ -397,7 +397,7 @@ export function TrainlyProvider(_a) {
|
|
|
397
397
|
_a.trys.push([0, 2, 10, 11]);
|
|
398
398
|
setIsLoading(true);
|
|
399
399
|
setError(null);
|
|
400
|
-
return [4 /*yield*/, client.bulkUploadFiles(files)];
|
|
400
|
+
return [4 /*yield*/, client.bulkUploadFiles(files, scopeValues)];
|
|
401
401
|
case 1:
|
|
402
402
|
result = _a.sent();
|
|
403
403
|
return [2 /*return*/, result];
|
|
@@ -420,7 +420,7 @@ export function TrainlyProvider(_a) {
|
|
|
420
420
|
return [4 /*yield*/, client.connectWithOAuthToken(newToken)];
|
|
421
421
|
case 5:
|
|
422
422
|
_a.sent();
|
|
423
|
-
return [4 /*yield*/, client.bulkUploadFiles(files)];
|
|
423
|
+
return [4 /*yield*/, client.bulkUploadFiles(files, scopeValues)];
|
|
424
424
|
case 6:
|
|
425
425
|
result = _a.sent();
|
|
426
426
|
console.log("✅ Bulk upload succeeded after token refresh");
|
|
@@ -17,9 +17,10 @@ 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
|
-
upload(file: File): Promise<UploadResult>;
|
|
22
|
-
bulkUploadFiles(files: File[]): Promise<BulkUploadResult>;
|
|
22
|
+
upload(file: File, scopeValues?: Record<string, string | number | boolean>): Promise<UploadResult>;
|
|
23
|
+
bulkUploadFiles(files: File[], scopeValues?: Record<string, string | number | boolean>): Promise<BulkUploadResult>;
|
|
23
24
|
listFiles(): Promise<FileListResult>;
|
|
24
25
|
deleteFile(fileId: string): Promise<FileDeleteResult>;
|
|
25
26
|
private extractChatId;
|
|
@@ -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();
|
|
@@ -218,7 +224,7 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
218
224
|
});
|
|
219
225
|
});
|
|
220
226
|
};
|
|
221
|
-
TrainlyClient.prototype.upload = function (file) {
|
|
227
|
+
TrainlyClient.prototype.upload = function (file, scopeValues) {
|
|
222
228
|
return __awaiter(this, void 0, void 0, function () {
|
|
223
229
|
var formData, response, error, data, formData, response, error, presignedResponse, error, _a, upload_url, upload_headers, formData, uploadResponse;
|
|
224
230
|
return __generator(this, function (_b) {
|
|
@@ -230,6 +236,10 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
230
236
|
if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
|
|
231
237
|
formData = new FormData();
|
|
232
238
|
formData.append("file", file);
|
|
239
|
+
// Add scope values if provided
|
|
240
|
+
if (scopeValues && Object.keys(scopeValues).length > 0) {
|
|
241
|
+
formData.append("scope_values", JSON.stringify(scopeValues));
|
|
242
|
+
}
|
|
233
243
|
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/files/upload"), {
|
|
234
244
|
method: "POST",
|
|
235
245
|
headers: {
|
|
@@ -258,6 +268,10 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
258
268
|
if (!this.config.apiKey) return [3 /*break*/, 9];
|
|
259
269
|
formData = new FormData();
|
|
260
270
|
formData.append("file", file);
|
|
271
|
+
// Add scope values if provided
|
|
272
|
+
if (scopeValues && Object.keys(scopeValues).length > 0) {
|
|
273
|
+
formData.append("scope_values", JSON.stringify(scopeValues));
|
|
274
|
+
}
|
|
261
275
|
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/upload_file"), {
|
|
262
276
|
method: "POST",
|
|
263
277
|
headers: {
|
|
@@ -324,7 +338,7 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
324
338
|
});
|
|
325
339
|
});
|
|
326
340
|
};
|
|
327
|
-
TrainlyClient.prototype.bulkUploadFiles = function (files) {
|
|
341
|
+
TrainlyClient.prototype.bulkUploadFiles = function (files, scopeValues) {
|
|
328
342
|
return __awaiter(this, void 0, void 0, function () {
|
|
329
343
|
var formData_1, response, error, data, results, successful_uploads, total_size_bytes, _i, files_1, file, uploadResult, error_1;
|
|
330
344
|
return __generator(this, function (_a) {
|
|
@@ -345,6 +359,10 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
345
359
|
files.forEach(function (file) {
|
|
346
360
|
formData_1.append("files", file);
|
|
347
361
|
});
|
|
362
|
+
// Add scope values if provided
|
|
363
|
+
if (scopeValues && Object.keys(scopeValues).length > 0) {
|
|
364
|
+
formData_1.append("scope_values", JSON.stringify(scopeValues));
|
|
365
|
+
}
|
|
348
366
|
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/files/upload-bulk"), {
|
|
349
367
|
method: "POST",
|
|
350
368
|
headers: {
|
|
@@ -386,7 +404,7 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
386
404
|
_a.label = 7;
|
|
387
405
|
case 7:
|
|
388
406
|
_a.trys.push([7, 9, , 10]);
|
|
389
|
-
return [4 /*yield*/, this.upload(file)];
|
|
407
|
+
return [4 /*yield*/, this.upload(file, scopeValues)];
|
|
390
408
|
case 8:
|
|
391
409
|
uploadResult = _a.sent();
|
|
392
410
|
results.push({
|
|
@@ -6,5 +6,6 @@ export interface TrainlyUploadProps {
|
|
|
6
6
|
className?: string;
|
|
7
7
|
onUpload?: (files: File[]) => void;
|
|
8
8
|
onError?: (error: string) => void;
|
|
9
|
+
scopeValues?: Record<string, string | number | boolean>;
|
|
9
10
|
}
|
|
10
|
-
export declare function TrainlyUpload({ variant, accept, maxSize, multiple, className, onUpload, onError, }: TrainlyUploadProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function TrainlyUpload({ variant, accept, maxSize, multiple, className, onUpload, onError, scopeValues, }: TrainlyUploadProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -40,7 +40,7 @@ import * as React from "react";
|
|
|
40
40
|
import { useTrainly } from "../useTrainly";
|
|
41
41
|
export function TrainlyUpload(_a) {
|
|
42
42
|
var _this = this;
|
|
43
|
-
var _b = _a.variant, variant = _b === void 0 ? "drag-drop" : _b, _c = _a.accept, accept = _c === void 0 ? ".pdf,.doc,.docx,.txt,.md" : _c, _d = _a.maxSize, maxSize = _d === void 0 ? "10MB" : _d, _e = _a.multiple, multiple = _e === void 0 ? false : _e, _f = _a.className, className = _f === void 0 ? "" : _f, onUpload = _a.onUpload, onError = _a.onError;
|
|
43
|
+
var _b = _a.variant, variant = _b === void 0 ? "drag-drop" : _b, _c = _a.accept, accept = _c === void 0 ? ".pdf,.doc,.docx,.txt,.md" : _c, _d = _a.maxSize, maxSize = _d === void 0 ? "10MB" : _d, _e = _a.multiple, multiple = _e === void 0 ? false : _e, _f = _a.className, className = _f === void 0 ? "" : _f, onUpload = _a.onUpload, onError = _a.onError, scopeValues = _a.scopeValues;
|
|
44
44
|
var _g = useTrainly(), upload = _g.upload, isLoading = _g.isLoading;
|
|
45
45
|
var _h = React.useState(false), isDragOver = _h[0], setIsDragOver = _h[1];
|
|
46
46
|
var fileInputRef = React.useRef(null);
|
|
@@ -70,7 +70,7 @@ export function TrainlyUpload(_a) {
|
|
|
70
70
|
case 2:
|
|
71
71
|
if (!(_a < fileArray_2.length)) return [3 /*break*/, 5];
|
|
72
72
|
file = fileArray_2[_a];
|
|
73
|
-
return [4 /*yield*/, upload(file)];
|
|
73
|
+
return [4 /*yield*/, upload(file, scopeValues)];
|
|
74
74
|
case 3:
|
|
75
75
|
_b.sent();
|
|
76
76
|
_b.label = 4;
|
package/dist/types.d.ts
CHANGED
|
@@ -93,10 +93,10 @@ export interface TrainlyContextValue {
|
|
|
93
93
|
answer: string;
|
|
94
94
|
citations: Citation[];
|
|
95
95
|
}>;
|
|
96
|
-
upload: (file: File) => Promise<UploadResult>;
|
|
96
|
+
upload: (file: File, scopeValues?: Record<string, string | number | boolean>) => Promise<UploadResult>;
|
|
97
97
|
listFiles: () => Promise<FileListResult>;
|
|
98
98
|
deleteFile: (fileId: string) => Promise<FileDeleteResult>;
|
|
99
|
-
bulkUploadFiles: (files: File[]) => Promise<BulkUploadResult>;
|
|
99
|
+
bulkUploadFiles: (files: File[], scopeValues?: Record<string, string | number | boolean>) => Promise<BulkUploadResult>;
|
|
100
100
|
connectWithOAuthToken: (idToken: string) => Promise<void>;
|
|
101
101
|
isLoading: boolean;
|
|
102
102
|
isConnected: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trainly/react",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Dead simple RAG integration for React apps with OAuth authentication",
|
|
3
|
+
"version": "1.4.1",
|
|
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",
|