@realtimex/sdk 1.0.8 → 1.1.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 +155 -2
- package/dist/index.d.mts +336 -7
- package/dist/index.d.ts +336 -7
- package/dist/index.js +619 -62
- package/dist/index.mjs +613 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32,18 +32,144 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ActivitiesModule: () => ActivitiesModule,
|
|
34
34
|
ApiModule: () => ApiModule,
|
|
35
|
+
LLMModule: () => LLMModule,
|
|
36
|
+
LLMPermissionError: () => LLMPermissionError,
|
|
37
|
+
LLMProviderError: () => LLMProviderError,
|
|
38
|
+
PermissionDeniedError: () => PermissionDeniedError,
|
|
39
|
+
PermissionRequiredError: () => PermissionRequiredError,
|
|
35
40
|
PortModule: () => PortModule,
|
|
36
41
|
RealtimeXSDK: () => RealtimeXSDK,
|
|
37
42
|
TaskModule: () => TaskModule,
|
|
43
|
+
VectorStore: () => VectorStore,
|
|
38
44
|
WebhookModule: () => WebhookModule
|
|
39
45
|
});
|
|
40
46
|
module.exports = __toCommonJS(index_exports);
|
|
41
47
|
|
|
48
|
+
// src/modules/api.ts
|
|
49
|
+
var PermissionDeniedError = class extends Error {
|
|
50
|
+
constructor(permission, message) {
|
|
51
|
+
super(message || `Permission '${permission}' was denied`);
|
|
52
|
+
this.name = "PermissionDeniedError";
|
|
53
|
+
this.permission = permission;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var PermissionRequiredError = class extends Error {
|
|
57
|
+
constructor(permission, message) {
|
|
58
|
+
super(message || `Permission '${permission}' is required`);
|
|
59
|
+
this.name = "PermissionRequiredError";
|
|
60
|
+
this.permission = permission;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var ApiModule = class {
|
|
64
|
+
constructor(realtimexUrl, appId, appName) {
|
|
65
|
+
this.realtimexUrl = realtimexUrl.replace(/\/$/, "");
|
|
66
|
+
this.appId = appId;
|
|
67
|
+
this.appName = appName || process.env.RTX_APP_NAME || "Local App";
|
|
68
|
+
}
|
|
69
|
+
getHeaders() {
|
|
70
|
+
return {
|
|
71
|
+
"Content-Type": "application/json",
|
|
72
|
+
"x-app-id": this.appId
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Request a single permission from Electron via internal API
|
|
77
|
+
*/
|
|
78
|
+
async requestPermission(permission) {
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(`${this.realtimexUrl}/api/local-apps/request-permission`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "Content-Type": "application/json" },
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
app_id: this.appId,
|
|
85
|
+
app_name: this.appName,
|
|
86
|
+
permission
|
|
87
|
+
})
|
|
88
|
+
});
|
|
89
|
+
const data = await response.json();
|
|
90
|
+
return data.granted === true;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Make an API call with automatic permission handling
|
|
97
|
+
*/
|
|
98
|
+
async apiCall(method, endpoint, options) {
|
|
99
|
+
const url = `${this.realtimexUrl}${endpoint}`;
|
|
100
|
+
const response = await fetch(url, {
|
|
101
|
+
method,
|
|
102
|
+
headers: this.getHeaders(),
|
|
103
|
+
...options
|
|
104
|
+
});
|
|
105
|
+
const data = await response.json();
|
|
106
|
+
if (response.status === 403) {
|
|
107
|
+
const errorCode = data.error;
|
|
108
|
+
const permission = data.permission;
|
|
109
|
+
const message = data.message;
|
|
110
|
+
if (errorCode === "PERMISSION_REQUIRED" && permission) {
|
|
111
|
+
const granted = await this.requestPermission(permission);
|
|
112
|
+
if (granted) {
|
|
113
|
+
return this.apiCall(method, endpoint, options);
|
|
114
|
+
} else {
|
|
115
|
+
throw new PermissionDeniedError(permission, message);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (errorCode === "PERMISSION_DENIED") {
|
|
119
|
+
throw new PermissionDeniedError(permission, message);
|
|
120
|
+
}
|
|
121
|
+
throw new Error(data.error || "Permission denied");
|
|
122
|
+
}
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
throw new Error(data.error || `API call failed: ${response.status}`);
|
|
125
|
+
}
|
|
126
|
+
return data;
|
|
127
|
+
}
|
|
128
|
+
async getAgents() {
|
|
129
|
+
const data = await this.apiCall("GET", "/agents");
|
|
130
|
+
return data.agents;
|
|
131
|
+
}
|
|
132
|
+
async getWorkspaces() {
|
|
133
|
+
const data = await this.apiCall("GET", "/workspaces");
|
|
134
|
+
return data.workspaces;
|
|
135
|
+
}
|
|
136
|
+
async getThreads(workspaceSlug) {
|
|
137
|
+
const data = await this.apiCall("GET", `/workspaces/${encodeURIComponent(workspaceSlug)}/threads`);
|
|
138
|
+
return data.threads;
|
|
139
|
+
}
|
|
140
|
+
async getTask(taskUuid) {
|
|
141
|
+
const data = await this.apiCall("GET", `/task/${encodeURIComponent(taskUuid)}`);
|
|
142
|
+
return { ...data.task, runs: data.runs };
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
42
146
|
// src/modules/activities.ts
|
|
43
147
|
var ActivitiesModule = class {
|
|
44
|
-
constructor(realtimexUrl, appId) {
|
|
148
|
+
constructor(realtimexUrl, appId, appName) {
|
|
45
149
|
this.baseUrl = realtimexUrl.replace(/\/$/, "");
|
|
46
150
|
this.appId = appId;
|
|
151
|
+
this.appName = appName || process.env.RTX_APP_NAME || "Local App";
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Request a single permission from Electron via internal API
|
|
155
|
+
*/
|
|
156
|
+
async requestPermission(permission) {
|
|
157
|
+
try {
|
|
158
|
+
const response = await fetch(`${this.baseUrl}/api/local-apps/request-permission`, {
|
|
159
|
+
method: "POST",
|
|
160
|
+
headers: { "Content-Type": "application/json" },
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
app_id: this.appId,
|
|
163
|
+
app_name: this.appName,
|
|
164
|
+
permission
|
|
165
|
+
})
|
|
166
|
+
});
|
|
167
|
+
const data = await response.json();
|
|
168
|
+
return data.granted === true;
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error("[SDK] Permission request failed:", error);
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
47
173
|
}
|
|
48
174
|
async request(path, options = {}) {
|
|
49
175
|
const url = `${this.baseUrl}${path}`;
|
|
@@ -61,6 +187,22 @@ var ActivitiesModule = class {
|
|
|
61
187
|
}
|
|
62
188
|
});
|
|
63
189
|
const data = await response.json();
|
|
190
|
+
if (response.status === 403) {
|
|
191
|
+
const errorCode = data.error;
|
|
192
|
+
const permission = data.permission;
|
|
193
|
+
const message = data.message;
|
|
194
|
+
if (errorCode === "PERMISSION_REQUIRED" && permission) {
|
|
195
|
+
const granted = await this.requestPermission(permission);
|
|
196
|
+
if (granted) {
|
|
197
|
+
return this.request(path, options);
|
|
198
|
+
} else {
|
|
199
|
+
throw new PermissionDeniedError(permission, message);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (errorCode === "PERMISSION_DENIED") {
|
|
203
|
+
throw new PermissionDeniedError(permission, message);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
64
206
|
if (!response.ok) {
|
|
65
207
|
throw new Error(data.error || `Request failed: ${response.status}`);
|
|
66
208
|
}
|
|
@@ -127,13 +269,64 @@ var WebhookModule = class {
|
|
|
127
269
|
this.appName = appName;
|
|
128
270
|
this.appId = appId;
|
|
129
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Request a single permission from Electron via internal API
|
|
274
|
+
*/
|
|
275
|
+
async requestPermission(permission) {
|
|
276
|
+
try {
|
|
277
|
+
const response = await fetch(`${this.realtimexUrl}/api/local-apps/request-permission`, {
|
|
278
|
+
method: "POST",
|
|
279
|
+
headers: { "Content-Type": "application/json" },
|
|
280
|
+
body: JSON.stringify({
|
|
281
|
+
app_id: this.appId,
|
|
282
|
+
app_name: this.appName,
|
|
283
|
+
permission
|
|
284
|
+
})
|
|
285
|
+
});
|
|
286
|
+
const data = await response.json();
|
|
287
|
+
return data.granted === true;
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.error("[SDK] Permission request failed:", error);
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async request(path, options = {}) {
|
|
294
|
+
const url = `${this.realtimexUrl}${path}`;
|
|
295
|
+
const response = await fetch(url, {
|
|
296
|
+
...options,
|
|
297
|
+
headers: {
|
|
298
|
+
"Content-Type": "application/json",
|
|
299
|
+
...options.headers
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
const data = await response.json();
|
|
303
|
+
if (response.status === 403) {
|
|
304
|
+
const errorCode = data.error;
|
|
305
|
+
const permission = data.permission;
|
|
306
|
+
const message = data.message;
|
|
307
|
+
if (errorCode === "PERMISSION_REQUIRED" && permission) {
|
|
308
|
+
const granted = await this.requestPermission(permission);
|
|
309
|
+
if (granted) {
|
|
310
|
+
return this.request(path, options);
|
|
311
|
+
} else {
|
|
312
|
+
throw new PermissionDeniedError(permission, message);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (errorCode === "PERMISSION_DENIED") {
|
|
316
|
+
throw new PermissionDeniedError(permission, message);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (!response.ok) {
|
|
320
|
+
throw new Error(data.error || `Request failed: ${response.status}`);
|
|
321
|
+
}
|
|
322
|
+
return data;
|
|
323
|
+
}
|
|
130
324
|
async triggerAgent(payload) {
|
|
131
325
|
if (payload.auto_run && (!payload.agent_name || !payload.workspace_slug)) {
|
|
132
326
|
throw new Error("auto_run requires agent_name and workspace_slug");
|
|
133
327
|
}
|
|
134
|
-
|
|
328
|
+
return this.request("/webhooks/realtimex", {
|
|
135
329
|
method: "POST",
|
|
136
|
-
headers: { "Content-Type": "application/json" },
|
|
137
330
|
body: JSON.stringify({
|
|
138
331
|
app_name: this.appName,
|
|
139
332
|
app_id: this.appId,
|
|
@@ -148,69 +341,16 @@ var WebhookModule = class {
|
|
|
148
341
|
}
|
|
149
342
|
})
|
|
150
343
|
});
|
|
151
|
-
const data = await response.json();
|
|
152
|
-
if (!response.ok) throw new Error(data.error || "Failed to trigger agent");
|
|
153
|
-
return data;
|
|
154
344
|
}
|
|
155
345
|
async ping() {
|
|
156
|
-
|
|
346
|
+
return this.request("/webhooks/realtimex", {
|
|
157
347
|
method: "POST",
|
|
158
|
-
headers: { "Content-Type": "application/json" },
|
|
159
348
|
body: JSON.stringify({
|
|
160
349
|
app_name: this.appName,
|
|
161
350
|
app_id: this.appId,
|
|
162
351
|
event: "ping"
|
|
163
352
|
})
|
|
164
353
|
});
|
|
165
|
-
const data = await response.json();
|
|
166
|
-
if (!response.ok) throw new Error(data.error || "Ping failed");
|
|
167
|
-
return data;
|
|
168
|
-
}
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
// src/modules/api.ts
|
|
172
|
-
var ApiModule = class {
|
|
173
|
-
constructor(realtimexUrl, appId) {
|
|
174
|
-
this.realtimexUrl = realtimexUrl.replace(/\/$/, "");
|
|
175
|
-
this.appId = appId;
|
|
176
|
-
}
|
|
177
|
-
getHeaders() {
|
|
178
|
-
return {
|
|
179
|
-
"Content-Type": "application/json",
|
|
180
|
-
"x-app-id": this.appId
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
async getAgents() {
|
|
184
|
-
const response = await fetch(`${this.realtimexUrl}/agents`, {
|
|
185
|
-
headers: this.getHeaders()
|
|
186
|
-
});
|
|
187
|
-
const data = await response.json();
|
|
188
|
-
if (!response.ok) throw new Error(data.error || "Failed to get agents");
|
|
189
|
-
return data.agents;
|
|
190
|
-
}
|
|
191
|
-
async getWorkspaces() {
|
|
192
|
-
const response = await fetch(`${this.realtimexUrl}/workspaces`, {
|
|
193
|
-
headers: this.getHeaders()
|
|
194
|
-
});
|
|
195
|
-
const data = await response.json();
|
|
196
|
-
if (!response.ok) throw new Error(data.error || "Failed to get workspaces");
|
|
197
|
-
return data.workspaces;
|
|
198
|
-
}
|
|
199
|
-
async getThreads(workspaceSlug) {
|
|
200
|
-
const response = await fetch(`${this.realtimexUrl}/workspaces/${encodeURIComponent(workspaceSlug)}/threads`, {
|
|
201
|
-
headers: this.getHeaders()
|
|
202
|
-
});
|
|
203
|
-
const data = await response.json();
|
|
204
|
-
if (!response.ok) throw new Error(data.error || "Failed to get threads");
|
|
205
|
-
return data.threads;
|
|
206
|
-
}
|
|
207
|
-
async getTask(taskUuid) {
|
|
208
|
-
const response = await fetch(`${this.realtimexUrl}/task/${encodeURIComponent(taskUuid)}`, {
|
|
209
|
-
headers: this.getHeaders()
|
|
210
|
-
});
|
|
211
|
-
const data = await response.json();
|
|
212
|
-
if (!response.ok) throw new Error(data.error || "Failed to get task");
|
|
213
|
-
return { ...data.task, runs: data.runs };
|
|
214
354
|
}
|
|
215
355
|
};
|
|
216
356
|
|
|
@@ -336,6 +476,384 @@ var PortModule = class {
|
|
|
336
476
|
}
|
|
337
477
|
};
|
|
338
478
|
|
|
479
|
+
// src/modules/llm.ts
|
|
480
|
+
var LLMPermissionError = class extends Error {
|
|
481
|
+
constructor(permission, code = "PERMISSION_REQUIRED") {
|
|
482
|
+
super(`Permission required: ${permission}`);
|
|
483
|
+
this.permission = permission;
|
|
484
|
+
this.code = code;
|
|
485
|
+
this.name = "LLMPermissionError";
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
var LLMProviderError = class extends Error {
|
|
489
|
+
constructor(message, code = "LLM_ERROR") {
|
|
490
|
+
super(message);
|
|
491
|
+
this.code = code;
|
|
492
|
+
this.name = "LLMProviderError";
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
var VectorStore = class {
|
|
496
|
+
constructor(baseUrl, appId) {
|
|
497
|
+
this.baseUrl = baseUrl;
|
|
498
|
+
this.appId = appId;
|
|
499
|
+
}
|
|
500
|
+
get headers() {
|
|
501
|
+
return {
|
|
502
|
+
"Content-Type": "application/json",
|
|
503
|
+
"x-app-id": this.appId
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Upsert (insert or update) vectors into storage
|
|
508
|
+
*
|
|
509
|
+
* @example
|
|
510
|
+
* ```ts
|
|
511
|
+
* await sdk.llm.vectors.upsert([
|
|
512
|
+
* { id: 'chunk-1', vector: embeddings[0], metadata: { text: 'Hello', documentId: 'doc-1' } }
|
|
513
|
+
* ], { workspaceId: 'ws-123' });
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
async upsert(vectors, options = {}) {
|
|
517
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/upsert`, {
|
|
518
|
+
method: "POST",
|
|
519
|
+
headers: this.headers,
|
|
520
|
+
body: JSON.stringify({
|
|
521
|
+
vectors,
|
|
522
|
+
workspaceId: options.workspaceId
|
|
523
|
+
})
|
|
524
|
+
});
|
|
525
|
+
const data = await response.json();
|
|
526
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
527
|
+
throw new LLMPermissionError(data.permission || "vectors.write");
|
|
528
|
+
}
|
|
529
|
+
return data;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Query similar vectors by embedding
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```ts
|
|
536
|
+
* const results = await sdk.llm.vectors.query(queryVector, {
|
|
537
|
+
* topK: 5,
|
|
538
|
+
* filter: { documentId: 'doc-1' },
|
|
539
|
+
* workspaceId: 'ws-123'
|
|
540
|
+
* });
|
|
541
|
+
* ```
|
|
542
|
+
*/
|
|
543
|
+
async query(vector, options = {}) {
|
|
544
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/query`, {
|
|
545
|
+
method: "POST",
|
|
546
|
+
headers: this.headers,
|
|
547
|
+
body: JSON.stringify({
|
|
548
|
+
vector,
|
|
549
|
+
topK: options.topK ?? 5,
|
|
550
|
+
filter: options.filter,
|
|
551
|
+
workspaceId: options.workspaceId
|
|
552
|
+
})
|
|
553
|
+
});
|
|
554
|
+
const data = await response.json();
|
|
555
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
556
|
+
throw new LLMPermissionError(data.permission || "vectors.read");
|
|
557
|
+
}
|
|
558
|
+
return data;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Delete vectors from storage
|
|
562
|
+
*
|
|
563
|
+
* Note: Currently only supports deleteAll: true
|
|
564
|
+
* Use workspaceId to scope deletion to a specific workspace
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* ```ts
|
|
568
|
+
* await sdk.llm.vectors.delete({ deleteAll: true, workspaceId: 'ws-123' });
|
|
569
|
+
* ```
|
|
570
|
+
*/
|
|
571
|
+
async delete(options) {
|
|
572
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/delete`, {
|
|
573
|
+
method: "POST",
|
|
574
|
+
headers: this.headers,
|
|
575
|
+
body: JSON.stringify(options)
|
|
576
|
+
});
|
|
577
|
+
const data = await response.json();
|
|
578
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
579
|
+
throw new LLMPermissionError(data.permission || "vectors.write");
|
|
580
|
+
}
|
|
581
|
+
return data;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* List all available workspaces (namespaces) for this app
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```ts
|
|
588
|
+
* const { workspaces } = await sdk.llm.vectors.listWorkspaces();
|
|
589
|
+
* console.log('Workspaces:', workspaces);
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
async listWorkspaces() {
|
|
593
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/vectors/workspaces`, {
|
|
594
|
+
method: "GET",
|
|
595
|
+
headers: this.headers
|
|
596
|
+
});
|
|
597
|
+
const data = await response.json();
|
|
598
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
599
|
+
throw new LLMPermissionError(data.permission || "vectors.read");
|
|
600
|
+
}
|
|
601
|
+
return data;
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
var LLMModule = class {
|
|
605
|
+
constructor(baseUrl, appId) {
|
|
606
|
+
this.baseUrl = baseUrl;
|
|
607
|
+
this.appId = appId;
|
|
608
|
+
this.vectors = new VectorStore(baseUrl, appId);
|
|
609
|
+
}
|
|
610
|
+
get headers() {
|
|
611
|
+
return {
|
|
612
|
+
"Content-Type": "application/json",
|
|
613
|
+
"x-app-id": this.appId
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Get available LLM and embedding providers/models
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* ```ts
|
|
621
|
+
* const { llm, embedding } = await sdk.llm.getProviders();
|
|
622
|
+
* console.log('Available LLM models:', llm[0].models);
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
async getProviders() {
|
|
626
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/providers`, {
|
|
627
|
+
method: "GET",
|
|
628
|
+
headers: this.headers
|
|
629
|
+
});
|
|
630
|
+
const data = await response.json();
|
|
631
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
632
|
+
throw new LLMPermissionError(data.permission || "llm.providers");
|
|
633
|
+
}
|
|
634
|
+
return data;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Send a chat completion request (synchronous)
|
|
638
|
+
*
|
|
639
|
+
* @example
|
|
640
|
+
* ```ts
|
|
641
|
+
* const response = await sdk.llm.chat([
|
|
642
|
+
* { role: 'system', content: 'You are a helpful assistant.' },
|
|
643
|
+
* { role: 'user', content: 'Hello!' }
|
|
644
|
+
* ], { model: 'gpt-4o', temperature: 0.7 });
|
|
645
|
+
*
|
|
646
|
+
* console.log(response.response?.content);
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
async chat(messages, options = {}) {
|
|
650
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/chat`, {
|
|
651
|
+
method: "POST",
|
|
652
|
+
headers: this.headers,
|
|
653
|
+
body: JSON.stringify({
|
|
654
|
+
messages,
|
|
655
|
+
model: options.model,
|
|
656
|
+
provider: options.provider,
|
|
657
|
+
temperature: options.temperature ?? 0.7,
|
|
658
|
+
max_tokens: options.max_tokens ?? 1e3
|
|
659
|
+
})
|
|
660
|
+
});
|
|
661
|
+
const data = await response.json();
|
|
662
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
663
|
+
throw new LLMPermissionError(data.permission || "llm.chat");
|
|
664
|
+
}
|
|
665
|
+
if (data.code === "LLM_ERROR") {
|
|
666
|
+
throw new LLMProviderError(data.error || "LLM request failed");
|
|
667
|
+
}
|
|
668
|
+
return data;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Send a streaming chat completion request (SSE)
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```ts
|
|
675
|
+
* for await (const chunk of sdk.llm.chatStream([
|
|
676
|
+
* { role: 'user', content: 'Tell me a story' }
|
|
677
|
+
* ])) {
|
|
678
|
+
* process.stdout.write(chunk.textResponse || '');
|
|
679
|
+
* }
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
async *chatStream(messages, options = {}) {
|
|
683
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/chat/stream`, {
|
|
684
|
+
method: "POST",
|
|
685
|
+
headers: {
|
|
686
|
+
...this.headers,
|
|
687
|
+
"Accept": "text/event-stream"
|
|
688
|
+
},
|
|
689
|
+
body: JSON.stringify({
|
|
690
|
+
messages,
|
|
691
|
+
model: options.model,
|
|
692
|
+
provider: options.provider,
|
|
693
|
+
temperature: options.temperature ?? 0.7,
|
|
694
|
+
max_tokens: options.max_tokens ?? 1e3
|
|
695
|
+
})
|
|
696
|
+
});
|
|
697
|
+
if (!response.ok) {
|
|
698
|
+
const errorData = await response.json();
|
|
699
|
+
if (errorData.code === "PERMISSION_REQUIRED") {
|
|
700
|
+
throw new LLMPermissionError(errorData.permission || "llm.chat");
|
|
701
|
+
}
|
|
702
|
+
throw new LLMProviderError(errorData.error || "Stream request failed");
|
|
703
|
+
}
|
|
704
|
+
const reader = response.body?.getReader();
|
|
705
|
+
if (!reader) {
|
|
706
|
+
throw new LLMProviderError("Response body is not readable");
|
|
707
|
+
}
|
|
708
|
+
const decoder = new TextDecoder();
|
|
709
|
+
let buffer = "";
|
|
710
|
+
let isErrorEvent = false;
|
|
711
|
+
try {
|
|
712
|
+
while (true) {
|
|
713
|
+
const { done, value } = await reader.read();
|
|
714
|
+
if (done) break;
|
|
715
|
+
buffer += decoder.decode(value, { stream: true });
|
|
716
|
+
const lines = buffer.split("\n");
|
|
717
|
+
buffer = lines.pop() || "";
|
|
718
|
+
for (const line of lines) {
|
|
719
|
+
const trimmedLine = line.trim();
|
|
720
|
+
if (!trimmedLine || trimmedLine.startsWith(":")) continue;
|
|
721
|
+
if (trimmedLine.startsWith("event: error")) {
|
|
722
|
+
isErrorEvent = true;
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (trimmedLine.startsWith("data: ")) {
|
|
726
|
+
const jsonStr = trimmedLine.slice(6);
|
|
727
|
+
if (jsonStr === "[DONE]") {
|
|
728
|
+
isErrorEvent = false;
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
try {
|
|
732
|
+
const data = JSON.parse(jsonStr);
|
|
733
|
+
if (isErrorEvent) {
|
|
734
|
+
isErrorEvent = false;
|
|
735
|
+
throw new LLMProviderError(
|
|
736
|
+
data.error || "Stream error",
|
|
737
|
+
data.code || "LLM_STREAM_ERROR"
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
const chunk = data;
|
|
741
|
+
if (chunk.error) {
|
|
742
|
+
throw new LLMProviderError(
|
|
743
|
+
chunk.message || "Stream error"
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
yield chunk;
|
|
747
|
+
} catch (parseError) {
|
|
748
|
+
isErrorEvent = false;
|
|
749
|
+
if (jsonStr !== "[DONE]") {
|
|
750
|
+
console.warn("[LLM Stream] Parse error:", jsonStr);
|
|
751
|
+
}
|
|
752
|
+
if (parseError instanceof LLMProviderError) throw parseError;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
} finally {
|
|
758
|
+
reader.releaseLock();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Generate vector embeddings from text
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* ```ts
|
|
766
|
+
* // Single text
|
|
767
|
+
* const { embeddings } = await sdk.llm.embed('Hello world');
|
|
768
|
+
*
|
|
769
|
+
* // Multiple texts
|
|
770
|
+
* const { embeddings } = await sdk.llm.embed(['Hello', 'World']);
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
async embed(input, options = {}) {
|
|
774
|
+
const inputArray = Array.isArray(input) ? input : [input];
|
|
775
|
+
const response = await fetch(`${this.baseUrl}/sdk/llm/embed`, {
|
|
776
|
+
method: "POST",
|
|
777
|
+
headers: this.headers,
|
|
778
|
+
body: JSON.stringify({
|
|
779
|
+
input: inputArray,
|
|
780
|
+
provider: options.provider,
|
|
781
|
+
model: options.model
|
|
782
|
+
})
|
|
783
|
+
});
|
|
784
|
+
const data = await response.json();
|
|
785
|
+
if (data.code === "PERMISSION_REQUIRED") {
|
|
786
|
+
throw new LLMPermissionError(data.permission || "llm.embed");
|
|
787
|
+
}
|
|
788
|
+
if (data.code === "PROVIDER_UNAVAILABLE") {
|
|
789
|
+
throw new LLMProviderError(data.error || "Embedding provider not available");
|
|
790
|
+
}
|
|
791
|
+
return data;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Helper: Embed text and store as vectors in one call
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* await sdk.llm.embedAndStore({
|
|
799
|
+
* texts: ['Hello world', 'Goodbye world'],
|
|
800
|
+
* documentId: 'doc-123',
|
|
801
|
+
* workspaceId: 'ws-456'
|
|
802
|
+
* });
|
|
803
|
+
* ```
|
|
804
|
+
*/
|
|
805
|
+
async embedAndStore(params) {
|
|
806
|
+
const { texts, documentId, workspaceId, idPrefix = "chunk", provider, model } = params;
|
|
807
|
+
const embedResult = await this.embed(texts, { provider, model });
|
|
808
|
+
if (!embedResult.success || !embedResult.embeddings) {
|
|
809
|
+
return {
|
|
810
|
+
success: false,
|
|
811
|
+
error: embedResult.error || "Embedding failed",
|
|
812
|
+
code: embedResult.code
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
const vectors = texts.map((text, i) => ({
|
|
816
|
+
id: `${idPrefix}_${i}`,
|
|
817
|
+
vector: embedResult.embeddings[i],
|
|
818
|
+
metadata: {
|
|
819
|
+
text,
|
|
820
|
+
documentId,
|
|
821
|
+
workspaceId
|
|
822
|
+
}
|
|
823
|
+
}));
|
|
824
|
+
return this.vectors.upsert(vectors, { workspaceId });
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Helper: Search similar documents by text query
|
|
828
|
+
*
|
|
829
|
+
* @example
|
|
830
|
+
* ```ts
|
|
831
|
+
* const results = await sdk.llm.search('What is RealtimeX?', {
|
|
832
|
+
* topK: 5,
|
|
833
|
+
* workspaceId: 'ws-123'
|
|
834
|
+
* });
|
|
835
|
+
*
|
|
836
|
+
* for (const result of results) {
|
|
837
|
+
* console.log(result.metadata?.text, result.score);
|
|
838
|
+
* }
|
|
839
|
+
* ```
|
|
840
|
+
*/
|
|
841
|
+
async search(query, options = {}) {
|
|
842
|
+
const embedResult = await this.embed(query, {
|
|
843
|
+
provider: options.provider,
|
|
844
|
+
model: options.model
|
|
845
|
+
});
|
|
846
|
+
if (!embedResult.success || !embedResult.embeddings?.[0]) {
|
|
847
|
+
throw new LLMProviderError("Failed to embed query");
|
|
848
|
+
}
|
|
849
|
+
const queryResult = await this.vectors.query(embedResult.embeddings[0], options);
|
|
850
|
+
if (!queryResult.success) {
|
|
851
|
+
throw new LLMProviderError(queryResult.error || "Vector search failed");
|
|
852
|
+
}
|
|
853
|
+
return queryResult.results || [];
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
|
|
339
857
|
// src/index.ts
|
|
340
858
|
var _RealtimeXSDK = class _RealtimeXSDK {
|
|
341
859
|
constructor(config = {}) {
|
|
@@ -343,12 +861,45 @@ var _RealtimeXSDK = class _RealtimeXSDK {
|
|
|
343
861
|
const envAppName = this.getEnvVar("RTX_APP_NAME");
|
|
344
862
|
this.appId = config.realtimex?.appId || envAppId || "";
|
|
345
863
|
this.appName = config.realtimex?.appName || envAppName;
|
|
346
|
-
|
|
347
|
-
this.
|
|
348
|
-
this.
|
|
349
|
-
this.
|
|
350
|
-
this.
|
|
864
|
+
this.permissions = config.permissions || [];
|
|
865
|
+
this.realtimexUrl = config.realtimex?.url || _RealtimeXSDK.DEFAULT_REALTIMEX_URL;
|
|
866
|
+
this.activities = new ActivitiesModule(this.realtimexUrl, this.appId, this.appName);
|
|
867
|
+
this.webhook = new WebhookModule(this.realtimexUrl, this.appName, this.appId);
|
|
868
|
+
this.api = new ApiModule(this.realtimexUrl, this.appId, this.appName);
|
|
869
|
+
this.task = new TaskModule(this.realtimexUrl, this.appName, this.appId);
|
|
351
870
|
this.port = new PortModule(config.defaultPort);
|
|
871
|
+
this.llm = new LLMModule(this.realtimexUrl, this.appId);
|
|
872
|
+
if (this.permissions.length > 0) {
|
|
873
|
+
this.register().catch((err) => {
|
|
874
|
+
console.error("[RealtimeX SDK] Auto-registration failed:", err.message);
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Register app with RealtimeX hub and request declared permissions upfront.
|
|
880
|
+
* This is called automatically if permissions are provided in constructor.
|
|
881
|
+
*/
|
|
882
|
+
async register(permissions) {
|
|
883
|
+
const perms = permissions || this.permissions;
|
|
884
|
+
if (perms.length === 0) return;
|
|
885
|
+
try {
|
|
886
|
+
const response = await fetch(`${this.realtimexUrl.replace(/\/$/, "")}/sdk/register`, {
|
|
887
|
+
method: "POST",
|
|
888
|
+
headers: { "Content-Type": "application/json" },
|
|
889
|
+
body: JSON.stringify({
|
|
890
|
+
app_id: this.appId,
|
|
891
|
+
app_name: this.appName,
|
|
892
|
+
permissions: perms
|
|
893
|
+
})
|
|
894
|
+
});
|
|
895
|
+
const data = await response.json();
|
|
896
|
+
if (!response.ok) {
|
|
897
|
+
throw new Error(data.error || "Registration failed");
|
|
898
|
+
}
|
|
899
|
+
console.log(`[RealtimeX SDK] App registered successfully (${data.message})`);
|
|
900
|
+
} catch (error) {
|
|
901
|
+
throw new Error(`Failed to register app: ${error.message}`);
|
|
902
|
+
}
|
|
352
903
|
}
|
|
353
904
|
/**
|
|
354
905
|
* Get environment variable (works in Node.js and browser)
|
|
@@ -369,8 +920,14 @@ var RealtimeXSDK = _RealtimeXSDK;
|
|
|
369
920
|
0 && (module.exports = {
|
|
370
921
|
ActivitiesModule,
|
|
371
922
|
ApiModule,
|
|
923
|
+
LLMModule,
|
|
924
|
+
LLMPermissionError,
|
|
925
|
+
LLMProviderError,
|
|
926
|
+
PermissionDeniedError,
|
|
927
|
+
PermissionRequiredError,
|
|
372
928
|
PortModule,
|
|
373
929
|
RealtimeXSDK,
|
|
374
930
|
TaskModule,
|
|
931
|
+
VectorStore,
|
|
375
932
|
WebhookModule
|
|
376
933
|
});
|