@trainly/react 1.0.3 → 1.1.0

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
@@ -1,10 +1,94 @@
1
1
  # @trainly/react
2
2
 
3
- **Dead simple RAG integration for React apps**
3
+ **Dead simple RAG integration for React apps with V1 OAuth Authentication**
4
4
 
5
- Go from `npm install` to working AI in under 5 minutes. No backend required, no complex setup, just install and use.
5
+ Go from `npm install` to working AI in under 5 minutes. Now supports direct OAuth integration with **permanent user subchats** and complete privacy protection.
6
6
 
7
- ## 🚀 Quick Start
7
+ ## 🆕 **NEW: V1 Trusted Issuer Authentication**
8
+
9
+ Use your existing OAuth provider (Clerk, Auth0, Cognito) directly with Trainly! Users get permanent private workspaces, and developers never see raw files or queries.
10
+
11
+ ### V1 Quick Start
12
+
13
+ #### 1. Install
14
+
15
+ ```bash
16
+ npm install @trainly/react
17
+ ```
18
+
19
+ #### 2. Register Your OAuth App (One-time)
20
+
21
+ ```bash
22
+ curl -X POST "http://localhost:8000/v1/console/apps/register" \
23
+ -H "X-Admin-Token: admin_dev_token_123" \
24
+ -F "app_name=My App" \
25
+ -F "issuer=https://clerk.myapp.com" \
26
+ -F 'allowed_audiences=["my-clerk-frontend-api"]'
27
+ ```
28
+
29
+ Save the `app_id` from the response!
30
+
31
+ #### 3. Setup with V1 (Clerk Example)
32
+
33
+ ```tsx
34
+ // app/layout.tsx
35
+ import { ClerkProvider } from "@clerk/nextjs";
36
+ import { TrainlyProvider } from "@trainly/react";
37
+
38
+ export default function RootLayout({ children }) {
39
+ return (
40
+ <html>
41
+ <body>
42
+ <ClerkProvider>
43
+ <TrainlyProvider appId="your_app_id_from_step_2">
44
+ {children}
45
+ </TrainlyProvider>
46
+ </ClerkProvider>
47
+ </body>
48
+ </html>
49
+ );
50
+ }
51
+ ```
52
+
53
+ #### 4. Use with OAuth Authentication
54
+
55
+ ```tsx
56
+ // Any component
57
+ import { useAuth } from "@clerk/nextjs";
58
+ import { useTrainly } from "@trainly/react";
59
+
60
+ function MyComponent() {
61
+ const { getToken } = useAuth();
62
+ const { ask, connectWithOAuthToken } = useTrainly();
63
+
64
+ React.useEffect(() => {
65
+ async function setupTrainly() {
66
+ const idToken = await getToken();
67
+ await connectWithOAuthToken(idToken);
68
+ }
69
+ setupTrainly();
70
+ }, []);
71
+
72
+ const handleClick = async () => {
73
+ const answer = await ask("What files do I have?");
74
+ console.log(answer); // AI response from user's permanent private subchat!
75
+ };
76
+
77
+ return <button onClick={handleClick}>Ask My AI</button>;
78
+ }
79
+ ```
80
+
81
+ ## 🔒 **V1 Benefits**
82
+
83
+ - ✅ **Permanent User Data**: Same user = same private subchat forever
84
+ - ✅ **Complete Privacy**: Developer never sees user files or queries
85
+ - ✅ **Any OAuth Provider**: Clerk, Auth0, Cognito, Firebase, custom OIDC
86
+ - ✅ **Zero Migration**: Works with your existing OAuth setup
87
+ - ✅ **Simple Integration**: Just add `appId` and use `connectWithOAuthToken()`
88
+
89
+ ---
90
+
91
+ ## 🚀 Original Quick Start (Legacy)
8
92
 
9
93
  ### 1. Install
10
94
 
