@trainly/react 1.0.2 → 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 +136 -15
- package/dist/TrainlyProvider.d.ts +3 -1
- package/dist/TrainlyProvider.js +48 -13
- package/dist/api/TrainlyClient.d.ts +6 -0
- package/dist/api/TrainlyClient.js +143 -33
- package/dist/components/TrainlyChat.js +2 -1
- package/dist/components/TrainlyStatus.js +1 -0
- package/dist/components/TrainlyUpload.js +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/useTrainly.d.ts +1 -1
- package/dist/useTrainly.js +1 -1
- package/package.json +7 -2
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.
|
|
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
|
-
##
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
202
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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;
|
package/dist/TrainlyProvider.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -49,11 +50,14 @@ import { TrainlyClient } from "./api/TrainlyClient";
|
|
|
49
50
|
var TrainlyContext = React.createContext(undefined);
|
|
50
51
|
export function TrainlyProvider(_a) {
|
|
51
52
|
var _this = this;
|
|
52
|
-
var children = _a.children, appSecret = _a.appSecret, apiKey = _a.apiKey,
|
|
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;
|
|
53
56
|
var client = React.useState(function () {
|
|
54
57
|
return new TrainlyClient({
|
|
55
58
|
appSecret: appSecret,
|
|
56
59
|
apiKey: apiKey,
|
|
60
|
+
appId: appId, // NEW: Pass appId to client
|
|
57
61
|
baseUrl: baseUrl,
|
|
58
62
|
userId: userId,
|
|
59
63
|
userEmail: userEmail,
|
|
@@ -96,8 +100,38 @@ export function TrainlyProvider(_a) {
|
|
|
96
100
|
}
|
|
97
101
|
});
|
|
98
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
|
+
}); };
|
|
99
133
|
var ask = function (question) { return __awaiter(_this, void 0, void 0, function () {
|
|
100
|
-
var response,
|
|
134
|
+
var response, err_3, error_1;
|
|
101
135
|
return __generator(this, function (_a) {
|
|
102
136
|
switch (_a.label) {
|
|
103
137
|
case 0:
|
|
@@ -109,11 +143,11 @@ export function TrainlyProvider(_a) {
|
|
|
109
143
|
response = _a.sent();
|
|
110
144
|
return [2 /*return*/, response.answer];
|
|
111
145
|
case 2:
|
|
112
|
-
|
|
146
|
+
err_3 = _a.sent();
|
|
113
147
|
error_1 = {
|
|
114
148
|
code: "QUERY_FAILED",
|
|
115
149
|
message: "Failed to get answer",
|
|
116
|
-
details:
|
|
150
|
+
details: err_3,
|
|
117
151
|
};
|
|
118
152
|
setError(error_1);
|
|
119
153
|
throw error_1;
|
|
@@ -125,7 +159,7 @@ export function TrainlyProvider(_a) {
|
|
|
125
159
|
});
|
|
126
160
|
}); };
|
|
127
161
|
var askWithCitations = function (question) { return __awaiter(_this, void 0, void 0, function () {
|
|
128
|
-
var response,
|
|
162
|
+
var response, err_4, error_2;
|
|
129
163
|
return __generator(this, function (_a) {
|
|
130
164
|
switch (_a.label) {
|
|
131
165
|
case 0:
|
|
@@ -140,11 +174,11 @@ export function TrainlyProvider(_a) {
|
|
|
140
174
|
citations: response.citations || [],
|
|
141
175
|
}];
|
|
142
176
|
case 2:
|
|
143
|
-
|
|
177
|
+
err_4 = _a.sent();
|
|
144
178
|
error_2 = {
|
|
145
179
|
code: "QUERY_FAILED",
|
|
146
180
|
message: "Failed to get answer with citations",
|
|
147
|
-
details:
|
|
181
|
+
details: err_4,
|
|
148
182
|
};
|
|
149
183
|
setError(error_2);
|
|
150
184
|
throw error_2;
|
|
@@ -156,7 +190,7 @@ export function TrainlyProvider(_a) {
|
|
|
156
190
|
});
|
|
157
191
|
}); };
|
|
158
192
|
var upload = function (file) { return __awaiter(_this, void 0, void 0, function () {
|
|
159
|
-
var result,
|
|
193
|
+
var result, err_5, error_3;
|
|
160
194
|
return __generator(this, function (_a) {
|
|
161
195
|
switch (_a.label) {
|
|
162
196
|
case 0:
|
|
@@ -168,11 +202,11 @@ export function TrainlyProvider(_a) {
|
|
|
168
202
|
result = _a.sent();
|
|
169
203
|
return [2 /*return*/, result];
|
|
170
204
|
case 2:
|
|
171
|
-
|
|
205
|
+
err_5 = _a.sent();
|
|
172
206
|
error_3 = {
|
|
173
207
|
code: "UPLOAD_FAILED",
|
|
174
208
|
message: "Failed to upload file",
|
|
175
|
-
details:
|
|
209
|
+
details: err_5,
|
|
176
210
|
};
|
|
177
211
|
setError(error_3);
|
|
178
212
|
throw error_3;
|
|
@@ -184,7 +218,7 @@ export function TrainlyProvider(_a) {
|
|
|
184
218
|
});
|
|
185
219
|
}); };
|
|
186
220
|
var sendMessage = function (content) { return __awaiter(_this, void 0, void 0, function () {
|
|
187
|
-
var userMessage, response, assistantMessage_1,
|
|
221
|
+
var userMessage, response, assistantMessage_1, err_6;
|
|
188
222
|
return __generator(this, function (_a) {
|
|
189
223
|
switch (_a.label) {
|
|
190
224
|
case 0:
|
|
@@ -211,9 +245,9 @@ export function TrainlyProvider(_a) {
|
|
|
211
245
|
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantMessage_1], false); });
|
|
212
246
|
return [3 /*break*/, 4];
|
|
213
247
|
case 3:
|
|
214
|
-
|
|
248
|
+
err_6 = _a.sent();
|
|
215
249
|
// Error is already set by askWithCitations
|
|
216
|
-
console.error("Failed to send message:",
|
|
250
|
+
console.error("Failed to send message:", err_6);
|
|
217
251
|
return [3 /*break*/, 4];
|
|
218
252
|
case 4: return [2 /*return*/];
|
|
219
253
|
}
|
|
@@ -226,6 +260,7 @@ export function TrainlyProvider(_a) {
|
|
|
226
260
|
ask: ask,
|
|
227
261
|
askWithCitations: askWithCitations,
|
|
228
262
|
upload: upload,
|
|
263
|
+
connectWithOAuthToken: connectWithOAuthToken, // NEW: V1 OAuth connection method
|
|
229
264
|
isLoading: isLoading,
|
|
230
265
|
isConnected: isConnected,
|
|
231
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;
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
1
12
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
13
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
14
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -38,8 +49,51 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
38
49
|
function TrainlyClient(config) {
|
|
39
50
|
this.scopedToken = null;
|
|
40
51
|
this.currentUserId = null;
|
|
52
|
+
this.isV1Mode = false;
|
|
41
53
|
this.config = config;
|
|
42
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
|
+
};
|
|
43
97
|
TrainlyClient.prototype.connect = function () {
|
|
44
98
|
return __awaiter(this, void 0, void 0, function () {
|
|
45
99
|
var response, error, data;
|
|
@@ -88,14 +142,42 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
88
142
|
};
|
|
89
143
|
TrainlyClient.prototype.ask = function (question_1) {
|
|
90
144
|
return __awaiter(this, arguments, void 0, function (question, options) {
|
|
91
|
-
var url, headers, body, response, error, data;
|
|
145
|
+
var response_1, error, data_1, url, headers, body, response, error, data;
|
|
92
146
|
if (options === void 0) { options = {}; }
|
|
93
147
|
return __generator(this, function (_a) {
|
|
94
148
|
switch (_a.label) {
|
|
95
149
|
case 0:
|
|
96
150
|
if (!this.scopedToken) {
|
|
97
|
-
throw new Error("Not connected. Call connect() first.");
|
|
151
|
+
throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
|
|
98
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:
|
|
99
181
|
url = this.config.apiKey
|
|
100
182
|
? "".concat(this.config.baseUrl, "/v1/").concat(this.extractChatId(), "/answer_question")
|
|
101
183
|
: "".concat(this.config.baseUrl, "/v1/privacy/query");
|
|
@@ -118,15 +200,15 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
118
200
|
headers: headers,
|
|
119
201
|
body: JSON.stringify(body),
|
|
120
202
|
})];
|
|
121
|
-
case
|
|
203
|
+
case 6:
|
|
122
204
|
response = _a.sent();
|
|
123
|
-
if (!!response.ok) return [3 /*break*/,
|
|
205
|
+
if (!!response.ok) return [3 /*break*/, 8];
|
|
124
206
|
return [4 /*yield*/, response.json()];
|
|
125
|
-
case
|
|
207
|
+
case 7:
|
|
126
208
|
error = _a.sent();
|
|
127
209
|
throw new Error("Query failed: ".concat(error.detail || response.statusText));
|
|
128
|
-
case
|
|
129
|
-
case
|
|
210
|
+
case 8: return [4 /*yield*/, response.json()];
|
|
211
|
+
case 9:
|
|
130
212
|
data = _a.sent();
|
|
131
213
|
return [2 /*return*/, {
|
|
132
214
|
answer: data.answer,
|
|
@@ -138,37 +220,65 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
138
220
|
};
|
|
139
221
|
TrainlyClient.prototype.upload = function (file) {
|
|
140
222
|
return __awaiter(this, void 0, void 0, function () {
|
|
141
|
-
var formData, response, error, presignedResponse, error, upload_url, uploadResponse;
|
|
142
|
-
return __generator(this, function (
|
|
143
|
-
switch (
|
|
223
|
+
var formData, response, error, data, formData, response, error, presignedResponse, error, _a, upload_url, upload_headers, formData, uploadResponse;
|
|
224
|
+
return __generator(this, function (_b) {
|
|
225
|
+
switch (_b.label) {
|
|
144
226
|
case 0:
|
|
145
227
|
if (!this.scopedToken) {
|
|
146
|
-
throw new Error("Not connected. Call connect() first.");
|
|
228
|
+
throw new Error("Not connected. Call connect() or connectWithOAuthToken() first.");
|
|
147
229
|
}
|
|
148
|
-
if (!this.config.
|
|
230
|
+
if (!(this.isV1Mode && this.config.appId)) return [3 /*break*/, 5];
|
|
149
231
|
formData = new FormData();
|
|
150
232
|
formData.append("file", file);
|
|
151
|
-
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/
|
|
233
|
+
return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/me/chats/files/upload"), {
|
|
152
234
|
method: "POST",
|
|
153
235
|
headers: {
|
|
154
|
-
Authorization: "Bearer ".concat(this.
|
|
236
|
+
Authorization: "Bearer ".concat(this.scopedToken),
|
|
237
|
+
"X-App-ID": this.config.appId,
|
|
155
238
|
},
|
|
156
239
|
body: formData,
|
|
157
240
|
})];
|
|
158
241
|
case 1:
|
|
159
|
-
response =
|
|
242
|
+
response = _b.sent();
|
|
160
243
|
if (!!response.ok) return [3 /*break*/, 3];
|
|
161
244
|
return [4 /*yield*/, response.json()];
|
|
162
245
|
case 2:
|
|
163
|
-
error =
|
|
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:
|
|
273
|
+
error = _b.sent();
|
|
164
274
|
throw new Error("Upload failed: ".concat(error.detail || response.statusText));
|
|
165
|
-
case
|
|
275
|
+
case 8: return [2 /*return*/, {
|
|
166
276
|
success: true,
|
|
167
277
|
filename: file.name,
|
|
168
278
|
size: file.size,
|
|
169
279
|
message: "File uploaded successfully",
|
|
170
280
|
}];
|
|
171
|
-
case
|
|
281
|
+
case 9: return [4 /*yield*/, fetch("".concat(this.config.baseUrl, "/v1/privacy/upload/presigned-url"), {
|
|
172
282
|
method: "POST",
|
|
173
283
|
headers: {
|
|
174
284
|
"Content-Type": "application/json",
|
|
@@ -182,25 +292,25 @@ var TrainlyClient = /** @class */ (function () {
|
|
|
182
292
|
file_type: file.type,
|
|
183
293
|
}),
|
|
184
294
|
})];
|
|
185
|
-
case
|
|
186
|
-
presignedResponse =
|
|
187
|
-
if (!!presignedResponse.ok) return [3 /*break*/,
|
|
295
|
+
case 10:
|
|
296
|
+
presignedResponse = _b.sent();
|
|
297
|
+
if (!!presignedResponse.ok) return [3 /*break*/, 12];
|
|
188
298
|
return [4 /*yield*/, presignedResponse.json()];
|
|
189
|
-
case
|
|
190
|
-
error =
|
|
299
|
+
case 11:
|
|
300
|
+
error = _b.sent();
|
|
191
301
|
throw new Error("Failed to get upload URL: ".concat(error.detail || presignedResponse.statusText));
|
|
192
|
-
case
|
|
193
|
-
case
|
|
194
|
-
|
|
302
|
+
case 12: return [4 /*yield*/, presignedResponse.json()];
|
|
303
|
+
case 13:
|
|
304
|
+
_a = _b.sent(), upload_url = _a.upload_url, upload_headers = _a.upload_headers;
|
|
305
|
+
formData = new FormData();
|
|
306
|
+
formData.append("file", file);
|
|
195
307
|
return [4 /*yield*/, fetch(upload_url, {
|
|
196
|
-
method: "
|
|
197
|
-
body:
|
|
198
|
-
headers: {
|
|
199
|
-
"Content-Type": file.type,
|
|
200
|
-
},
|
|
308
|
+
method: "POST",
|
|
309
|
+
body: formData,
|
|
310
|
+
headers: __assign({}, upload_headers),
|
|
201
311
|
})];
|
|
202
|
-
case
|
|
203
|
-
uploadResponse =
|
|
312
|
+
case 14:
|
|
313
|
+
uploadResponse = _b.sent();
|
|
204
314
|
if (!uploadResponse.ok) {
|
|
205
315
|
throw new Error("Failed to upload file");
|
|
206
316
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -86,7 +87,7 @@ export function TrainlyChat(_a) {
|
|
|
86
87
|
var file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
87
88
|
if (file) {
|
|
88
89
|
// This would trigger upload and add a system message
|
|
89
|
-
|
|
90
|
+
// File selected for upload
|
|
90
91
|
}
|
|
91
92
|
};
|
|
92
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 ");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
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;
|
package/dist/useTrainly.d.ts
CHANGED
package/dist/useTrainly.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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"
|