@snap-agent/core 0.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/LICENSE +22 -0
- package/README.md +625 -0
- package/dist/chunk-V4TPAVOY.mjs +2227 -0
- package/dist/chunk-Y5TTFQWC.mjs +801 -0
- package/dist/dist-2CMI4QQD.mjs +1825 -0
- package/dist/dist-JU54Y3G4.mjs +3216 -0
- package/dist/index-CDsqnM8L.d.mts +680 -0
- package/dist/index-CDsqnM8L.d.ts +680 -0
- package/dist/index.d.mts +472 -0
- package/dist/index.d.ts +472 -0
- package/dist/index.js +9413 -0
- package/dist/index.mjs +1284 -0
- package/dist/storage/index.d.mts +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +829 -0
- package/dist/storage/index.mjs +10 -0
- package/package.json +114 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1284 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MemoryStorage,
|
|
3
|
+
MongoDBStorage,
|
|
4
|
+
UpstashStorage
|
|
5
|
+
} from "./chunk-Y5TTFQWC.mjs";
|
|
6
|
+
|
|
7
|
+
// src/core/Agent.ts
|
|
8
|
+
import { generateText, streamText } from "ai";
|
|
9
|
+
|
|
10
|
+
// src/core/PluginManager.ts
|
|
11
|
+
var PluginManager = class {
|
|
12
|
+
constructor(plugins = []) {
|
|
13
|
+
this.plugins = plugins.sort((a, b) => {
|
|
14
|
+
const aPriority = a.priority ?? 100;
|
|
15
|
+
const bPriority = b.priority ?? 100;
|
|
16
|
+
return aPriority - bPriority;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Plugin Getters by Type
|
|
21
|
+
// ============================================================================
|
|
22
|
+
getRAGPlugins() {
|
|
23
|
+
return this.plugins.filter((p) => p.type === "rag");
|
|
24
|
+
}
|
|
25
|
+
getToolPlugins() {
|
|
26
|
+
return this.plugins.filter((p) => p.type === "tool");
|
|
27
|
+
}
|
|
28
|
+
getMiddlewarePlugins() {
|
|
29
|
+
return this.plugins.filter((p) => p.type === "middleware");
|
|
30
|
+
}
|
|
31
|
+
getAnalyticsPlugins() {
|
|
32
|
+
return this.plugins.filter((p) => p.type === "analytics");
|
|
33
|
+
}
|
|
34
|
+
getAllPlugins() {
|
|
35
|
+
return [...this.plugins];
|
|
36
|
+
}
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// RAG Plugin Execution
|
|
39
|
+
// ============================================================================
|
|
40
|
+
/**
|
|
41
|
+
* Execute all RAG plugins and merge their contexts
|
|
42
|
+
* Returns an array of context strings, one per plugin
|
|
43
|
+
*/
|
|
44
|
+
async executeRAG(message, options) {
|
|
45
|
+
const ragPlugins = this.getRAGPlugins();
|
|
46
|
+
if (ragPlugins.length === 0) {
|
|
47
|
+
return { contexts: [], allMetadata: [] };
|
|
48
|
+
}
|
|
49
|
+
const results = await Promise.all(
|
|
50
|
+
ragPlugins.map(async (plugin) => {
|
|
51
|
+
try {
|
|
52
|
+
const context = await plugin.retrieveContext(message, options);
|
|
53
|
+
const formattedContext = plugin.formatContext ? plugin.formatContext(context) : context.content;
|
|
54
|
+
return {
|
|
55
|
+
context: formattedContext,
|
|
56
|
+
metadata: context.metadata || {},
|
|
57
|
+
pluginName: plugin.name
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error(`RAG plugin "${plugin.name}" failed:`, error);
|
|
61
|
+
return {
|
|
62
|
+
context: "",
|
|
63
|
+
metadata: { error: error instanceof Error ? error.message : "Unknown error" },
|
|
64
|
+
pluginName: plugin.name
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
);
|
|
69
|
+
return {
|
|
70
|
+
contexts: results.map((r) => r.context).filter(Boolean),
|
|
71
|
+
allMetadata: results.map((r) => ({ [r.pluginName]: r.metadata }))
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// Middleware Plugin Execution
|
|
76
|
+
// ============================================================================
|
|
77
|
+
/**
|
|
78
|
+
* Execute all middleware plugins before request
|
|
79
|
+
*/
|
|
80
|
+
async executeBeforeRequest(messages, context) {
|
|
81
|
+
const middlewarePlugins = this.getMiddlewarePlugins();
|
|
82
|
+
let result = { messages, metadata: {} };
|
|
83
|
+
for (const plugin of middlewarePlugins) {
|
|
84
|
+
if (plugin.beforeRequest) {
|
|
85
|
+
try {
|
|
86
|
+
const pluginResult = await plugin.beforeRequest(result.messages, context);
|
|
87
|
+
result = { ...result, ...pluginResult };
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(`Middleware plugin "${plugin.name}" beforeRequest failed:`, error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Execute all middleware plugins after response
|
|
97
|
+
*/
|
|
98
|
+
async executeAfterResponse(response, context) {
|
|
99
|
+
const middlewarePlugins = this.getMiddlewarePlugins();
|
|
100
|
+
let result = { response, metadata: context.metadata };
|
|
101
|
+
for (const plugin of middlewarePlugins) {
|
|
102
|
+
if (plugin.afterResponse) {
|
|
103
|
+
try {
|
|
104
|
+
const pluginResult = await plugin.afterResponse(result.response, context);
|
|
105
|
+
result = { ...result, ...pluginResult };
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error(`Middleware plugin "${plugin.name}" afterResponse failed:`, error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// Analytics Plugin Execution
|
|
115
|
+
// ============================================================================
|
|
116
|
+
/**
|
|
117
|
+
* Track request in all analytics plugins
|
|
118
|
+
*/
|
|
119
|
+
async trackRequest(data) {
|
|
120
|
+
const analyticsPlugins = this.getAnalyticsPlugins();
|
|
121
|
+
await Promise.all(
|
|
122
|
+
analyticsPlugins.map(async (plugin) => {
|
|
123
|
+
try {
|
|
124
|
+
await plugin.trackRequest(data);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(`Analytics plugin "${plugin.name}" trackRequest failed:`, error);
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Track response in all analytics plugins
|
|
133
|
+
*/
|
|
134
|
+
async trackResponse(data) {
|
|
135
|
+
const analyticsPlugins = this.getAnalyticsPlugins();
|
|
136
|
+
await Promise.all(
|
|
137
|
+
analyticsPlugins.map(async (plugin) => {
|
|
138
|
+
try {
|
|
139
|
+
await plugin.trackResponse(data);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`Analytics plugin "${plugin.name}" trackResponse failed:`, error);
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// Plugin Management
|
|
148
|
+
// ============================================================================
|
|
149
|
+
/**
|
|
150
|
+
* Check if any plugins of a specific type exist
|
|
151
|
+
*/
|
|
152
|
+
hasPluginsOfType(type) {
|
|
153
|
+
return this.plugins.some((p) => p.type === type);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get plugin by name
|
|
157
|
+
*/
|
|
158
|
+
getPluginByName(name) {
|
|
159
|
+
return this.plugins.find((p) => p.name === name);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/types/index.ts
|
|
164
|
+
var AgentSDKError = class extends Error {
|
|
165
|
+
constructor(message) {
|
|
166
|
+
super(message);
|
|
167
|
+
this.name = "AgentSDKError";
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var AgentNotFoundError = class extends AgentSDKError {
|
|
171
|
+
constructor(agentId) {
|
|
172
|
+
super(`Agent not found: ${agentId}`);
|
|
173
|
+
this.name = "AgentNotFoundError";
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var ThreadNotFoundError = class extends AgentSDKError {
|
|
177
|
+
constructor(threadId) {
|
|
178
|
+
super(`Thread not found: ${threadId}`);
|
|
179
|
+
this.name = "ThreadNotFoundError";
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
var CouldNotCreateThreadError = class extends AgentSDKError {
|
|
183
|
+
constructor(threadId) {
|
|
184
|
+
super(`Could not create thread: ${threadId}`);
|
|
185
|
+
this.name = "CouldNotCreateThreadError";
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
var ProviderNotFoundError = class extends AgentSDKError {
|
|
189
|
+
constructor(provider) {
|
|
190
|
+
super(`Provider not configured: ${provider}`);
|
|
191
|
+
this.name = "ProviderNotFoundError";
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
var InvalidConfigError = class extends AgentSDKError {
|
|
195
|
+
constructor(message) {
|
|
196
|
+
super(`Invalid configuration: ${message}`);
|
|
197
|
+
this.name = "InvalidConfigError";
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// src/core/Agent.ts
|
|
202
|
+
function extractTextContent(content) {
|
|
203
|
+
if (typeof content === "string") {
|
|
204
|
+
return content;
|
|
205
|
+
}
|
|
206
|
+
return content.map((part) => {
|
|
207
|
+
if ("text" in part) {
|
|
208
|
+
return part.text;
|
|
209
|
+
}
|
|
210
|
+
return "";
|
|
211
|
+
}).filter(Boolean).join(" ");
|
|
212
|
+
}
|
|
213
|
+
var Agent = class _Agent {
|
|
214
|
+
constructor(data, storage, providerFactory) {
|
|
215
|
+
this.data = data;
|
|
216
|
+
this.storage = storage;
|
|
217
|
+
this.providerFactory = providerFactory;
|
|
218
|
+
this.pluginManager = new PluginManager(data.plugins || []);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Create a new agent
|
|
222
|
+
*/
|
|
223
|
+
static async create(config, storage, providerFactory) {
|
|
224
|
+
const agentId = await storage.createAgent(config);
|
|
225
|
+
const data = await storage.getAgent(agentId);
|
|
226
|
+
if (!data) {
|
|
227
|
+
throw new AgentNotFoundError(agentId);
|
|
228
|
+
}
|
|
229
|
+
return new _Agent(data, storage, providerFactory);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Load an existing agent by ID
|
|
233
|
+
*/
|
|
234
|
+
static async load(agentId, storage, providerFactory) {
|
|
235
|
+
const data = await storage.getAgent(agentId);
|
|
236
|
+
if (!data) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return new _Agent(data, storage, providerFactory);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Update agent properties
|
|
243
|
+
*/
|
|
244
|
+
async update(updates) {
|
|
245
|
+
await this.storage.updateAgent(this.data.id, updates);
|
|
246
|
+
const updatedData = await this.storage.getAgent(this.data.id);
|
|
247
|
+
if (updatedData) {
|
|
248
|
+
this.data = updatedData;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Delete this agent
|
|
253
|
+
*/
|
|
254
|
+
async delete() {
|
|
255
|
+
await this.storage.deleteAgent(this.data.id);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Add files to the agent
|
|
259
|
+
*/
|
|
260
|
+
async addFiles(files) {
|
|
261
|
+
const currentFiles = [...this.data.files, ...files];
|
|
262
|
+
this.data.files = currentFiles;
|
|
263
|
+
this.data.updatedAt = /* @__PURE__ */ new Date();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Generate a text response with optional plugin support
|
|
267
|
+
*/
|
|
268
|
+
async generateResponse(messages, options) {
|
|
269
|
+
const startTime = Date.now();
|
|
270
|
+
if (messages.length > 0) {
|
|
271
|
+
await this.pluginManager.trackRequest({
|
|
272
|
+
agentId: this.data.id,
|
|
273
|
+
threadId: options?.threadId,
|
|
274
|
+
message: extractTextContent(messages[messages.length - 1].content),
|
|
275
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
const beforeResult = await this.pluginManager.executeBeforeRequest(messages, {
|
|
279
|
+
agentId: this.data.id,
|
|
280
|
+
threadId: options?.threadId
|
|
281
|
+
});
|
|
282
|
+
let systemPrompt = this.data.instructions;
|
|
283
|
+
let ragMetadata = [];
|
|
284
|
+
if (options?.useRAG && this.pluginManager.hasPluginsOfType("rag")) {
|
|
285
|
+
const lastMessage = messages[messages.length - 1];
|
|
286
|
+
const { contexts, allMetadata } = await this.pluginManager.executeRAG(
|
|
287
|
+
extractTextContent(lastMessage.content),
|
|
288
|
+
{
|
|
289
|
+
agentId: this.data.id,
|
|
290
|
+
threadId: options.threadId,
|
|
291
|
+
filters: options.ragFilters
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
if (contexts.length > 0) {
|
|
295
|
+
systemPrompt += "\n\n" + contexts.join("\n\n");
|
|
296
|
+
}
|
|
297
|
+
ragMetadata = allMetadata;
|
|
298
|
+
}
|
|
299
|
+
const model = await this.providerFactory.getModel(this.data.provider, this.data.model);
|
|
300
|
+
const { text } = await generateText({
|
|
301
|
+
model,
|
|
302
|
+
messages: beforeResult.messages,
|
|
303
|
+
system: systemPrompt
|
|
304
|
+
});
|
|
305
|
+
const afterResult = await this.pluginManager.executeAfterResponse(text, {
|
|
306
|
+
agentId: this.data.id,
|
|
307
|
+
threadId: options?.threadId,
|
|
308
|
+
metadata: beforeResult.metadata
|
|
309
|
+
});
|
|
310
|
+
const latency = Date.now() - startTime;
|
|
311
|
+
await this.pluginManager.trackResponse({
|
|
312
|
+
agentId: this.data.id,
|
|
313
|
+
threadId: options?.threadId,
|
|
314
|
+
response: afterResult.response,
|
|
315
|
+
latency,
|
|
316
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
317
|
+
});
|
|
318
|
+
return {
|
|
319
|
+
text: afterResult.response,
|
|
320
|
+
metadata: {
|
|
321
|
+
...afterResult.metadata,
|
|
322
|
+
ragMetadata,
|
|
323
|
+
latency
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Stream a text response with optional plugin support
|
|
329
|
+
*/
|
|
330
|
+
async streamResponse(messages, onChunk, onComplete, onError, options) {
|
|
331
|
+
try {
|
|
332
|
+
const startTime = Date.now();
|
|
333
|
+
if (messages.length > 0) {
|
|
334
|
+
await this.pluginManager.trackRequest({
|
|
335
|
+
agentId: this.data.id,
|
|
336
|
+
threadId: options?.threadId,
|
|
337
|
+
message: extractTextContent(messages[messages.length - 1].content),
|
|
338
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const beforeResult = await this.pluginManager.executeBeforeRequest(messages, {
|
|
342
|
+
agentId: this.data.id,
|
|
343
|
+
threadId: options?.threadId
|
|
344
|
+
});
|
|
345
|
+
let systemPrompt = this.data.instructions;
|
|
346
|
+
let ragMetadata = [];
|
|
347
|
+
if (options?.useRAG && this.pluginManager.hasPluginsOfType("rag")) {
|
|
348
|
+
const lastMessage = messages[messages.length - 1];
|
|
349
|
+
const { contexts, allMetadata } = await this.pluginManager.executeRAG(
|
|
350
|
+
extractTextContent(lastMessage.content),
|
|
351
|
+
{
|
|
352
|
+
agentId: this.data.id,
|
|
353
|
+
threadId: options.threadId,
|
|
354
|
+
filters: options.ragFilters
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
if (contexts.length > 0) {
|
|
358
|
+
systemPrompt += "\n\n" + contexts.join("\n\n");
|
|
359
|
+
}
|
|
360
|
+
ragMetadata = allMetadata;
|
|
361
|
+
}
|
|
362
|
+
const model = await this.providerFactory.getModel(this.data.provider, this.data.model);
|
|
363
|
+
const { textStream } = streamText({
|
|
364
|
+
model,
|
|
365
|
+
messages: beforeResult.messages,
|
|
366
|
+
system: systemPrompt
|
|
367
|
+
});
|
|
368
|
+
let fullText = "";
|
|
369
|
+
for await (const chunk of textStream) {
|
|
370
|
+
fullText += chunk;
|
|
371
|
+
onChunk(chunk);
|
|
372
|
+
}
|
|
373
|
+
const afterResult = await this.pluginManager.executeAfterResponse(fullText, {
|
|
374
|
+
agentId: this.data.id,
|
|
375
|
+
threadId: options?.threadId,
|
|
376
|
+
metadata: beforeResult.metadata
|
|
377
|
+
});
|
|
378
|
+
const latency = Date.now() - startTime;
|
|
379
|
+
await this.pluginManager.trackResponse({
|
|
380
|
+
agentId: this.data.id,
|
|
381
|
+
threadId: options?.threadId,
|
|
382
|
+
response: afterResult.response,
|
|
383
|
+
latency,
|
|
384
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
385
|
+
});
|
|
386
|
+
if (onComplete) {
|
|
387
|
+
onComplete(afterResult.response, {
|
|
388
|
+
...afterResult.metadata,
|
|
389
|
+
ragMetadata,
|
|
390
|
+
latency
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (onError) {
|
|
395
|
+
onError(error instanceof Error ? error : new Error("Unknown error"));
|
|
396
|
+
} else {
|
|
397
|
+
throw error;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Get agent ID
|
|
403
|
+
*/
|
|
404
|
+
get id() {
|
|
405
|
+
return this.data.id;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Get agent name
|
|
409
|
+
*/
|
|
410
|
+
get name() {
|
|
411
|
+
return this.data.name;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get agent instructions
|
|
415
|
+
*/
|
|
416
|
+
get instructions() {
|
|
417
|
+
return this.data.instructions;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Get agent provider
|
|
421
|
+
*/
|
|
422
|
+
get provider() {
|
|
423
|
+
return this.data.provider;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Get agent model
|
|
427
|
+
*/
|
|
428
|
+
get model() {
|
|
429
|
+
return this.data.model;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Get all plugins attached to this agent
|
|
433
|
+
*/
|
|
434
|
+
get plugins() {
|
|
435
|
+
return this.data.plugins || [];
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Add a plugin to this agent
|
|
439
|
+
*/
|
|
440
|
+
addPlugin(plugin) {
|
|
441
|
+
this.data.plugins = [...this.data.plugins || [], plugin];
|
|
442
|
+
this.pluginManager = new PluginManager(this.data.plugins);
|
|
443
|
+
this.data.updatedAt = /* @__PURE__ */ new Date();
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Remove a plugin by name
|
|
447
|
+
*/
|
|
448
|
+
removePlugin(pluginName) {
|
|
449
|
+
this.data.plugins = (this.data.plugins || []).filter((p) => p.name !== pluginName);
|
|
450
|
+
this.pluginManager = new PluginManager(this.data.plugins);
|
|
451
|
+
this.data.updatedAt = /* @__PURE__ */ new Date();
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Get all agent data
|
|
455
|
+
*/
|
|
456
|
+
toJSON() {
|
|
457
|
+
return { ...this.data };
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Ingest documents into RAG plugins
|
|
461
|
+
* Documents will be ingested into all RAG plugins that support ingestion
|
|
462
|
+
*/
|
|
463
|
+
async ingestDocuments(documents, options) {
|
|
464
|
+
const ragPlugins = this.data.plugins?.filter((p) => p.type === "rag") || [];
|
|
465
|
+
const results = [];
|
|
466
|
+
for (const plugin of ragPlugins) {
|
|
467
|
+
if ("ingest" in plugin && typeof plugin.ingest === "function") {
|
|
468
|
+
const result = await plugin.ingest(documents, {
|
|
469
|
+
agentId: this.data.id,
|
|
470
|
+
...options
|
|
471
|
+
});
|
|
472
|
+
results.push(result);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
if (results.length === 0) {
|
|
476
|
+
throw new Error("No RAG plugins with ingestion support found");
|
|
477
|
+
}
|
|
478
|
+
return results;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* Update a document in RAG plugins
|
|
482
|
+
*/
|
|
483
|
+
async updateDocument(id, document, options) {
|
|
484
|
+
const ragPlugins = this.data.plugins?.filter((p) => p.type === "rag") || [];
|
|
485
|
+
let updated = false;
|
|
486
|
+
for (const plugin of ragPlugins) {
|
|
487
|
+
if ("update" in plugin && typeof plugin.update === "function") {
|
|
488
|
+
await plugin.update(id, document, {
|
|
489
|
+
agentId: this.data.id,
|
|
490
|
+
...options
|
|
491
|
+
});
|
|
492
|
+
updated = true;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (!updated) {
|
|
496
|
+
throw new Error("No RAG plugins with update support found");
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Delete documents from RAG plugins
|
|
501
|
+
*/
|
|
502
|
+
async deleteDocuments(ids, options) {
|
|
503
|
+
const ragPlugins = this.data.plugins?.filter((p) => p.type === "rag") || [];
|
|
504
|
+
let totalDeleted = 0;
|
|
505
|
+
for (const plugin of ragPlugins) {
|
|
506
|
+
if ("delete" in plugin && typeof plugin.delete === "function") {
|
|
507
|
+
const count = await plugin.delete(ids, {
|
|
508
|
+
agentId: this.data.id,
|
|
509
|
+
...options
|
|
510
|
+
});
|
|
511
|
+
totalDeleted += count;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return totalDeleted;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Perform bulk operations on RAG plugins
|
|
518
|
+
*/
|
|
519
|
+
async bulkDocumentOperations(operations, options) {
|
|
520
|
+
const ragPlugins = this.data.plugins?.filter((p) => p.type === "rag") || [];
|
|
521
|
+
const results = [];
|
|
522
|
+
for (const plugin of ragPlugins) {
|
|
523
|
+
if ("bulk" in plugin && typeof plugin.bulk === "function") {
|
|
524
|
+
const result = await plugin.bulk(operations, {
|
|
525
|
+
agentId: this.data.id,
|
|
526
|
+
...options
|
|
527
|
+
});
|
|
528
|
+
results.push(result);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if (results.length === 0) {
|
|
532
|
+
throw new Error("No RAG plugins with bulk operation support found");
|
|
533
|
+
}
|
|
534
|
+
return results;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Ingest documents from a URL source (CSV, JSON, XML, API)
|
|
538
|
+
* Supports authentication, scheduling, and data transformation
|
|
539
|
+
*/
|
|
540
|
+
async ingestFromUrl(source, options) {
|
|
541
|
+
const ragPlugins = this.data.plugins?.filter((p) => p.type === "rag") || [];
|
|
542
|
+
const results = [];
|
|
543
|
+
for (const plugin of ragPlugins) {
|
|
544
|
+
if ("ingestFromUrl" in plugin && typeof plugin.ingestFromUrl === "function") {
|
|
545
|
+
const result = await plugin.ingestFromUrl(source, {
|
|
546
|
+
agentId: this.data.id,
|
|
547
|
+
...options
|
|
548
|
+
});
|
|
549
|
+
results.push(result);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (results.length === 0) {
|
|
553
|
+
throw new Error("No RAG plugins with URL ingestion support found");
|
|
554
|
+
}
|
|
555
|
+
return results;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Handle webhook payload for real-time document updates
|
|
559
|
+
* Useful for product inventory updates, price changes, etc.
|
|
560
|
+
*/
|
|
561
|
+
async handleWebhook(payload, source, options) {
|
|
562
|
+
const ragPlugins = this.data.plugins?.filter((p) => p.type === "rag") || [];
|
|
563
|
+
const results = [];
|
|
564
|
+
for (const plugin of ragPlugins) {
|
|
565
|
+
if ("handleWebhook" in plugin && typeof plugin.handleWebhook === "function") {
|
|
566
|
+
const result = await plugin.handleWebhook(payload, source, {
|
|
567
|
+
agentId: this.data.id,
|
|
568
|
+
...options
|
|
569
|
+
});
|
|
570
|
+
results.push(result);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (results.length === 0) {
|
|
574
|
+
throw new Error("No RAG plugins with webhook support found");
|
|
575
|
+
}
|
|
576
|
+
return results;
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// src/core/Thread.ts
|
|
581
|
+
var Thread = class _Thread {
|
|
582
|
+
constructor(data, storage) {
|
|
583
|
+
this.data = data;
|
|
584
|
+
this.storage = storage;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Create a new thread
|
|
588
|
+
*/
|
|
589
|
+
static async create(config, storage) {
|
|
590
|
+
const threadId = await storage.createThread(config);
|
|
591
|
+
const data = await storage.getThread(threadId);
|
|
592
|
+
if (!data) {
|
|
593
|
+
throw new CouldNotCreateThreadError(threadId);
|
|
594
|
+
}
|
|
595
|
+
return new _Thread(data, storage);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Load an existing thread by ID
|
|
599
|
+
*/
|
|
600
|
+
static async load(threadId, storage) {
|
|
601
|
+
const data = await storage.getThread(threadId);
|
|
602
|
+
if (!data) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
return new _Thread(data, storage);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Update thread properties
|
|
609
|
+
*/
|
|
610
|
+
async update(updates) {
|
|
611
|
+
await this.storage.updateThread(this.data.id, updates);
|
|
612
|
+
const updatedData = await this.storage.getThread(this.data.id);
|
|
613
|
+
if (updatedData) {
|
|
614
|
+
this.data = updatedData;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Delete this thread
|
|
619
|
+
*/
|
|
620
|
+
async delete() {
|
|
621
|
+
await this.storage.deleteThread(this.data.id);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Add a message to the thread
|
|
625
|
+
*/
|
|
626
|
+
async addMessage(role, content, attachments) {
|
|
627
|
+
const messageId = await this.storage.addMessage(
|
|
628
|
+
this.data.id,
|
|
629
|
+
role,
|
|
630
|
+
content,
|
|
631
|
+
attachments
|
|
632
|
+
);
|
|
633
|
+
const updatedData = await this.storage.getThread(this.data.id);
|
|
634
|
+
if (updatedData) {
|
|
635
|
+
this.data = updatedData;
|
|
636
|
+
}
|
|
637
|
+
return messageId;
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Get messages from this thread
|
|
641
|
+
*/
|
|
642
|
+
async getMessages(limit) {
|
|
643
|
+
return await this.storage.getMessages(this.data.id, limit);
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Get conversation context for AI (formatted for Vercel AI SDK)
|
|
647
|
+
*/
|
|
648
|
+
async getConversationContext(maxMessages = 20) {
|
|
649
|
+
const messages = await this.storage.getMessages(this.data.id, maxMessages);
|
|
650
|
+
return messages.map((msg) => ({
|
|
651
|
+
role: msg.role,
|
|
652
|
+
content: msg.content
|
|
653
|
+
}));
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Update thread name
|
|
657
|
+
*/
|
|
658
|
+
async updateName(name) {
|
|
659
|
+
await this.update({ name });
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Update pending status
|
|
663
|
+
*/
|
|
664
|
+
async updatePendingStatus(isPending) {
|
|
665
|
+
this.data.isPendingThread = isPending;
|
|
666
|
+
this.data.updatedAt = /* @__PURE__ */ new Date();
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Get thread ID
|
|
670
|
+
*/
|
|
671
|
+
get id() {
|
|
672
|
+
return this.data.id;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Get thread name
|
|
676
|
+
*/
|
|
677
|
+
get name() {
|
|
678
|
+
return this.data.name;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Get agent ID
|
|
682
|
+
*/
|
|
683
|
+
get agentId() {
|
|
684
|
+
return this.data.agentId;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Get messages (cached from last load)
|
|
688
|
+
*/
|
|
689
|
+
get messages() {
|
|
690
|
+
return this.data.messages;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Check if thread is pending
|
|
694
|
+
*/
|
|
695
|
+
get isPending() {
|
|
696
|
+
return this.data.isPendingThread;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Get all thread data
|
|
700
|
+
*/
|
|
701
|
+
toJSON() {
|
|
702
|
+
return { ...this.data };
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
// src/providers/index.ts
|
|
707
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
708
|
+
var ProviderFactory = class {
|
|
709
|
+
constructor(config) {
|
|
710
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
711
|
+
this.config = config;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Get a language model for the specified provider and model
|
|
715
|
+
* Uses dynamic imports for edge runtime compatibility
|
|
716
|
+
*/
|
|
717
|
+
async getModel(provider, modelName) {
|
|
718
|
+
const cacheKey = `${provider}:${modelName}`;
|
|
719
|
+
if (this.modelCache.has(cacheKey)) {
|
|
720
|
+
return this.modelCache.get(cacheKey);
|
|
721
|
+
}
|
|
722
|
+
let model;
|
|
723
|
+
switch (provider) {
|
|
724
|
+
case "openai": {
|
|
725
|
+
if (!this.config.openai?.apiKey) {
|
|
726
|
+
throw new ProviderNotFoundError("OpenAI API key not configured");
|
|
727
|
+
}
|
|
728
|
+
const openai = createOpenAI({
|
|
729
|
+
apiKey: this.config.openai.apiKey
|
|
730
|
+
});
|
|
731
|
+
model = openai(modelName);
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
case "anthropic": {
|
|
735
|
+
if (!this.config.anthropic?.apiKey) {
|
|
736
|
+
throw new ProviderNotFoundError("Anthropic API key not configured");
|
|
737
|
+
}
|
|
738
|
+
try {
|
|
739
|
+
const { createAnthropic } = await import("./dist-JU54Y3G4.mjs");
|
|
740
|
+
const anthropic = createAnthropic({
|
|
741
|
+
apiKey: this.config.anthropic.apiKey
|
|
742
|
+
});
|
|
743
|
+
model = anthropic(modelName);
|
|
744
|
+
} catch (error) {
|
|
745
|
+
throw new ProviderNotFoundError(
|
|
746
|
+
"Anthropic provider not installed. Run: npm install @ai-sdk/anthropic"
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
case "google": {
|
|
752
|
+
if (!this.config.google?.apiKey) {
|
|
753
|
+
throw new ProviderNotFoundError("Google API key not configured");
|
|
754
|
+
}
|
|
755
|
+
try {
|
|
756
|
+
const { createGoogleGenerativeAI } = await import("./dist-2CMI4QQD.mjs");
|
|
757
|
+
const google = createGoogleGenerativeAI({
|
|
758
|
+
apiKey: this.config.google.apiKey
|
|
759
|
+
});
|
|
760
|
+
model = google(modelName);
|
|
761
|
+
} catch (error) {
|
|
762
|
+
throw new ProviderNotFoundError(
|
|
763
|
+
"Google provider not installed. Run: npm install @ai-sdk/google"
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
break;
|
|
767
|
+
}
|
|
768
|
+
default:
|
|
769
|
+
throw new ProviderNotFoundError(`Unknown provider: ${provider}`);
|
|
770
|
+
}
|
|
771
|
+
this.modelCache.set(cacheKey, model);
|
|
772
|
+
return model;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Check if a provider is configured
|
|
776
|
+
*/
|
|
777
|
+
isProviderConfigured(provider) {
|
|
778
|
+
switch (provider) {
|
|
779
|
+
case "openai":
|
|
780
|
+
return !!this.config.openai?.apiKey;
|
|
781
|
+
case "anthropic":
|
|
782
|
+
return !!this.config.anthropic?.apiKey;
|
|
783
|
+
case "google":
|
|
784
|
+
return !!this.config.google?.apiKey;
|
|
785
|
+
default:
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Get list of configured providers
|
|
791
|
+
*/
|
|
792
|
+
getConfiguredProviders() {
|
|
793
|
+
const providers = [];
|
|
794
|
+
if (this.config.openai?.apiKey) providers.push("openai");
|
|
795
|
+
if (this.config.anthropic?.apiKey) providers.push("anthropic");
|
|
796
|
+
if (this.config.google?.apiKey) providers.push("google");
|
|
797
|
+
return providers;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Clear the model cache
|
|
801
|
+
*/
|
|
802
|
+
clearCache() {
|
|
803
|
+
this.modelCache.clear();
|
|
804
|
+
}
|
|
805
|
+
};
|
|
806
|
+
var Models = {
|
|
807
|
+
OpenAI: {
|
|
808
|
+
GPT4O: "gpt-4o",
|
|
809
|
+
GPT4O_MINI: "gpt-4o-mini",
|
|
810
|
+
GPT4_TURBO: "gpt-4-turbo",
|
|
811
|
+
GPT35_TURBO: "gpt-3.5-turbo"
|
|
812
|
+
},
|
|
813
|
+
Anthropic: {
|
|
814
|
+
CLAUDE_35_SONNET: "claude-3-5-sonnet-20241022",
|
|
815
|
+
CLAUDE_35_HAIKU: "claude-3-5-haiku-20241022",
|
|
816
|
+
CLAUDE_3_OPUS: "claude-3-opus-20240229"
|
|
817
|
+
},
|
|
818
|
+
Google: {
|
|
819
|
+
GEMINI_2_FLASH: "gemini-2.0-flash-exp",
|
|
820
|
+
GEMINI_15_PRO: "gemini-1.5-pro",
|
|
821
|
+
GEMINI_15_FLASH: "gemini-1.5-flash"
|
|
822
|
+
}
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
// src/inc/DefaultRAGPlugin.ts
|
|
826
|
+
var DefaultRAGPlugin = class {
|
|
827
|
+
constructor(config) {
|
|
828
|
+
this.name = "default-rag";
|
|
829
|
+
this.type = "rag";
|
|
830
|
+
this.documents = /* @__PURE__ */ new Map();
|
|
831
|
+
this.config = {
|
|
832
|
+
embeddingProvider: config.embeddingProvider || "openai",
|
|
833
|
+
embeddingModel: config.embeddingModel || "text-embedding-3-small",
|
|
834
|
+
limit: config.limit || 5,
|
|
835
|
+
embeddingProviderApiKey: config.embeddingProviderApiKey
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Retrieve context for a message using semantic search
|
|
840
|
+
*/
|
|
841
|
+
async retrieveContext(message, options) {
|
|
842
|
+
const agentDocs = this.documents.get(options.agentId) || [];
|
|
843
|
+
if (agentDocs.length === 0) {
|
|
844
|
+
return {
|
|
845
|
+
content: "",
|
|
846
|
+
sources: [],
|
|
847
|
+
metadata: { count: 0 }
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
const queryEmbedding = await this.generateEmbedding(message);
|
|
851
|
+
const scoredDocs = agentDocs.map((doc) => ({
|
|
852
|
+
...doc,
|
|
853
|
+
score: this.cosineSimilarity(queryEmbedding, doc.embedding)
|
|
854
|
+
})).sort((a, b) => b.score - a.score).slice(0, this.config.limit);
|
|
855
|
+
const content = scoredDocs.map((doc, idx) => `[${idx + 1}] ${doc.content}`).join("\n\n");
|
|
856
|
+
return {
|
|
857
|
+
content,
|
|
858
|
+
sources: scoredDocs.map((doc) => ({
|
|
859
|
+
id: doc.id,
|
|
860
|
+
title: doc.metadata?.title,
|
|
861
|
+
score: doc.score,
|
|
862
|
+
type: "document",
|
|
863
|
+
...doc.metadata
|
|
864
|
+
})),
|
|
865
|
+
metadata: {
|
|
866
|
+
count: scoredDocs.length,
|
|
867
|
+
totalDocuments: agentDocs.length
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Ingest documents into the RAG system
|
|
873
|
+
*/
|
|
874
|
+
async ingest(documents, options) {
|
|
875
|
+
if (!options?.agentId) {
|
|
876
|
+
return {
|
|
877
|
+
success: false,
|
|
878
|
+
indexed: 0,
|
|
879
|
+
failed: documents.length,
|
|
880
|
+
errors: [
|
|
881
|
+
{
|
|
882
|
+
id: "batch",
|
|
883
|
+
error: "agentId is required for document ingestion"
|
|
884
|
+
}
|
|
885
|
+
]
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
let indexed = 0;
|
|
889
|
+
const errors = [];
|
|
890
|
+
const agentDocs = this.documents.get(options.agentId) || [];
|
|
891
|
+
for (const doc of documents) {
|
|
892
|
+
try {
|
|
893
|
+
const embedding = await this.generateEmbedding(doc.content);
|
|
894
|
+
const existingIdx = agentDocs.findIndex((d) => d.id === doc.id);
|
|
895
|
+
const storedDoc = {
|
|
896
|
+
...doc,
|
|
897
|
+
embedding,
|
|
898
|
+
agentId: options.agentId,
|
|
899
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
900
|
+
};
|
|
901
|
+
if (existingIdx >= 0) {
|
|
902
|
+
if (options.overwrite) {
|
|
903
|
+
agentDocs[existingIdx] = storedDoc;
|
|
904
|
+
indexed++;
|
|
905
|
+
} else if (!options.skipExisting) {
|
|
906
|
+
agentDocs[existingIdx] = storedDoc;
|
|
907
|
+
indexed++;
|
|
908
|
+
}
|
|
909
|
+
} else {
|
|
910
|
+
agentDocs.push(storedDoc);
|
|
911
|
+
indexed++;
|
|
912
|
+
}
|
|
913
|
+
} catch (error) {
|
|
914
|
+
errors.push({
|
|
915
|
+
id: doc.id,
|
|
916
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
this.documents.set(options.agentId, agentDocs);
|
|
921
|
+
return {
|
|
922
|
+
success: errors.length === 0,
|
|
923
|
+
indexed,
|
|
924
|
+
failed: errors.length,
|
|
925
|
+
errors: errors.length > 0 ? errors : void 0,
|
|
926
|
+
metadata: {
|
|
927
|
+
totalDocuments: agentDocs.length
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Update a single document
|
|
933
|
+
*/
|
|
934
|
+
async update(id, document, options) {
|
|
935
|
+
if (!options?.agentId) {
|
|
936
|
+
throw new Error("agentId is required for document update");
|
|
937
|
+
}
|
|
938
|
+
const agentDocs = this.documents.get(options.agentId) || [];
|
|
939
|
+
const existingIdx = agentDocs.findIndex((d) => d.id === id);
|
|
940
|
+
if (existingIdx < 0) {
|
|
941
|
+
throw new Error(`Document not found: ${id}`);
|
|
942
|
+
}
|
|
943
|
+
const existing = agentDocs[existingIdx];
|
|
944
|
+
if (document.content && document.content !== existing.content) {
|
|
945
|
+
const embedding = await this.generateEmbedding(document.content);
|
|
946
|
+
agentDocs[existingIdx] = {
|
|
947
|
+
...existing,
|
|
948
|
+
...document,
|
|
949
|
+
content: document.content,
|
|
950
|
+
embedding
|
|
951
|
+
};
|
|
952
|
+
} else {
|
|
953
|
+
agentDocs[existingIdx] = {
|
|
954
|
+
...existing,
|
|
955
|
+
...document,
|
|
956
|
+
id: existing.id,
|
|
957
|
+
// Ensure ID doesn't change
|
|
958
|
+
content: existing.content,
|
|
959
|
+
// Ensure content doesn't change
|
|
960
|
+
embedding: existing.embedding
|
|
961
|
+
// Keep existing embedding
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
this.documents.set(options.agentId, agentDocs);
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Delete document(s) by ID
|
|
968
|
+
*/
|
|
969
|
+
async delete(ids, options) {
|
|
970
|
+
if (!options?.agentId) {
|
|
971
|
+
throw new Error("agentId is required for document deletion");
|
|
972
|
+
}
|
|
973
|
+
const agentDocs = this.documents.get(options.agentId) || [];
|
|
974
|
+
const idsArray = Array.isArray(ids) ? ids : [ids];
|
|
975
|
+
const initialCount = agentDocs.length;
|
|
976
|
+
const filtered = agentDocs.filter((doc) => !idsArray.includes(doc.id));
|
|
977
|
+
const deletedCount = initialCount - filtered.length;
|
|
978
|
+
this.documents.set(options.agentId, filtered);
|
|
979
|
+
return deletedCount;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Generate embedding using OpenAI
|
|
983
|
+
*/
|
|
984
|
+
async generateEmbedding(text) {
|
|
985
|
+
const response = await fetch("https://api.openai.com/v1/embeddings", {
|
|
986
|
+
method: "POST",
|
|
987
|
+
headers: {
|
|
988
|
+
"Content-Type": "application/json",
|
|
989
|
+
Authorization: `Bearer ${this.config.embeddingProviderApiKey}`
|
|
990
|
+
},
|
|
991
|
+
body: JSON.stringify({
|
|
992
|
+
model: this.config.embeddingModel,
|
|
993
|
+
input: text
|
|
994
|
+
})
|
|
995
|
+
});
|
|
996
|
+
if (!response.ok) {
|
|
997
|
+
const error = await response.text();
|
|
998
|
+
throw new Error(`OpenAI API error: ${response.status} - ${error}`);
|
|
999
|
+
}
|
|
1000
|
+
const data = await response.json();
|
|
1001
|
+
return data.data[0].embedding;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Calculate cosine similarity between two vectors
|
|
1005
|
+
*/
|
|
1006
|
+
cosineSimilarity(a, b) {
|
|
1007
|
+
if (a.length !== b.length) {
|
|
1008
|
+
throw new Error("Vectors must have the same length");
|
|
1009
|
+
}
|
|
1010
|
+
let dotProduct = 0;
|
|
1011
|
+
let normA = 0;
|
|
1012
|
+
let normB = 0;
|
|
1013
|
+
for (let i = 0; i < a.length; i++) {
|
|
1014
|
+
dotProduct += a[i] * b[i];
|
|
1015
|
+
normA += a[i] * a[i];
|
|
1016
|
+
normB += b[i] * b[i];
|
|
1017
|
+
}
|
|
1018
|
+
normA = Math.sqrt(normA);
|
|
1019
|
+
normB = Math.sqrt(normB);
|
|
1020
|
+
if (normA === 0 || normB === 0) {
|
|
1021
|
+
return 0;
|
|
1022
|
+
}
|
|
1023
|
+
return dotProduct / (normA * normB);
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Get statistics about stored documents
|
|
1027
|
+
*/
|
|
1028
|
+
getStats() {
|
|
1029
|
+
const stats = {
|
|
1030
|
+
totalAgents: this.documents.size,
|
|
1031
|
+
agentStats: {}
|
|
1032
|
+
};
|
|
1033
|
+
for (const [agentId, docs] of this.documents.entries()) {
|
|
1034
|
+
stats.agentStats[agentId] = {
|
|
1035
|
+
documentCount: docs.length
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
return stats;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Clear all documents for an agent
|
|
1042
|
+
*/
|
|
1043
|
+
clearAgent(agentId) {
|
|
1044
|
+
this.documents.delete(agentId);
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Clear all documents
|
|
1048
|
+
*/
|
|
1049
|
+
clearAll() {
|
|
1050
|
+
this.documents.clear();
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
|
|
1054
|
+
// src/core/Client.ts
|
|
1055
|
+
var AgentClient = class {
|
|
1056
|
+
constructor(config) {
|
|
1057
|
+
this.validateConfig(config);
|
|
1058
|
+
this.storage = config.storage;
|
|
1059
|
+
this.providers = config.providers;
|
|
1060
|
+
this.providerFactory = new ProviderFactory(config.providers);
|
|
1061
|
+
}
|
|
1062
|
+
validateConfig(config) {
|
|
1063
|
+
if (!config.storage) {
|
|
1064
|
+
throw new InvalidConfigError("Storage adapter is required");
|
|
1065
|
+
}
|
|
1066
|
+
if (!config.providers || Object.keys(config.providers).length === 0) {
|
|
1067
|
+
throw new InvalidConfigError("At least one provider must be configured");
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
// ============================================================================
|
|
1071
|
+
// Agent Operations
|
|
1072
|
+
// ============================================================================
|
|
1073
|
+
/**
|
|
1074
|
+
* Create a new agent
|
|
1075
|
+
*/
|
|
1076
|
+
async createAgent(config) {
|
|
1077
|
+
const agentConfig = {
|
|
1078
|
+
...config,
|
|
1079
|
+
provider: config.provider || "openai"
|
|
1080
|
+
};
|
|
1081
|
+
if (agentConfig.rag?.enabled) {
|
|
1082
|
+
const hasRAGPlugin = agentConfig.plugins?.some((p) => p.type === "rag");
|
|
1083
|
+
if (!hasRAGPlugin) {
|
|
1084
|
+
const embeddingProviderApiKey = agentConfig.rag.embeddingProviderApiKey || this.providers.openai?.apiKey;
|
|
1085
|
+
if (!embeddingProviderApiKey) {
|
|
1086
|
+
throw new InvalidConfigError(
|
|
1087
|
+
"RAG requires an embedding provider API key. Either configure OpenAI provider or set rag.embeddingProviderApiKey"
|
|
1088
|
+
);
|
|
1089
|
+
}
|
|
1090
|
+
const defaultRAGPlugin = new DefaultRAGPlugin({
|
|
1091
|
+
embeddingProviderApiKey,
|
|
1092
|
+
embeddingProvider: agentConfig.rag.embeddingProvider,
|
|
1093
|
+
embeddingModel: agentConfig.rag.embeddingModel,
|
|
1094
|
+
limit: agentConfig.rag.limit
|
|
1095
|
+
});
|
|
1096
|
+
agentConfig.plugins = [...agentConfig.plugins || [], defaultRAGPlugin];
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
return await Agent.create(agentConfig, this.storage, this.providerFactory);
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Get an agent by ID
|
|
1103
|
+
*/
|
|
1104
|
+
async getAgent(agentId) {
|
|
1105
|
+
const agent = await Agent.load(agentId, this.storage, this.providerFactory);
|
|
1106
|
+
if (!agent) {
|
|
1107
|
+
throw new AgentNotFoundError(agentId);
|
|
1108
|
+
}
|
|
1109
|
+
return agent;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* List agents for a user
|
|
1113
|
+
*/
|
|
1114
|
+
async listAgents(userId, organizationId) {
|
|
1115
|
+
return await this.storage.listAgents(userId, organizationId);
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* Delete an agent
|
|
1119
|
+
*/
|
|
1120
|
+
async deleteAgent(agentId) {
|
|
1121
|
+
const agent = await this.getAgent(agentId);
|
|
1122
|
+
await agent.delete();
|
|
1123
|
+
}
|
|
1124
|
+
// ============================================================================
|
|
1125
|
+
// Thread Operations
|
|
1126
|
+
// ============================================================================
|
|
1127
|
+
/**
|
|
1128
|
+
* Create a new thread
|
|
1129
|
+
*/
|
|
1130
|
+
async createThread(config) {
|
|
1131
|
+
await this.getAgent(config.agentId);
|
|
1132
|
+
return await Thread.create(config, this.storage);
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Get a thread by ID
|
|
1136
|
+
*/
|
|
1137
|
+
async getThread(threadId) {
|
|
1138
|
+
if (!threadId) {
|
|
1139
|
+
throw new ThreadNotFoundError("Thread ID is required");
|
|
1140
|
+
}
|
|
1141
|
+
const thread = await Thread.load(threadId, this.storage);
|
|
1142
|
+
if (!thread) {
|
|
1143
|
+
throw new ThreadNotFoundError(threadId);
|
|
1144
|
+
}
|
|
1145
|
+
return thread;
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* List threads by user or agent
|
|
1149
|
+
*/
|
|
1150
|
+
async listThreads(filters) {
|
|
1151
|
+
return await this.storage.listThreads(filters);
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Delete a thread
|
|
1155
|
+
*/
|
|
1156
|
+
async deleteThread(threadId) {
|
|
1157
|
+
const thread = await this.getThread(threadId);
|
|
1158
|
+
await thread.delete();
|
|
1159
|
+
}
|
|
1160
|
+
// ============================================================================
|
|
1161
|
+
// Chat Operations
|
|
1162
|
+
// ============================================================================
|
|
1163
|
+
/**
|
|
1164
|
+
* Send a message and get a response (non-streaming)
|
|
1165
|
+
*/
|
|
1166
|
+
async chat(request) {
|
|
1167
|
+
const thread = await this.getThread(request.threadId);
|
|
1168
|
+
const agent = await this.getAgent(thread.agentId);
|
|
1169
|
+
await thread.addMessage("user", request.message, request.attachments);
|
|
1170
|
+
const contextLength = request.contextLength ?? 20;
|
|
1171
|
+
const messages = await thread.getConversationContext(contextLength);
|
|
1172
|
+
const result = await agent.generateResponse(messages, {
|
|
1173
|
+
useRAG: request.useRAG,
|
|
1174
|
+
ragFilters: request.ragFilters,
|
|
1175
|
+
threadId: thread.id
|
|
1176
|
+
});
|
|
1177
|
+
const messageId = await thread.addMessage("assistant", result.text);
|
|
1178
|
+
return {
|
|
1179
|
+
reply: result.text,
|
|
1180
|
+
messageId,
|
|
1181
|
+
threadId: thread.id,
|
|
1182
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1183
|
+
metadata: result.metadata
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Send a message and stream the response
|
|
1188
|
+
*/
|
|
1189
|
+
async chatStream(request, callbacks) {
|
|
1190
|
+
try {
|
|
1191
|
+
const thread = await this.getThread(request.threadId);
|
|
1192
|
+
const agent = await this.getAgent(thread.agentId);
|
|
1193
|
+
await thread.addMessage("user", request.message, request.attachments);
|
|
1194
|
+
const contextLength = request.contextLength ?? 20;
|
|
1195
|
+
const messages = await thread.getConversationContext(contextLength);
|
|
1196
|
+
await agent.streamResponse(
|
|
1197
|
+
messages,
|
|
1198
|
+
callbacks.onChunk,
|
|
1199
|
+
async (fullResponse, metadata) => {
|
|
1200
|
+
await thread.addMessage("assistant", fullResponse);
|
|
1201
|
+
callbacks.onComplete(fullResponse, metadata);
|
|
1202
|
+
},
|
|
1203
|
+
callbacks.onError,
|
|
1204
|
+
{
|
|
1205
|
+
useRAG: request.useRAG,
|
|
1206
|
+
ragFilters: request.ragFilters,
|
|
1207
|
+
threadId: thread.id
|
|
1208
|
+
}
|
|
1209
|
+
);
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
callbacks.onError(
|
|
1212
|
+
error instanceof Error ? error : new Error("Unknown error")
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Generate a name for a thread based on its first message
|
|
1218
|
+
*/
|
|
1219
|
+
async generateThreadName(firstMessage) {
|
|
1220
|
+
const providers = this.providerFactory.getConfiguredProviders();
|
|
1221
|
+
if (providers.length === 0) {
|
|
1222
|
+
throw new InvalidConfigError("No providers configured");
|
|
1223
|
+
}
|
|
1224
|
+
const model = await this.providerFactory.getModel(providers[0], "gpt-4o");
|
|
1225
|
+
const { generateText: generateText2 } = await import("ai");
|
|
1226
|
+
const { text } = await generateText2({
|
|
1227
|
+
model,
|
|
1228
|
+
messages: [
|
|
1229
|
+
{
|
|
1230
|
+
role: "user",
|
|
1231
|
+
content: `Generate a brief and clear title to identify this conversation thread, based solely on the following first user message:
|
|
1232
|
+
"${firstMessage}"
|
|
1233
|
+
|
|
1234
|
+
Requirements:
|
|
1235
|
+
- Maximum 4 words
|
|
1236
|
+
- Specific and concise
|
|
1237
|
+
- Avoid generic words like "query" or "question"
|
|
1238
|
+
- Reflect the main topic
|
|
1239
|
+
Return only the title without additional explanations.`
|
|
1240
|
+
}
|
|
1241
|
+
],
|
|
1242
|
+
temperature: 0.3
|
|
1243
|
+
});
|
|
1244
|
+
return text.trim() || "New Chat";
|
|
1245
|
+
}
|
|
1246
|
+
// ============================================================================
|
|
1247
|
+
// Utility Methods
|
|
1248
|
+
// ============================================================================
|
|
1249
|
+
/**
|
|
1250
|
+
* Get list of configured providers
|
|
1251
|
+
*/
|
|
1252
|
+
getConfiguredProviders() {
|
|
1253
|
+
return this.providerFactory.getConfiguredProviders();
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Check if a provider is configured
|
|
1257
|
+
*/
|
|
1258
|
+
isProviderConfigured(provider) {
|
|
1259
|
+
return this.providerFactory.isProviderConfigured(provider);
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
// src/index.ts
|
|
1264
|
+
function createClient(config) {
|
|
1265
|
+
return new AgentClient(config);
|
|
1266
|
+
}
|
|
1267
|
+
export {
|
|
1268
|
+
Agent,
|
|
1269
|
+
AgentClient,
|
|
1270
|
+
AgentNotFoundError,
|
|
1271
|
+
AgentSDKError,
|
|
1272
|
+
DefaultRAGPlugin,
|
|
1273
|
+
InvalidConfigError,
|
|
1274
|
+
MemoryStorage,
|
|
1275
|
+
Models,
|
|
1276
|
+
MongoDBStorage,
|
|
1277
|
+
PluginManager,
|
|
1278
|
+
ProviderFactory,
|
|
1279
|
+
ProviderNotFoundError,
|
|
1280
|
+
Thread,
|
|
1281
|
+
ThreadNotFoundError,
|
|
1282
|
+
UpstashStorage,
|
|
1283
|
+
createClient
|
|
1284
|
+
};
|