@avee1234/agent-kit 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 +21 -0
- package/README.md +283 -0
- package/dist/index.cjs +657 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +201 -0
- package/dist/index.d.ts +201 -0
- package/dist/index.js +619 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
Agent: () => Agent,
|
|
24
|
+
AgentEventEmitter: () => AgentEventEmitter,
|
|
25
|
+
InMemoryStore: () => InMemoryStore,
|
|
26
|
+
Memory: () => Memory,
|
|
27
|
+
MockAdapter: () => MockAdapter,
|
|
28
|
+
OpenAICompatibleAdapter: () => OpenAICompatibleAdapter,
|
|
29
|
+
SQLiteStore: () => SQLiteStore,
|
|
30
|
+
Tool: () => Tool,
|
|
31
|
+
createMessage: () => createMessage,
|
|
32
|
+
createSummary: () => createSummary,
|
|
33
|
+
isToolCallMessage: () => isToolCallMessage
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/model/mock.ts
|
|
38
|
+
var MockAdapter = class {
|
|
39
|
+
async chat(messages, tools) {
|
|
40
|
+
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
41
|
+
const content = lastUserMsg?.content ?? "";
|
|
42
|
+
const systemMsg = messages.find((m) => m.role === "system");
|
|
43
|
+
if (systemMsg?.content.toLowerCase().includes("summarize")) {
|
|
44
|
+
const conversationContent = messages.filter((m) => m.role !== "system").map((m) => m.content).join(" ");
|
|
45
|
+
return { content: `[Mock Summary] ${conversationContent.slice(0, 200)}` };
|
|
46
|
+
}
|
|
47
|
+
if (tools?.length) {
|
|
48
|
+
for (const tool of tools) {
|
|
49
|
+
const toolNameWords = tool.name.replace(/_/g, " ").split(" ");
|
|
50
|
+
const matches = toolNameWords.some(
|
|
51
|
+
(word) => content.toLowerCase().includes(word.toLowerCase())
|
|
52
|
+
);
|
|
53
|
+
if (matches) {
|
|
54
|
+
return {
|
|
55
|
+
content: "",
|
|
56
|
+
toolCalls: [
|
|
57
|
+
{
|
|
58
|
+
id: `mock-tc-${Date.now()}`,
|
|
59
|
+
name: tool.name,
|
|
60
|
+
arguments: JSON.stringify(
|
|
61
|
+
Object.fromEntries(Object.keys(tool.parameters).map((key) => [key, content]))
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
content: `[Mock] Received: "${content}". Tools available: ${tools?.map((t) => t.name).join(", ") ?? "none"}.`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async *stream(messages, tools) {
|
|
74
|
+
const response = await this.chat(messages, tools);
|
|
75
|
+
const words = response.content.split(" ");
|
|
76
|
+
for (let i = 0; i < words.length; i++) {
|
|
77
|
+
yield {
|
|
78
|
+
text: (i > 0 ? " " : "") + words[i],
|
|
79
|
+
done: i === words.length - 1,
|
|
80
|
+
...i === words.length - 1 && response.toolCalls ? { toolCalls: response.toolCalls } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/model/openai-compatible.ts
|
|
87
|
+
var OpenAICompatibleAdapter = class {
|
|
88
|
+
config;
|
|
89
|
+
constructor(config) {
|
|
90
|
+
this.config = config;
|
|
91
|
+
}
|
|
92
|
+
async chat(messages, tools) {
|
|
93
|
+
const body = {
|
|
94
|
+
model: this.config.model,
|
|
95
|
+
messages: messages.map((m) => this.toOpenAIMessage(m))
|
|
96
|
+
};
|
|
97
|
+
if (tools?.length) {
|
|
98
|
+
body.tools = tools.map((t) => ({
|
|
99
|
+
type: "function",
|
|
100
|
+
function: {
|
|
101
|
+
name: t.name,
|
|
102
|
+
description: t.description,
|
|
103
|
+
parameters: { type: "object", properties: t.parameters }
|
|
104
|
+
}
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
const headers = { "Content-Type": "application/json" };
|
|
108
|
+
if (this.config.apiKey) headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
109
|
+
const response = await fetch(`${this.config.baseURL}/chat/completions`, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
headers,
|
|
112
|
+
body: JSON.stringify(body)
|
|
113
|
+
});
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
const errorText = await response.text();
|
|
116
|
+
throw new Error(`Model API error (${response.status}): ${errorText}`);
|
|
117
|
+
}
|
|
118
|
+
const data = await response.json();
|
|
119
|
+
const choice = data.choices[0].message;
|
|
120
|
+
const result = { content: choice.content ?? "" };
|
|
121
|
+
if (choice.tool_calls?.length) {
|
|
122
|
+
result.toolCalls = choice.tool_calls.map(
|
|
123
|
+
(tc) => ({
|
|
124
|
+
id: tc.id,
|
|
125
|
+
name: tc.function.name,
|
|
126
|
+
arguments: tc.function.arguments
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
if (data.usage) {
|
|
131
|
+
result.tokens = { input: data.usage.prompt_tokens, output: data.usage.completion_tokens };
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
async *stream(messages, tools) {
|
|
136
|
+
const headers = { "Content-Type": "application/json" };
|
|
137
|
+
if (this.config.apiKey) headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
138
|
+
const body = {
|
|
139
|
+
model: this.config.model,
|
|
140
|
+
messages: messages.map((m) => this.toOpenAIMessage(m)),
|
|
141
|
+
stream: true
|
|
142
|
+
};
|
|
143
|
+
if (tools?.length) {
|
|
144
|
+
body.tools = tools.map((t) => ({
|
|
145
|
+
type: "function",
|
|
146
|
+
function: {
|
|
147
|
+
name: t.name,
|
|
148
|
+
description: t.description,
|
|
149
|
+
parameters: { type: "object", properties: t.parameters }
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
const response = await fetch(`${this.config.baseURL}/chat/completions`, {
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers,
|
|
156
|
+
body: JSON.stringify(body)
|
|
157
|
+
});
|
|
158
|
+
if (!response.ok) {
|
|
159
|
+
const errorText = await response.text();
|
|
160
|
+
throw new Error(`Model API error (${response.status}): ${errorText}`);
|
|
161
|
+
}
|
|
162
|
+
const reader = response.body?.getReader();
|
|
163
|
+
if (!reader) throw new Error("No response body");
|
|
164
|
+
const decoder = new TextDecoder();
|
|
165
|
+
let buffer = "";
|
|
166
|
+
while (true) {
|
|
167
|
+
const { done, value } = await reader.read();
|
|
168
|
+
if (done) break;
|
|
169
|
+
buffer += decoder.decode(value, { stream: true });
|
|
170
|
+
const lines = buffer.split("\n");
|
|
171
|
+
buffer = lines.pop() ?? "";
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
if (!line.startsWith("data: ")) continue;
|
|
174
|
+
const data = line.slice(6).trim();
|
|
175
|
+
if (data === "[DONE]") {
|
|
176
|
+
yield { text: "", done: true };
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const parsed = JSON.parse(data);
|
|
180
|
+
const delta = parsed.choices?.[0]?.delta;
|
|
181
|
+
if (delta?.content) yield { text: delta.content, done: false };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
toOpenAIMessage(msg) {
|
|
186
|
+
const result = { role: msg.role, content: msg.content };
|
|
187
|
+
if (msg.toolCalls) {
|
|
188
|
+
result.tool_calls = msg.toolCalls.map((tc) => ({
|
|
189
|
+
id: tc.id,
|
|
190
|
+
type: "function",
|
|
191
|
+
function: { name: tc.name, arguments: tc.arguments }
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
if (msg.toolCallId) result.tool_call_id = msg.toolCallId;
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/events.ts
|
|
200
|
+
var AgentEventEmitter = class {
|
|
201
|
+
listeners = /* @__PURE__ */ new Map();
|
|
202
|
+
on(type, handler) {
|
|
203
|
+
if (!this.listeners.has(type)) {
|
|
204
|
+
this.listeners.set(type, /* @__PURE__ */ new Set());
|
|
205
|
+
}
|
|
206
|
+
this.listeners.get(type).add(handler);
|
|
207
|
+
}
|
|
208
|
+
off(type, handler) {
|
|
209
|
+
this.listeners.get(type)?.delete(handler);
|
|
210
|
+
}
|
|
211
|
+
emit(event) {
|
|
212
|
+
this.listeners.get(event.type)?.forEach((handler) => handler(event));
|
|
213
|
+
if (event.type !== "*") {
|
|
214
|
+
this.listeners.get("*")?.forEach((handler) => handler(event));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// src/types.ts
|
|
220
|
+
function createMessage(opts) {
|
|
221
|
+
return {
|
|
222
|
+
...opts,
|
|
223
|
+
timestamp: opts.timestamp ?? Date.now()
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function isToolCallMessage(msg) {
|
|
227
|
+
return Array.isArray(msg.toolCalls) && msg.toolCalls.length > 0;
|
|
228
|
+
}
|
|
229
|
+
function createSummary(opts) {
|
|
230
|
+
return {
|
|
231
|
+
id: opts.id ?? crypto.randomUUID(),
|
|
232
|
+
content: opts.content,
|
|
233
|
+
timestamp: opts.timestamp ?? Date.now(),
|
|
234
|
+
messageRange: opts.messageRange ?? { from: 0, to: 0 }
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/agent.ts
|
|
239
|
+
var Agent = class {
|
|
240
|
+
name;
|
|
241
|
+
model;
|
|
242
|
+
memory;
|
|
243
|
+
tools;
|
|
244
|
+
toolMap;
|
|
245
|
+
system;
|
|
246
|
+
maxToolRounds;
|
|
247
|
+
emitter;
|
|
248
|
+
constructor(config) {
|
|
249
|
+
this.name = config.name;
|
|
250
|
+
this.memory = config.memory;
|
|
251
|
+
this.tools = config.tools ?? [];
|
|
252
|
+
this.system = config.system;
|
|
253
|
+
this.maxToolRounds = config.maxToolRounds ?? 10;
|
|
254
|
+
this.emitter = new AgentEventEmitter();
|
|
255
|
+
this.toolMap = new Map(this.tools.map((t) => [t.name, t]));
|
|
256
|
+
if (!config.model) {
|
|
257
|
+
this.model = new MockAdapter();
|
|
258
|
+
} else if (this.isModelAdapter(config.model)) {
|
|
259
|
+
this.model = config.model;
|
|
260
|
+
} else {
|
|
261
|
+
const cfg = config.model;
|
|
262
|
+
const baseURL = cfg.provider === "ollama" ? cfg.baseURL ?? "http://localhost:11434/v1" : cfg.baseURL ?? "http://localhost:11434/v1";
|
|
263
|
+
this.model = new OpenAICompatibleAdapter({
|
|
264
|
+
baseURL,
|
|
265
|
+
model: cfg.model,
|
|
266
|
+
apiKey: cfg.apiKey
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (this.memory) {
|
|
270
|
+
this.memory.setModel(this.model);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
isModelAdapter(obj) {
|
|
274
|
+
return typeof obj.chat === "function";
|
|
275
|
+
}
|
|
276
|
+
on(type, handler) {
|
|
277
|
+
this.emitter.on(type, handler);
|
|
278
|
+
}
|
|
279
|
+
off(type, handler) {
|
|
280
|
+
this.emitter.off(type, handler);
|
|
281
|
+
}
|
|
282
|
+
emit(type, data, extras) {
|
|
283
|
+
this.emitter.emit({
|
|
284
|
+
type,
|
|
285
|
+
timestamp: Date.now(),
|
|
286
|
+
agentId: this.name,
|
|
287
|
+
data,
|
|
288
|
+
...extras
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
async chat(input) {
|
|
292
|
+
const toolDefs = this.tools.map((t) => t.definition);
|
|
293
|
+
const messages = [];
|
|
294
|
+
if (this.system) {
|
|
295
|
+
messages.push(createMessage({ role: "system", content: this.system }));
|
|
296
|
+
}
|
|
297
|
+
if (this.memory) {
|
|
298
|
+
const ctx = await this.memory.getContext(this.name, input);
|
|
299
|
+
this.emit("memory:retrieve", {
|
|
300
|
+
summaries: ctx.relevantSummaries.length,
|
|
301
|
+
recentMessages: ctx.recentMessages.length
|
|
302
|
+
});
|
|
303
|
+
for (const summary of ctx.relevantSummaries) {
|
|
304
|
+
messages.push(
|
|
305
|
+
createMessage({
|
|
306
|
+
role: "system",
|
|
307
|
+
content: `[Previous conversation summary]: ${summary.content}`,
|
|
308
|
+
timestamp: summary.timestamp
|
|
309
|
+
})
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
messages.push(...ctx.recentMessages);
|
|
313
|
+
}
|
|
314
|
+
const userMessage = createMessage({ role: "user", content: input });
|
|
315
|
+
messages.push(userMessage);
|
|
316
|
+
this.emit("message", { role: "user", content: input });
|
|
317
|
+
let response = { content: "" };
|
|
318
|
+
let toolRoundsUsed = 0;
|
|
319
|
+
response = await this.model.chat(messages, toolDefs.length ? toolDefs : void 0);
|
|
320
|
+
while (isToolCallMessage({ ...response, role: "assistant", timestamp: Date.now() }) && toolRoundsUsed < this.maxToolRounds) {
|
|
321
|
+
const assistantMsg = createMessage({
|
|
322
|
+
role: "assistant",
|
|
323
|
+
content: response.content,
|
|
324
|
+
toolCalls: response.toolCalls
|
|
325
|
+
});
|
|
326
|
+
messages.push(assistantMsg);
|
|
327
|
+
for (const toolCall of response.toolCalls ?? []) {
|
|
328
|
+
const tool = this.toolMap.get(toolCall.name);
|
|
329
|
+
const startTime = Date.now();
|
|
330
|
+
this.emit("tool:start", {
|
|
331
|
+
toolName: toolCall.name,
|
|
332
|
+
toolCallId: toolCall.id,
|
|
333
|
+
arguments: toolCall.arguments
|
|
334
|
+
});
|
|
335
|
+
let toolResult;
|
|
336
|
+
if (!tool) {
|
|
337
|
+
toolResult = `Error: tool "${toolCall.name}" not found`;
|
|
338
|
+
this.emit("error", {
|
|
339
|
+
message: `Tool not found: ${toolCall.name}`,
|
|
340
|
+
toolCallId: toolCall.id
|
|
341
|
+
});
|
|
342
|
+
} else {
|
|
343
|
+
try {
|
|
344
|
+
let parsedArgs = {};
|
|
345
|
+
try {
|
|
346
|
+
parsedArgs = JSON.parse(toolCall.arguments);
|
|
347
|
+
} catch {
|
|
348
|
+
parsedArgs = {};
|
|
349
|
+
}
|
|
350
|
+
const result = await tool.execute(parsedArgs);
|
|
351
|
+
toolResult = typeof result === "string" ? result : JSON.stringify(result);
|
|
352
|
+
} catch (err) {
|
|
353
|
+
const error = err;
|
|
354
|
+
toolResult = `Error: ${error.message}`;
|
|
355
|
+
this.emit("error", {
|
|
356
|
+
message: error.message,
|
|
357
|
+
toolCallId: toolCall.id,
|
|
358
|
+
toolName: toolCall.name
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const latencyMs = Date.now() - startTime;
|
|
363
|
+
this.emit("tool:end", {
|
|
364
|
+
toolName: toolCall.name,
|
|
365
|
+
toolCallId: toolCall.id,
|
|
366
|
+
result: toolResult,
|
|
367
|
+
latencyMs
|
|
368
|
+
});
|
|
369
|
+
messages.push(
|
|
370
|
+
createMessage({
|
|
371
|
+
role: "tool",
|
|
372
|
+
content: toolResult,
|
|
373
|
+
toolCallId: toolCall.id
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
toolRoundsUsed++;
|
|
378
|
+
response = await this.model.chat(messages, toolDefs.length ? toolDefs : void 0);
|
|
379
|
+
}
|
|
380
|
+
this.emit("message", { role: "assistant", content: response.content });
|
|
381
|
+
if (this.memory) {
|
|
382
|
+
const exchangeMessages = [
|
|
383
|
+
userMessage,
|
|
384
|
+
createMessage({
|
|
385
|
+
role: "assistant",
|
|
386
|
+
content: response.content,
|
|
387
|
+
toolCalls: response.toolCalls
|
|
388
|
+
})
|
|
389
|
+
];
|
|
390
|
+
await this.memory.saveExchange(this.name, exchangeMessages);
|
|
391
|
+
}
|
|
392
|
+
return response;
|
|
393
|
+
}
|
|
394
|
+
async *stream(input) {
|
|
395
|
+
const toolDefs = this.tools.map((t) => t.definition);
|
|
396
|
+
const messages = [];
|
|
397
|
+
if (this.system) {
|
|
398
|
+
messages.push(createMessage({ role: "system", content: this.system }));
|
|
399
|
+
}
|
|
400
|
+
if (this.memory) {
|
|
401
|
+
const ctx = await this.memory.getContext(this.name, input);
|
|
402
|
+
for (const summary of ctx.relevantSummaries) {
|
|
403
|
+
messages.push(
|
|
404
|
+
createMessage({
|
|
405
|
+
role: "system",
|
|
406
|
+
content: `[Previous conversation summary]: ${summary.content}`,
|
|
407
|
+
timestamp: summary.timestamp
|
|
408
|
+
})
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
messages.push(...ctx.recentMessages);
|
|
412
|
+
}
|
|
413
|
+
const userMessage = createMessage({ role: "user", content: input });
|
|
414
|
+
messages.push(userMessage);
|
|
415
|
+
this.emit("message", { role: "user", content: input });
|
|
416
|
+
let fullText = "";
|
|
417
|
+
for await (const chunk of this.model.stream(messages, toolDefs.length ? toolDefs : void 0)) {
|
|
418
|
+
fullText += chunk.text;
|
|
419
|
+
yield chunk;
|
|
420
|
+
}
|
|
421
|
+
this.emit("message", { role: "assistant", content: fullText });
|
|
422
|
+
if (this.memory) {
|
|
423
|
+
await this.memory.saveExchange(this.name, [
|
|
424
|
+
userMessage,
|
|
425
|
+
createMessage({ role: "assistant", content: fullText })
|
|
426
|
+
]);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// src/tool.ts
|
|
432
|
+
var Tool = class _Tool {
|
|
433
|
+
name;
|
|
434
|
+
description;
|
|
435
|
+
definition;
|
|
436
|
+
executeFn;
|
|
437
|
+
constructor(config) {
|
|
438
|
+
this.name = config.name;
|
|
439
|
+
this.description = config.description;
|
|
440
|
+
this.definition = {
|
|
441
|
+
name: config.name,
|
|
442
|
+
description: config.description,
|
|
443
|
+
parameters: config.parameters
|
|
444
|
+
};
|
|
445
|
+
this.executeFn = config.execute;
|
|
446
|
+
}
|
|
447
|
+
static create(config) {
|
|
448
|
+
return new _Tool(config);
|
|
449
|
+
}
|
|
450
|
+
async execute(params) {
|
|
451
|
+
return this.executeFn(params);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// src/store/in-memory.ts
|
|
456
|
+
var InMemoryStore = class {
|
|
457
|
+
messages = /* @__PURE__ */ new Map();
|
|
458
|
+
summaries = /* @__PURE__ */ new Map();
|
|
459
|
+
async saveMessages(agentId, messages) {
|
|
460
|
+
const existing = this.messages.get(agentId) ?? [];
|
|
461
|
+
existing.push(...messages);
|
|
462
|
+
this.messages.set(agentId, existing);
|
|
463
|
+
}
|
|
464
|
+
async getRecentMessages(agentId, limit) {
|
|
465
|
+
const all = this.messages.get(agentId) ?? [];
|
|
466
|
+
return all.slice(-limit);
|
|
467
|
+
}
|
|
468
|
+
async saveSummary(agentId, summary) {
|
|
469
|
+
const existing = this.summaries.get(agentId) ?? [];
|
|
470
|
+
existing.push(summary);
|
|
471
|
+
this.summaries.set(agentId, existing);
|
|
472
|
+
}
|
|
473
|
+
async searchSummaries(agentId, query, limit) {
|
|
474
|
+
const all = this.summaries.get(agentId) ?? [];
|
|
475
|
+
const queryLower = query.toLowerCase();
|
|
476
|
+
const matches = all.filter((s) => s.content.toLowerCase().includes(queryLower)).sort((a, b) => b.timestamp - a.timestamp).slice(0, limit);
|
|
477
|
+
return matches;
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/store/sqlite.ts
|
|
482
|
+
var import_module = require("module");
|
|
483
|
+
var import_meta = {};
|
|
484
|
+
var require2 = (0, import_module.createRequire)(import_meta.url);
|
|
485
|
+
var SQLiteStore = class {
|
|
486
|
+
db;
|
|
487
|
+
constructor(path) {
|
|
488
|
+
const Database = require2("better-sqlite3");
|
|
489
|
+
this.db = new Database(path);
|
|
490
|
+
this.db.pragma("journal_mode = WAL");
|
|
491
|
+
this.init();
|
|
492
|
+
}
|
|
493
|
+
init() {
|
|
494
|
+
this.db.exec(`
|
|
495
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
496
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
497
|
+
agent_id TEXT NOT NULL,
|
|
498
|
+
role TEXT NOT NULL,
|
|
499
|
+
content TEXT NOT NULL,
|
|
500
|
+
timestamp INTEGER NOT NULL,
|
|
501
|
+
tool_calls TEXT,
|
|
502
|
+
tool_call_id TEXT
|
|
503
|
+
);
|
|
504
|
+
CREATE TABLE IF NOT EXISTS summaries (
|
|
505
|
+
id TEXT PRIMARY KEY,
|
|
506
|
+
agent_id TEXT NOT NULL,
|
|
507
|
+
content TEXT NOT NULL,
|
|
508
|
+
timestamp INTEGER NOT NULL,
|
|
509
|
+
message_range_from INTEGER NOT NULL,
|
|
510
|
+
message_range_to INTEGER NOT NULL
|
|
511
|
+
);
|
|
512
|
+
CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(agent_id);
|
|
513
|
+
CREATE INDEX IF NOT EXISTS idx_summaries_agent ON summaries(agent_id);
|
|
514
|
+
`);
|
|
515
|
+
}
|
|
516
|
+
async saveMessages(agentId, messages) {
|
|
517
|
+
const stmt = this.db.prepare(
|
|
518
|
+
"INSERT INTO messages (agent_id, role, content, timestamp, tool_calls, tool_call_id) VALUES (?, ?, ?, ?, ?, ?)"
|
|
519
|
+
);
|
|
520
|
+
const insertMany = this.db.transaction((msgs) => {
|
|
521
|
+
for (const msg of msgs) {
|
|
522
|
+
stmt.run(
|
|
523
|
+
agentId,
|
|
524
|
+
msg.role,
|
|
525
|
+
msg.content,
|
|
526
|
+
msg.timestamp,
|
|
527
|
+
msg.toolCalls ? JSON.stringify(msg.toolCalls) : null,
|
|
528
|
+
msg.toolCallId ?? null
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
insertMany(messages);
|
|
533
|
+
}
|
|
534
|
+
async getRecentMessages(agentId, limit) {
|
|
535
|
+
const rows = this.db.prepare(
|
|
536
|
+
`SELECT role, content, timestamp, tool_calls, tool_call_id
|
|
537
|
+
FROM messages WHERE agent_id = ?
|
|
538
|
+
ORDER BY id DESC LIMIT ?`
|
|
539
|
+
).all(agentId, limit);
|
|
540
|
+
return rows.reverse().map((row) => ({
|
|
541
|
+
role: row.role,
|
|
542
|
+
content: row.content,
|
|
543
|
+
timestamp: row.timestamp,
|
|
544
|
+
...row.tool_calls ? { toolCalls: JSON.parse(row.tool_calls) } : {},
|
|
545
|
+
...row.tool_call_id ? { toolCallId: row.tool_call_id } : {}
|
|
546
|
+
}));
|
|
547
|
+
}
|
|
548
|
+
async saveSummary(agentId, summary) {
|
|
549
|
+
this.db.prepare(
|
|
550
|
+
"INSERT INTO summaries (id, agent_id, content, timestamp, message_range_from, message_range_to) VALUES (?, ?, ?, ?, ?, ?)"
|
|
551
|
+
).run(
|
|
552
|
+
summary.id,
|
|
553
|
+
agentId,
|
|
554
|
+
summary.content,
|
|
555
|
+
summary.timestamp,
|
|
556
|
+
summary.messageRange.from,
|
|
557
|
+
summary.messageRange.to
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
async searchSummaries(agentId, query, limit) {
|
|
561
|
+
const rows = this.db.prepare(
|
|
562
|
+
`SELECT id, content, timestamp, message_range_from, message_range_to
|
|
563
|
+
FROM summaries WHERE agent_id = ? AND content LIKE ?
|
|
564
|
+
ORDER BY timestamp DESC LIMIT ?`
|
|
565
|
+
).all(agentId, `%${query}%`, limit);
|
|
566
|
+
return rows.map((row) => ({
|
|
567
|
+
id: row.id,
|
|
568
|
+
content: row.content,
|
|
569
|
+
timestamp: row.timestamp,
|
|
570
|
+
messageRange: { from: row.message_range_from, to: row.message_range_to }
|
|
571
|
+
}));
|
|
572
|
+
}
|
|
573
|
+
close() {
|
|
574
|
+
this.db.close();
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// src/memory.ts
|
|
579
|
+
var Memory = class {
|
|
580
|
+
store;
|
|
581
|
+
windowSize;
|
|
582
|
+
summarizeAfter;
|
|
583
|
+
model;
|
|
584
|
+
messageCount = /* @__PURE__ */ new Map();
|
|
585
|
+
constructor(config = {}) {
|
|
586
|
+
this.windowSize = config.windowSize ?? 20;
|
|
587
|
+
this.summarizeAfter = config.summarizeAfter ?? 20;
|
|
588
|
+
if (!config.store || config.store === "memory") {
|
|
589
|
+
this.store = new InMemoryStore();
|
|
590
|
+
} else if (config.store === "sqlite") {
|
|
591
|
+
this.store = new SQLiteStore(config.path ?? "./agent-memory.db");
|
|
592
|
+
} else {
|
|
593
|
+
this.store = config.store;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
setModel(model) {
|
|
597
|
+
this.model = model;
|
|
598
|
+
}
|
|
599
|
+
getStore() {
|
|
600
|
+
return this.store;
|
|
601
|
+
}
|
|
602
|
+
async saveExchange(agentId, messages) {
|
|
603
|
+
await this.store.saveMessages(agentId, messages);
|
|
604
|
+
const count = (this.messageCount.get(agentId) ?? 0) + messages.length;
|
|
605
|
+
this.messageCount.set(agentId, count);
|
|
606
|
+
if (count >= this.summarizeAfter && this.model) {
|
|
607
|
+
await this.summarize(agentId);
|
|
608
|
+
this.messageCount.set(agentId, 0);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async getContext(agentId, query) {
|
|
612
|
+
const recentMessages = await this.store.getRecentMessages(agentId, this.windowSize);
|
|
613
|
+
const relevantSummaries = await this.store.searchSummaries(agentId, query, 3);
|
|
614
|
+
return { recentMessages, relevantSummaries };
|
|
615
|
+
}
|
|
616
|
+
async summarize(agentId) {
|
|
617
|
+
if (!this.model) return;
|
|
618
|
+
const allRecent = await this.store.getRecentMessages(agentId, this.summarizeAfter);
|
|
619
|
+
if (allRecent.length === 0) return;
|
|
620
|
+
const summaryResponse = await this.model.chat([
|
|
621
|
+
{
|
|
622
|
+
role: "system",
|
|
623
|
+
content: "Summarize the following conversation concisely. Focus on key topics, decisions, and facts discussed.",
|
|
624
|
+
timestamp: Date.now()
|
|
625
|
+
},
|
|
626
|
+
...allRecent
|
|
627
|
+
]);
|
|
628
|
+
const summary = createSummary({
|
|
629
|
+
content: summaryResponse.content,
|
|
630
|
+
messageRange: {
|
|
631
|
+
from: allRecent[0].timestamp,
|
|
632
|
+
to: allRecent[allRecent.length - 1].timestamp
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
await this.store.saveSummary(agentId, summary);
|
|
636
|
+
}
|
|
637
|
+
close() {
|
|
638
|
+
if ("close" in this.store && typeof this.store.close === "function") {
|
|
639
|
+
this.store.close();
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
644
|
+
0 && (module.exports = {
|
|
645
|
+
Agent,
|
|
646
|
+
AgentEventEmitter,
|
|
647
|
+
InMemoryStore,
|
|
648
|
+
Memory,
|
|
649
|
+
MockAdapter,
|
|
650
|
+
OpenAICompatibleAdapter,
|
|
651
|
+
SQLiteStore,
|
|
652
|
+
Tool,
|
|
653
|
+
createMessage,
|
|
654
|
+
createSummary,
|
|
655
|
+
isToolCallMessage
|
|
656
|
+
});
|
|
657
|
+
//# sourceMappingURL=index.cjs.map
|