@@ -111,20 +195,45 @@ function App() {
111
195
  ### Authentication Modes
112
196
 
113
197
  ```tsx
114
- // Mode 1: App Secret (recommended for multi-user apps)
198
+ // Mode 1: V1 Trusted Issuer (NEW - recommended for OAuth apps)
199
+ <TrainlyProvider appId="app_v1_12345" /> // Register via console API first
200
+
201
+ // Mode 2: App Secret (legacy - for multi-user apps)
115
202
  <TrainlyProvider appSecret="as_secret_123" />
116
203
 
117
- // Mode 2: With user context
204
+ // Mode 3: With user context (legacy)
118
205
  <TrainlyProvider
119
206
  appSecret="as_secret_123"
120
207
  userId="user_123"
121
208
  userEmail="user@example.com"
122
209
  />
123
210
 
124
- // Mode 3: Direct API key (simple apps)
211
+ // Mode 4: Direct API key (legacy - simple apps)
125
212
  <TrainlyProvider apiKey="tk_chat_id_key" />
126
213
  ```
127
214
 
215
+ ### V1 OAuth Provider Examples
216
+
217
+ ```tsx
218
+ // With Clerk
219
+ <TrainlyProvider
220
+ appId="app_v1_clerk_123"
221
+ baseUrl="https://api.trainly.com"
222
+ />
223
+
224
+ // With Auth0
225
+ <TrainlyProvider
226
+ appId="app_v1_auth0_456"
227
+ baseUrl="https://api.trainly.com"
228
+ />
229
+
230
+ // With AWS Cognito
231
+ <TrainlyProvider
232
+ appId="app_v1_cognito_789"
233
+ baseUrl="https://api.trainly.com"
234
+ />
235
+ ```
236
+
128
237
  ### Component Customization
129
238
 
130
239
  ```tsx
@@ -177,6 +286,9 @@ const {
177
286
  askWithCitations: (question: string) => Promise<{answer: string, citations: Citation[]}>,
178
287
  upload: (file: File) => Promise<UploadResult>,
179
288
 
289
+ // NEW: V1 Authentication
290
+ connectWithOAuthToken: (idToken: string) => Promise<void>,
291
+
180
292
  // State
181
293
  isLoading: boolean,
182
294
  isConnected: boolean,
@@ -198,22 +310,31 @@ const {
198
310
  ```tsx
199
311
  interface TrainlyProviderProps {
200
312
  children: React.ReactNode;
201
- appSecret?: string; // App secret from Trainly dashboard
202
- apiKey?: string; // Direct API key (alternative to appSecret)
313
+ appId?: string; // NEW: V1 app ID from console registration
314
+ appSecret?: string; // Legacy: App secret from Trainly dashboard
315
+ apiKey?: string; // Legacy: Direct API key (alternative to appSecret)
203
316
  baseUrl?: string; // Custom API URL (defaults to trainly.com)
204
- userId?: string; // Your app's user ID
205
- userEmail?: string; // Your app's user email
317
+ userId?: string; // Legacy: Your app's user ID
318
+ userEmail?: string; // Legacy: Your app's user email
206
319
  }
207
320
  ```
208
321
 
209
322
  ## 🔍 Examples
210
323
 
211
- Check out the `/examples` folder for complete implementations:
324
+ See complete implementation examples in the [API Documentation](https://trainly.com/docs/v1-authentication).
325
+
326
+ ## 🆚 **V1 vs Legacy Comparison**
327
+
328
+ | Feature | V1 Trusted Issuer | Legacy App Secret |
329
+ | -------------- | -------------------------------- | ------------------------- |
330
+ | **User Auth** | Your OAuth provider | Trainly OAuth flow |
331
+ | **User Data** | Permanent private subchat | Temporary or shared |
332
+ | **Privacy** | Complete (dev can't see files) | Limited |
333
+ | **Setup** | Register once, use OAuth tokens | Generate app secrets |
334
+ | **Migration** | Zero (uses existing OAuth) | Requires auth integration |
335
+ | **Permanence** | Same user = same subchat forever | Depends on implementation |
212
336
 
213
- - **Simple Chat App** - Drop-in components
214
- - **Custom Implementation** - Build your own UI
215
- - **Multi-user App** - User-specific workspaces
216
- - **File-focused App** - Document analysis focus
337
+ **Recommendation**: Use V1 for new apps and consider migrating existing apps for better privacy and user experience.
217
338
 
218
339
  ## 🛠️ Development
219
340
 
@@ -4,9 +4,11 @@ export interface TrainlyProviderProps {
4
4
  children: React.ReactNode;
5
5
  appSecret?: string;
6
6
  apiKey?: string;
7
+ appId?: string;
7
8
  baseUrl?: string;
8
9
  userId?: string;
9
10
  userEmail?: string;
10
11
  }
11
- export declare function TrainlyProvider({ children, appSecret, apiKey, baseUrl, userId, userEmail, }: TrainlyProviderProps): import("react/jsx-runtime").JSX.Element;
12
+ export declare function TrainlyProvider({ children, appSecret, apiKey, appId, // NEW: For V1 authentication
13
+ baseUrl, userId, userEmail, }: TrainlyProviderProps): import("react/jsx-runtime").JSX.Element;
12
14
  export declare function useTrainlyContext(): TrainlyContextValue;
@@ -50,11 +50,14 @@ import { TrainlyClient } from "./api/TrainlyClient";
50
50
  var TrainlyContext = React.createContext(undefined);
51
51
  export function TrainlyProvider(_a) {
52
52
  var _this = this;
53
- var children = _a.children, appSecret = _a.appSecret, apiKey = _a.apiKey, _b = _a.baseUrl, baseUrl = _b === void 0 ? "http://localhost:8000" : _b, userId = _a.userId, userEmail = _a.userEmail;
53
+ var children = _a.children, appSecret = _a.appSecret, apiKey = _a.apiKey, appId = _a.appId, // NEW: For V1 authentication
54
+ _b = _a.baseUrl, // NEW: For V1 authentication
55
+ baseUrl = _b === void 0 ? "http://localhost:8000" : _b, userId = _a.userId, userEmail = _a.userEmail;
54
56
  var client = React.useState(function () {
55
57
  return new TrainlyClient({
56
58
  appSecret: appSecret,
57
59
  apiKey: apiKey,
60
+ appId: appId, // NEW: Pass appId to client
58
61
  baseUrl: baseUrl,
59
62
  userId: userId,
60
63
  userEmail: userEmail,
@@ -97,8 +100,38 @@ export function TrainlyProvider(_a) {
97
100
  }
98
101
  });
99
102
  }); };
103
+ // NEW: V1 OAuth Token connection method
104
+ var connectWithOAuthToken = function (idToken) { return __awaiter(_this, void 0, void 0, function () {
105
+ var err_2;
106
+ return __generator(this, function (_a) {
107
+ switch (_a.label) {
108
+ case 0:
109
+ _a.trys.push([0, 2, 3, 4]);
110
+ setIsLoading(true);
111
+ setError(null);
112
+ return [4 /*yield*/, client.connectWithOAuthToken(idToken)];
113
+ case 1:
114
+ _a.sent();
115
+ setIsConnected(true);
116
+ return [3 /*break*/, 4];
117
+ case 2:
118
+ err_2 = _a.sent();
119
+ setError({
120
+ code: "V1_CONNECTION_FAILED",
121
+ message: "Failed to connect with OAuth token",
122
+ details: err_2,
123
+ });
124
+ setIsConnected(false);
125
+ throw err_2;
126
+ case 3:
127
+ setIsLoading(false);
128
+ return [7 /*endfinally*/];
129
+ case 4: return [2 /*return*/];
130
+ }
131
+ });
132
+ }); };
100
133
  var ask = function (question) { return __awaiter(_this, void 0, void 0, function () {
101
- var response, err_2, error_1;
134
+ var response, err_3, error_1;
102
135
  return __generator(this, function (_a) {
103
136
  switch (_a.label) {
104
137
  case 0:
@@ -110,11 +143,11 @@ export function TrainlyProvider(_a) {
110
143
  response = _a.sent();
111
144
  return [2 /*return*/, response.answer];
112
145
  case 2:
113
- err_2 = _a.sent();
146
+ err_3 = _a.sent();
114
147
  error_1 = {
115
148
  code: "QUERY_FAILED",
116
149
  message: "Failed to get answer",
117
- details: err_2,
150
+ details: err_3,
118
151
  };
119
152
  setError(error_1);
120
153
  throw error_1;
@@ -126,7 +159,7 @@ export function TrainlyProvider(_a) {
126
159
  });
127
160
  }); };
128
161
  var askWithCitations = function (question) { return __awaiter(_this, void 0, void 0, function () {
129
- var response, err_3, error_2;
162
+ var response, err_4, error_2;
130
163
  return __generator(this, function (_a) {
131
164
  switch (_a.label) {
132
165
  case 0:
@@ -141,11 +174,11 @@ export function TrainlyProvider(_a) {
141
174
  citations: response.citations || [],
142
175
  }];
143
176
  case 2:
144
- err_3 = _a.sent();
177
+ err_4 = _a.sent();
145
178
  error_2 = {
146
179
  code: "QUERY_FAILED",
147
180
  message: "Failed to get answer with citations",
148
- details: err_3,
181
+ details: err_4,
149
182
  };
150
183
  setError(error_2);
151
184
  throw error_2;
@@ -157,7 +190,7 @@ export function TrainlyProvider(_a) {
157
190
  });
158
191
  }); };
159
192
  var upload = function (file) { return __awaiter(_this, void 0, void 0, function () {
160
- var result, err_4, error_3;
193
+ var result, err_5, error_3;
161
194
  return __generator(this, function (_a) {
162
195
  switch (_a.label) {
163
196
  case 0:
@@ -169,11 +202,11 @@ export function TrainlyProvider(_a) {
169
202
  result = _a.sent();
170
203
  return [2 /*return*/, result];
171
204
  case 2:
172
- err_4 = _a.sent();
205
+ err_5 = _a.sent();
173
206
  error_3 = {
174
207
  code: "UPLOAD_FAILED",
175
208
  message: "Failed to upload file",
176
- details: err_4,
209
+ details: err_5,
177
210
  };
178
211
  setError(error_3);
179
212
  throw error_3;
@@ -185,7 +218,7 @@ export function TrainlyProvider(_a) {
185
218
  });
186
219
  }); };
187
220
  var sendMessage = function (content) { return __awaiter(_this, void 0, void 0, function () {
188
- var userMessage, response, assistantMessage_1, err_5;
221
+ var userMessage, response, assistantMessage_1, err_6;
189
222
  return __generator(this, function (_a) {
190
223
  switch (_a.label) {
191
224
  case 0:
@@ -212,9 +245,9 @@ export function TrainlyProvider(_a) {
212
245
  setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
213
246
  return [3 /*break*/, 4];
214
247
  case 3:
215
- err_5 = _a.sent();
248
+ err_6 = _a.sent();
216
249
  // Error is already set by askWithCitations
217
- console.error("Failed to send message:", err_5);
250
+ console.error("Failed to send message:", err_6);
218
251
  return [3 /*break*/, 4];
219
252
  case 4: return [2 /*return*/];
220
253
  }
@@ -227,6 +260,7 @@ export function TrainlyProvider(_a) {
227
260
  ask: ask,
228
261
  askWithCitations: askWithCitations,
229
262
  upload: upload,
263
+ connectWithOAuthToken: connectWithOAuthToken, // NEW: V1 OAuth connection method
230
264
  isLoading: isLoading,
231
265
  isConnected: isConnected,
232
266
  error: error,
@@ -7,7 +7,13 @@ export declare class TrainlyClient {
7
7
  private config;
8
8
  private scopedToken;
9
9
  private currentUserId;
10
+ private isV1Mode;
10
11
  constructor(config: TrainlyConfig);
12
+ /**
13
+ * NEW: Connect using V1 Trusted Issuer authentication with OAuth ID token
14
+ * This method allows users to authenticate directly with their OAuth provider tokens
15
+ */
16
+ connectWithOAuthToken(idToken: string): Promise<void>;
11
17
  connect(): Promise<void>;
12
18
  ask(question: string, options?: {
13
19
  includeCitations?: boolean;
@@ -49,8 +49,51 @@ var TrainlyClient = /** @class */ (function () {
49
49
  function TrainlyClient(config) {
50
50
  this.scopedToken = null;
51
51
  this.currentUserId = null;
52
+ this.isV1Mode = false;
52
53
  this.config = config;
53
54
  }
55
+ /**
56
+ * NEW: Connect using V1 Trusted Issuer authentication with OAuth ID token
57
+ * This method allows users to authenticate directly with their OAuth provider tokens
58
+ */
59
+ TrainlyClient.prototype.connectWithOAuthToken = function (idToken) {
60
+ return __awaiter(this, void 0, void 0, function () {
61
+ var response, error, profile;
62
+ return __generator(this, function (_a) {
63
+ switch (_a.label) {
64
+ case 0:
65
+ if (!this.config.appId) {
66
+ throw new Error("appId is required for V1 authentication.");
67
+ }
68
+ // For V1, we use the ID token directly - no need to provision
69
+ this.scopedToken = idToken;
70
+ this.isV1Mode = true;
71
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/profile"), {
72
+ headers: {
73
+ Authorization: "Bearer ".concat(idToken),
74
+ "X-App-ID": this.config.appId,
75
+ },
76
+ })];
77
+ case 1:
78
+ response = _a.sent();
79
+ if (!!response.ok) return [3 /*break*/, 3];
80
+ return [4 /*yield*/, response.json()];
81
+ case 2:
82
+ error = _a.sent();
83
+ throw new Error("V1 authentication failed: ".concat(error.detail || response.statusText));
84
+ case 3: return [4 /*yield*/, response.json()];
85
+ case 4:
86
+ profile = _a.sent();
87
+ this.currentUserId = profile.user_id;
88
+ console.log("✅ Connected to Trainly with V1 Trusted Issuer authentication");
89
+ console.log("\uD83D\uDCCB User ID: ".concat(profile.user_id));
90
+ console.log("\uD83D\uDCAC Chat ID: ".concat(profile.chat_id));
91
+ console.log("\uD83D\uDD12 OAuth Provider: ".concat(profile.issuer));
92
+ return [2 /*return*/];
93
+ }
94
+ });
95
+ });
96
+ };
54
97
  TrainlyClient.prototype.connect = function () {
55
98
  return __awaiter(this, void 0, void 0, function () {
56
99
  var response, error, data;
@@ -99,14 +142,42 @@ var TrainlyClient = /** @class */ (function () {
99
142
  };
100
143
  TrainlyClient.prototype.ask = function (question_1) {
101
144
  return __awaiter(this, arguments, void 0, function (question, options) {
102
- var url, headers, body, response, error, data;
145
+ var response_1, error, data_1, url, headers, body, response, error, data;
103
146
  if (options === void 0) { options = {}; }
104
147
  return __generator(this, function (_a) {
105
148
  switch (_a.label) {
106
149
  case 0:
107
150
  if (!this.scopedToken) {
108
- throw new Error("Not connected. Call connect() first.");
151
+ throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
109
152
  }
153
+ if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
154
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/query"), {
155
+ method: "POST",
156
+ headers: {
157
+ Authorization: "Bearer ".concat(this.scopedToken),
158
+ "X-App-ID": this.config.appId,
159
+ "Content-Type": "application/x-www-form-urlencoded",
160
+ },
161
+ body: new URLSearchParams({
162
+ messages: JSON.stringify([{ role: "user", content: question }]),
163
+ response_tokens: "150",
164
+ }),
165
+ })];
166
+ case 1:
167
+ response_1 = _a.sent();
168
+ if (!!response_1.ok) return [3 /*break*/, 3];
169
+ return [4 /*yield*/, response_1.json()];
170
+ case 2:
171
+ error = _a.sent();
172
+ throw new Error("V1 query failed: ".concat(error.detail || response_1.statusText));
173
+ case 3: return [4 /*yield*/, response_1.json()];
174
+ case 4:
175
+ data_1 = _a.sent();
176
+ return [2 /*return*/, {
177
+ answer: data_1.answer,
178
+ citations: data_1.citations || [],
179
+ }];
180
+ case 5:
110
181
  url = this.config.apiKey
111
182
  ? "".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/answer_question")
112
183
  : "".concat(this.config.baseUrl, "/v1/privacy/query");
@@ -129,15 +200,15 @@ var TrainlyClient = /** @class */ (function () {
129
200
  headers: headers,
130
201
  body: JSON.stringify(body),
131
202
  })];
132
- case 1:
203
+ case 6:
133
204
  response = _a.sent();
134
- if (!!response.ok) return [3 /*break*/, 3];
205
+ if (!!response.ok) return [3 /*break*/, 8];
135
206
  return [4 /*yield*/, response.json()];
136
- case 2:
207
+ case 7:
137
208
  error = _a.sent();
138
209
  throw new Error("Query failed: ".concat(error.detail || response.statusText));
139
- case 3: return [4 /*yield*/, response.json()];
140
- case 4:
210
+ case 8: return [4 /*yield*/, response.json()];
211
+ case 9:
141
212
  data = _a.sent();
142
213
  return [2 /*return*/, {
143
214
  answer: data.answer,
@@ -149,20 +220,21 @@ var TrainlyClient = /** @class */ (function () {
149
220
  };
150
221
  TrainlyClient.prototype.upload = function (file) {
151
222
  return __awaiter(this, void 0, void 0, function () {
152
- var formData, response, error, presignedResponse, error, _a, upload_url, upload_headers, formData, uploadResponse;
223
+ var formData, response, error, data, formData, response, error, presignedResponse, error, _a, upload_url, upload_headers, formData, uploadResponse;
153
224
  return __generator(this, function (_b) {
154
225
  switch (_b.label) {
155
226
  case 0:
156
227
  if (!this.scopedToken) {
157
- throw new Error("Not connected. Call connect() first.");
228
+ throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
158
229
  }
159
- if (!this.config.apiKey) return [3 /*break*/, 4];
230
+ if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
160
231
  formData = new FormData();
161
232
  formData.append("file", file);
162
- return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/upload_file"), {
233
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/files/upload"), {
163
234
  method: "POST",
164
235
  headers: {
165
- Authorization: "Bearer ".concat(this.config.apiKey),
236
+ Authorization: "Bearer ".concat(this.scopedToken),
237
+ "X-App-ID": this.config.appId,
166
238
  },
167
239
  body: formData,
168
240
  })];
@@ -171,15 +243,42 @@ var TrainlyClient = /** @class */ (function () {
171
243
  if (!!response.ok) return [3 /*break*/, 3];
172
244
  return [4 /*yield*/, response.json()];
173
245
  case 2:
246
+ error = _b.sent();
247
+ throw new Error("V1 upload failed: ".concat(error.detail || response.statusText));
248
+ case 3: return [4 /*yield*/, response.json()];
249
+ case 4:
250
+ data = _b.sent();
251
+ return [2 /*return*/, {
252
+ success: data.success,
253
+ filename: data.filename,
254
+ size: data.size_bytes,
255
+ message: data.message || "File uploaded to your permanent private subchat",
256
+ }];
257
+ case 5:
258
+ if (!this.config.apiKey) return [3 /*break*/, 9];
259
+ formData = new FormData();
260
+ formData.append("file", file);
261
+ return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/upload_file"), {
262
+ method: "POST",
263
+ headers: {
264
+ Authorization: "Bearer ".concat(this.config.apiKey),
265
+ },
266
+ body: formData,
267
+ })];
268
+ case 6:
269
+ response = _b.sent();
270
+ if (!!response.ok) return [3 /*break*/, 8];
271
+ return [4 /*yield*/, response.json()];
272
+ case 7:
174
273
  error = _b.sent();
175
274
  throw new Error("Upload failed: ".concat(error.detail || response.statusText));
176
- case 3: return [2 /*return*/, {
275
+ case 8: return [2 /*return*/, {
177
276
  success: true,
178
277
  filename: file.name,
179
278
  size: file.size,
180
279
  message: "File uploaded successfully",
181
280
  }];
182
- case 4: return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/privacy/upload/presigned-url"), {
281
+ case 9: return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/privacy/upload/presigned-url"), {
183
282
  method: "POST",
184
283
  headers: {
185
284
  "Content-Type": "application/json",
@@ -193,15 +292,15 @@ var TrainlyClient = /** @class */ (function () {
193
292
  file_type: file.type,
194
293
  }),
195
294
  })];
196
- case 5:
295
+ case 10:
197
296
  presignedResponse = _b.sent();
198
- if (!!presignedResponse.ok) return [3 /*break*/, 7];
297
+ if (!!presignedResponse.ok) return [3 /*break*/, 12];
199
298
  return [4 /*yield*/, presignedResponse.json()];
200
- case 6:
299
+ case 11:
201
300
  error = _b.sent();
202
301
  throw new Error("Failed to get upload URL: ".concat(error.detail || presignedResponse.statusText));
203
- case 7: return [4 /*yield*/, presignedResponse.json()];
204
- case 8:
302
+ case 12: return [4 /*yield*/, presignedResponse.json()];
303
+ case 13:
205
304
  _a = _b.sent(), upload_url = _a.upload_url, upload_headers = _a.upload_headers;
206
305
  formData = new FormData();
207
306
  formData.append("file", file);
@@ -210,7 +309,7 @@ var TrainlyClient = /** @class */ (function () {
210
309
  body: formData,
211
310
  headers: __assign({}, upload_headers),
212
311
  })];
213
- case 9:
312
+ case 14:
214
313
  uploadResponse = _b.sent();
215
314
  if (!uploadResponse.ok) {
216
315
  throw new Error("Failed to upload file");
@@ -87,7 +87,7 @@ export function TrainlyChat(_a) {
87
87
  var file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
88
88
  if (file) {
89
89
  // This would trigger upload and add a system message
90
- console.log("File selected:", file.name);
90
+ // File selected for upload
91
91
  }
92
92
  };
93
93
  var baseClasses = "\n flex flex-col border border-gray-200 rounded-lg overflow-hidden\n ".concat(theme === "dark" ? "bg-gray-900 text-white border-gray-700" : "bg-white text-gray-900", "\n ").concat(className, "\n ");
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface TrainlyConfig {
2
2
  appSecret?: string;
3
3
  apiKey?: string;
4
+ appId?: string;
4
5
  baseUrl?: string;
5
6
  userId?: string;
6
7
  userEmail?: string;
@@ -9,6 +10,7 @@ export interface TrainlyProviderProps {
9
10
  children: React.ReactNode;
10
11
  appSecret?: string;
11
12
  apiKey?: string;
13
+ appId?: string;
12
14
  baseUrl?: string;
13
15
  userId?: string;
14
16
  userEmail?: string;
@@ -44,6 +46,7 @@ export interface TrainlyContextValue {
44
46
  citations: Citation[];
45
47
  }>;
46
48
  upload: (file: File) => Promise<UploadResult>;
49
+ connectWithOAuthToken: (idToken: string) => Promise<void>;
47
50
  isLoading: boolean;
48
51
  isConnected: boolean;
49
52
  error: TrainlyError | null;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * const handleQuestion = async () => {
10
10
  * const answer = await ask("What is photosynthesis?");
11
- * console.log(answer);
11
+ * // Handle the answer response
12
12
  * };
13
13
  *
14
14
  * return <button onClick={handleQuestion}>Ask AI</button>;
@@ -9,7 +9,7 @@ import { useTrainlyContext } from "./TrainlyProvider";
9
9
  *
10
10
  * const handleQuestion = async () => {
11
11
  * const answer = await ask("What is photosynthesis?");
12
- * console.log(answer);
12
+ * // Handle the answer response
13
13
  * };
14
14
  *
15
15
  * return <button onClick={handleQuestion}>Ask AI</button>;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@trainly/react",
3
- "version": "1.0.3",
4
- "description": "Dead simple RAG integration for React apps",
3
+ "version": "1.1.0",
4
+ "description": "Dead simple RAG integration for React apps with OAuth authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
@@ -18,6 +18,11 @@
18
18
  "documents",
19
19
  "react",
20
20
  "trainly",
21
+ "oauth",
22
+ "authentication",
23
+ "privacy",
24
+ "clerk",
25
+ "auth0",
21
26
  "retrieval-augmented-generation",
22
27
  "semantic-search",
23
28
  "document-chat"