@gendive/chatllm 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/dist/index.d.mts +1099 -0
- package/dist/index.d.ts +1099 -0
- package/dist/index.js +2199 -0
- package/dist/index.mjs +2058 -0
- package/package.json +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2199 @@
|
|
|
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
|
+
BaseProvider: () => BaseProvider,
|
|
24
|
+
ChatLLMError: () => ChatLLMError,
|
|
25
|
+
ConfigurationError: () => ConfigurationError,
|
|
26
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
27
|
+
DEFAULT_OLLAMA_BASE_URL: () => DEFAULT_OLLAMA_BASE_URL,
|
|
28
|
+
DEFAULT_PERSONALIZATION: () => DEFAULT_PERSONALIZATION,
|
|
29
|
+
DEFAULT_STORAGE_PATH: () => DEFAULT_STORAGE_PATH,
|
|
30
|
+
FileStorage: () => FileStorage,
|
|
31
|
+
MemoryStorage: () => MemoryStorage,
|
|
32
|
+
OllamaProvider: () => OllamaProvider,
|
|
33
|
+
ProviderError: () => ProviderError,
|
|
34
|
+
ProviderRegistry: () => ProviderRegistry,
|
|
35
|
+
StorageError: () => StorageError,
|
|
36
|
+
StreamError: () => StreamError,
|
|
37
|
+
ToolExecutionError: () => ToolExecutionError,
|
|
38
|
+
ValidationError: () => ValidationError,
|
|
39
|
+
addSessionMessage: () => addMessage,
|
|
40
|
+
addSessionMessages: () => addMessages,
|
|
41
|
+
analyzeMessages: () => analyzeMessages,
|
|
42
|
+
applySkill: () => applySkill,
|
|
43
|
+
applySkillToSystemPrompt: () => applySkillToSystemPrompt,
|
|
44
|
+
buildSystemPromptWithMemory: () => buildSystemPromptWithMemory,
|
|
45
|
+
clearChatMemory: () => clearChatMemory,
|
|
46
|
+
clearGlobalMemory: () => clearGlobalMemory,
|
|
47
|
+
clearLearnedSkills: () => clearLearnedSkills,
|
|
48
|
+
clearMessages: () => clearMessages2,
|
|
49
|
+
clearObserverState: () => clearObserverState,
|
|
50
|
+
clearSessionMessages: () => clearMessages,
|
|
51
|
+
clearSkillApplicationHistory: () => clearSkillApplicationHistory,
|
|
52
|
+
cloneMessage: () => cloneMessage,
|
|
53
|
+
collectStreamContent: () => collectStreamContent,
|
|
54
|
+
createAssistantMessage: () => createAssistantMessage,
|
|
55
|
+
createChat: () => createChat,
|
|
56
|
+
createMessage: () => createMessage,
|
|
57
|
+
createSession: () => createSession,
|
|
58
|
+
createStreamEvent: () => createStreamEvent,
|
|
59
|
+
createSystemMessage: () => createSystemMessage,
|
|
60
|
+
createToolMessage: () => createToolMessage,
|
|
61
|
+
createUserMessage: () => createUserMessage,
|
|
62
|
+
deleteChatMemory: () => deleteChatMemory,
|
|
63
|
+
deleteGlobalMemory: () => deleteGlobalMemory,
|
|
64
|
+
deleteLearnedSkill: () => deleteLearnedSkill,
|
|
65
|
+
deserializeSession: () => deserializeSession,
|
|
66
|
+
destroyChat: () => destroyChat,
|
|
67
|
+
disableObserver: () => disableObserver,
|
|
68
|
+
enableObserver: () => enableObserver,
|
|
69
|
+
filterByProvider: () => filterByProvider,
|
|
70
|
+
filterByRole: () => filterByRole,
|
|
71
|
+
findRelevantSkills: () => findRelevantSkills,
|
|
72
|
+
formatMessagesForDisplay: () => formatMessagesForDisplay,
|
|
73
|
+
getBestSkill: () => getBestSkill,
|
|
74
|
+
getChatMemory: () => getChatMemory,
|
|
75
|
+
getChatMemoryContext: () => getChatMemoryContext,
|
|
76
|
+
getChatMemoryKeys: () => getChatMemoryKeys,
|
|
77
|
+
getDefaultModel: () => getDefaultModel,
|
|
78
|
+
getGlobalMemory: () => getGlobalMemory,
|
|
79
|
+
getGlobalMemoryContext: () => getGlobalMemoryContext,
|
|
80
|
+
getGlobalMemoryKeys: () => getGlobalMemoryKeys,
|
|
81
|
+
getGlobalMemoryStore: () => getGlobalMemoryStore,
|
|
82
|
+
getLastMessageByRole: () => getLastMessageByRole,
|
|
83
|
+
getLastMessages: () => getLastMessages,
|
|
84
|
+
getLearnedSkill: () => getLearnedSkill,
|
|
85
|
+
getLearnedSkills: () => getLearnedSkills,
|
|
86
|
+
getMemoryContext: () => getMemoryContext,
|
|
87
|
+
getMessages: () => getMessages,
|
|
88
|
+
getMessagesAfter: () => getMessagesAfter,
|
|
89
|
+
getMessagesForProvider: () => getMessagesForProvider,
|
|
90
|
+
getObservedPreferences: () => getObservedPreferences,
|
|
91
|
+
getObservedTasks: () => getObservedTasks,
|
|
92
|
+
getObserverConfig: () => getObserverConfig,
|
|
93
|
+
getObserverState: () => getObserverState,
|
|
94
|
+
getProviderBaseUrl: () => getProviderBaseUrl,
|
|
95
|
+
getSession: () => getSession,
|
|
96
|
+
getSessionSummary: () => getSessionSummary,
|
|
97
|
+
getSkillApplicationHistory: () => getSkillApplicationHistory,
|
|
98
|
+
getTools: () => getTools,
|
|
99
|
+
getTotalContentLength: () => getTotalContentLength,
|
|
100
|
+
hasChatMemory: () => hasChatMemory,
|
|
101
|
+
hasGlobalMemory: () => hasGlobalMemory,
|
|
102
|
+
initChatMemory: () => initChatMemory,
|
|
103
|
+
initGlobalMemory: () => initGlobalMemory,
|
|
104
|
+
initLearner: () => initLearner,
|
|
105
|
+
initMemory: () => initMemory,
|
|
106
|
+
isObserverEnabled: () => isObserverEnabled,
|
|
107
|
+
isProviderError: () => isProviderError,
|
|
108
|
+
isStorageError: () => isStorageError,
|
|
109
|
+
isToolExecutionError: () => isToolExecutionError,
|
|
110
|
+
learnFromConversation: () => learnFromConversation,
|
|
111
|
+
mergeConfig: () => mergeConfig,
|
|
112
|
+
onObserverEvent: () => onObserverEvent,
|
|
113
|
+
parseNDJSONStream: () => parseNDJSONStream,
|
|
114
|
+
parseSSEStream: () => parseSSEStream,
|
|
115
|
+
processMessages: () => processMessages,
|
|
116
|
+
recordSkillApplication: () => recordSkillApplication,
|
|
117
|
+
recordSkillUsage: () => recordSkillUsage,
|
|
118
|
+
registerTool: () => registerTool,
|
|
119
|
+
resetChat: () => resetChat,
|
|
120
|
+
searchLearnedSkills: () => searchLearnedSkills,
|
|
121
|
+
sendMessage: () => sendMessage,
|
|
122
|
+
sendMessageStream: () => sendMessageStream,
|
|
123
|
+
serializeSession: () => serializeSession,
|
|
124
|
+
setChatMemory: () => setChatMemory,
|
|
125
|
+
setGlobalMemory: () => setGlobalMemory,
|
|
126
|
+
setSessionMetadata: () => setSessionMetadata,
|
|
127
|
+
setSessionSystemPrompt: () => setSystemPrompt,
|
|
128
|
+
setSystemPrompt: () => setSystemPrompt2,
|
|
129
|
+
suggestSkills: () => suggestSkills,
|
|
130
|
+
syncChatMemory: () => syncChatMemory,
|
|
131
|
+
trackSkillApplication: () => trackSkillApplication,
|
|
132
|
+
truncateMessages: () => truncateMessages,
|
|
133
|
+
unregisterTool: () => unregisterTool,
|
|
134
|
+
updateSkillApplicationSuccess: () => updateSkillApplicationSuccess,
|
|
135
|
+
updateTaskStatus: () => updateTaskStatus,
|
|
136
|
+
validateProviderConfig: () => validateProviderConfig,
|
|
137
|
+
wrapProviderError: () => wrapProviderError
|
|
138
|
+
});
|
|
139
|
+
module.exports = __toCommonJS(index_exports);
|
|
140
|
+
|
|
141
|
+
// src/types.ts
|
|
142
|
+
var DEFAULT_PERSONALIZATION = {
|
|
143
|
+
responseStyle: {
|
|
144
|
+
warmth: "medium",
|
|
145
|
+
enthusiasm: "medium",
|
|
146
|
+
emojiUsage: "low",
|
|
147
|
+
formatting: "default",
|
|
148
|
+
verbosity: "balanced"
|
|
149
|
+
},
|
|
150
|
+
userProfile: {},
|
|
151
|
+
useMemory: true,
|
|
152
|
+
language: "auto"
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/providers/base.ts
|
|
156
|
+
var BaseProvider = class {
|
|
157
|
+
constructor(config) {
|
|
158
|
+
this.config = config;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Generate a unique message ID
|
|
162
|
+
*/
|
|
163
|
+
generateMessageId() {
|
|
164
|
+
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
var ProviderRegistry = class {
|
|
168
|
+
providers = /* @__PURE__ */ new Map();
|
|
169
|
+
register(provider) {
|
|
170
|
+
this.providers.set(provider.name, provider);
|
|
171
|
+
}
|
|
172
|
+
get(name) {
|
|
173
|
+
return this.providers.get(name);
|
|
174
|
+
}
|
|
175
|
+
has(name) {
|
|
176
|
+
return this.providers.has(name);
|
|
177
|
+
}
|
|
178
|
+
getAll() {
|
|
179
|
+
return this.providers;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/config.ts
|
|
184
|
+
var import_path = require("path");
|
|
185
|
+
var import_os = require("os");
|
|
186
|
+
var DEFAULT_STORAGE_PATH = (0, import_path.join)((0, import_os.homedir)(), ".devdive-chat");
|
|
187
|
+
var DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
188
|
+
var DEFAULT_MODELS = {
|
|
189
|
+
openai: "gpt-4",
|
|
190
|
+
anthropic: "claude-3-opus-20240229",
|
|
191
|
+
google: "gemini-pro",
|
|
192
|
+
naver: "HCX-003",
|
|
193
|
+
ollama: "llama2"
|
|
194
|
+
};
|
|
195
|
+
function validateProviderConfig(provider, config) {
|
|
196
|
+
const providerConfig = config.providers[provider];
|
|
197
|
+
if (!providerConfig) {
|
|
198
|
+
throw new Error(`Provider "${provider}" is not configured`);
|
|
199
|
+
}
|
|
200
|
+
switch (provider) {
|
|
201
|
+
case "openai":
|
|
202
|
+
if (!("apiKey" in providerConfig) || !providerConfig.apiKey) {
|
|
203
|
+
throw new Error("OpenAI provider requires apiKey");
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
case "anthropic":
|
|
207
|
+
if (!("apiKey" in providerConfig) || !providerConfig.apiKey) {
|
|
208
|
+
throw new Error("Anthropic provider requires apiKey");
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
case "google":
|
|
212
|
+
if (!("apiKey" in providerConfig) || !providerConfig.apiKey) {
|
|
213
|
+
throw new Error("Google provider requires apiKey");
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
case "naver":
|
|
217
|
+
if (!("apiKey" in providerConfig) || !providerConfig.apiKey || !("apiGatewayKey" in providerConfig) || !providerConfig.apiGatewayKey) {
|
|
218
|
+
throw new Error("Naver provider requires apiKey and apiGatewayKey");
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
case "ollama":
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function getProviderBaseUrl(provider, config) {
|
|
226
|
+
const providerConfig = config.providers[provider];
|
|
227
|
+
switch (provider) {
|
|
228
|
+
case "openai":
|
|
229
|
+
return providerConfig?.baseUrl ?? "https://api.openai.com/v1";
|
|
230
|
+
case "anthropic":
|
|
231
|
+
return providerConfig?.baseUrl ?? "https://api.anthropic.com";
|
|
232
|
+
case "google":
|
|
233
|
+
return "https://generativelanguage.googleapis.com";
|
|
234
|
+
case "naver":
|
|
235
|
+
return providerConfig?.baseUrl ?? "https://clovastudio.apigw.ntruss.com";
|
|
236
|
+
case "ollama":
|
|
237
|
+
return providerConfig?.baseUrl ?? DEFAULT_OLLAMA_BASE_URL;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function getDefaultModel(provider, config) {
|
|
241
|
+
return config.defaultModel ?? DEFAULT_MODELS[provider];
|
|
242
|
+
}
|
|
243
|
+
function mergeConfig(userConfig) {
|
|
244
|
+
return {
|
|
245
|
+
...userConfig,
|
|
246
|
+
storagePath: userConfig.storagePath ?? DEFAULT_STORAGE_PATH
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/utils/errors.ts
|
|
251
|
+
var ChatLLMError = class extends Error {
|
|
252
|
+
constructor(message) {
|
|
253
|
+
super(message);
|
|
254
|
+
this.name = "ChatLLMError";
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
var ProviderError = class extends ChatLLMError {
|
|
258
|
+
constructor(provider, message, statusCode, originalError) {
|
|
259
|
+
super(`[${provider}] ${message}`);
|
|
260
|
+
this.provider = provider;
|
|
261
|
+
this.statusCode = statusCode;
|
|
262
|
+
this.originalError = originalError;
|
|
263
|
+
this.name = "ProviderError";
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var ConfigurationError = class extends ChatLLMError {
|
|
267
|
+
constructor(message) {
|
|
268
|
+
super(message);
|
|
269
|
+
this.name = "ConfigurationError";
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
var ToolExecutionError = class extends ChatLLMError {
|
|
273
|
+
constructor(toolName, message, originalError) {
|
|
274
|
+
super(`Tool "${toolName}" execution failed: ${message}`);
|
|
275
|
+
this.toolName = toolName;
|
|
276
|
+
this.originalError = originalError;
|
|
277
|
+
this.name = "ToolExecutionError";
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var StorageError = class extends ChatLLMError {
|
|
281
|
+
constructor(operation, message, originalError) {
|
|
282
|
+
super(`Storage ${operation} failed: ${message}`);
|
|
283
|
+
this.operation = operation;
|
|
284
|
+
this.originalError = originalError;
|
|
285
|
+
this.name = "StorageError";
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
var StreamError = class extends ChatLLMError {
|
|
289
|
+
constructor(message, originalError) {
|
|
290
|
+
super(message);
|
|
291
|
+
this.originalError = originalError;
|
|
292
|
+
this.name = "StreamError";
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
var ValidationError = class extends ChatLLMError {
|
|
296
|
+
constructor(field, message) {
|
|
297
|
+
super(`Validation error for "${field}": ${message}`);
|
|
298
|
+
this.field = field;
|
|
299
|
+
this.name = "ValidationError";
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
function wrapProviderError(provider, error, statusCode) {
|
|
303
|
+
if (error instanceof ProviderError) {
|
|
304
|
+
return error;
|
|
305
|
+
}
|
|
306
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
307
|
+
const originalError = error instanceof Error ? error : void 0;
|
|
308
|
+
return new ProviderError(provider, message, statusCode, originalError);
|
|
309
|
+
}
|
|
310
|
+
function isProviderError(error) {
|
|
311
|
+
return error instanceof ProviderError;
|
|
312
|
+
}
|
|
313
|
+
function isToolExecutionError(error) {
|
|
314
|
+
return error instanceof ToolExecutionError;
|
|
315
|
+
}
|
|
316
|
+
function isStorageError(error) {
|
|
317
|
+
return error instanceof StorageError;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/utils/stream.ts
|
|
321
|
+
async function* parseSSEStream(response) {
|
|
322
|
+
const reader = response.body?.getReader();
|
|
323
|
+
if (!reader) {
|
|
324
|
+
throw new StreamError("Response body is not readable");
|
|
325
|
+
}
|
|
326
|
+
const decoder = new TextDecoder();
|
|
327
|
+
let buffer = "";
|
|
328
|
+
try {
|
|
329
|
+
while (true) {
|
|
330
|
+
const { done, value } = await reader.read();
|
|
331
|
+
if (done) break;
|
|
332
|
+
buffer += decoder.decode(value, { stream: true });
|
|
333
|
+
const lines = buffer.split("\n");
|
|
334
|
+
buffer = lines.pop() || "";
|
|
335
|
+
for (const line of lines) {
|
|
336
|
+
const trimmed = line.trim();
|
|
337
|
+
if (trimmed.startsWith("data: ")) {
|
|
338
|
+
const data = trimmed.slice(6);
|
|
339
|
+
if (data !== "[DONE]") {
|
|
340
|
+
yield data;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (buffer.trim()) {
|
|
346
|
+
const trimmed = buffer.trim();
|
|
347
|
+
if (trimmed.startsWith("data: ")) {
|
|
348
|
+
const data = trimmed.slice(6);
|
|
349
|
+
if (data !== "[DONE]") {
|
|
350
|
+
yield data;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} finally {
|
|
355
|
+
reader.releaseLock();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async function* parseNDJSONStream(response) {
|
|
359
|
+
const reader = response.body?.getReader();
|
|
360
|
+
if (!reader) {
|
|
361
|
+
throw new StreamError("Response body is not readable");
|
|
362
|
+
}
|
|
363
|
+
const decoder = new TextDecoder();
|
|
364
|
+
let buffer = "";
|
|
365
|
+
try {
|
|
366
|
+
while (true) {
|
|
367
|
+
const { done, value } = await reader.read();
|
|
368
|
+
if (done) break;
|
|
369
|
+
buffer += decoder.decode(value, { stream: true });
|
|
370
|
+
const lines = buffer.split("\n");
|
|
371
|
+
buffer = lines.pop() || "";
|
|
372
|
+
for (const line of lines) {
|
|
373
|
+
const trimmed = line.trim();
|
|
374
|
+
if (trimmed) {
|
|
375
|
+
try {
|
|
376
|
+
yield JSON.parse(trimmed);
|
|
377
|
+
} catch {
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (buffer.trim()) {
|
|
383
|
+
try {
|
|
384
|
+
yield JSON.parse(buffer.trim());
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} finally {
|
|
389
|
+
reader.releaseLock();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function createStreamEvent(type, data) {
|
|
393
|
+
return {
|
|
394
|
+
type,
|
|
395
|
+
...data
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
async function collectStreamContent(stream) {
|
|
399
|
+
let content = "";
|
|
400
|
+
for await (const event of stream) {
|
|
401
|
+
if (event.type === "text" && event.content) {
|
|
402
|
+
content += event.content;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return content;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/providers/ollama.ts
|
|
409
|
+
var OllamaProvider = class extends BaseProvider {
|
|
410
|
+
name = "ollama";
|
|
411
|
+
baseUrl;
|
|
412
|
+
constructor(config) {
|
|
413
|
+
super(config);
|
|
414
|
+
this.baseUrl = getProviderBaseUrl("ollama", config);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Send a message and get a complete response
|
|
418
|
+
*/
|
|
419
|
+
async sendMessage(messages, options) {
|
|
420
|
+
const formattedMessages = this.formatMessages(messages);
|
|
421
|
+
const tools = options.tools ? this.formatTools(options.tools) : void 0;
|
|
422
|
+
const body = this.buildRequestBody(formattedMessages, options, tools);
|
|
423
|
+
try {
|
|
424
|
+
const response = await fetch(this.getEndpoint(options.model || "llama2"), {
|
|
425
|
+
method: "POST",
|
|
426
|
+
headers: this.getHeaders(),
|
|
427
|
+
body: JSON.stringify(body)
|
|
428
|
+
});
|
|
429
|
+
if (!response.ok) {
|
|
430
|
+
const errorText = await response.text();
|
|
431
|
+
throw wrapProviderError("ollama", errorText, response.status);
|
|
432
|
+
}
|
|
433
|
+
const data = await response.json();
|
|
434
|
+
return this.parseResponse(data);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
throw wrapProviderError("ollama", error);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Send a message and stream the response
|
|
441
|
+
*/
|
|
442
|
+
async *sendMessageStream(messages, options) {
|
|
443
|
+
const formattedMessages = this.formatMessages(messages);
|
|
444
|
+
const tools = options.tools ? this.formatTools(options.tools) : void 0;
|
|
445
|
+
const body = this.buildRequestBody(formattedMessages, options, tools);
|
|
446
|
+
body.stream = true;
|
|
447
|
+
try {
|
|
448
|
+
const response = await fetch(this.getEndpoint(options.model || "llama2"), {
|
|
449
|
+
method: "POST",
|
|
450
|
+
headers: this.getHeaders(),
|
|
451
|
+
body: JSON.stringify(body)
|
|
452
|
+
});
|
|
453
|
+
if (!response.ok) {
|
|
454
|
+
const errorText = await response.text();
|
|
455
|
+
yield createStreamEvent("error", {
|
|
456
|
+
error: wrapProviderError("ollama", errorText, response.status)
|
|
457
|
+
});
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
let fullContent = "";
|
|
461
|
+
const toolCalls = [];
|
|
462
|
+
for await (const chunk of parseNDJSONStream(response)) {
|
|
463
|
+
const data = chunk;
|
|
464
|
+
if (data.message?.content) {
|
|
465
|
+
fullContent += data.message.content;
|
|
466
|
+
yield createStreamEvent("text", { content: data.message.content });
|
|
467
|
+
}
|
|
468
|
+
if (data.message?.tool_calls) {
|
|
469
|
+
for (const tc of data.message.tool_calls) {
|
|
470
|
+
const toolCall = {
|
|
471
|
+
id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
472
|
+
name: tc.function.name,
|
|
473
|
+
arguments: tc.function.arguments
|
|
474
|
+
};
|
|
475
|
+
toolCalls.push(toolCall);
|
|
476
|
+
yield createStreamEvent("tool_call", { toolCall });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (data.done) {
|
|
480
|
+
const message = {
|
|
481
|
+
id: this.generateMessageId(),
|
|
482
|
+
role: "assistant",
|
|
483
|
+
content: fullContent,
|
|
484
|
+
provider: "ollama",
|
|
485
|
+
model: data.model,
|
|
486
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
487
|
+
timestamp: Date.now()
|
|
488
|
+
};
|
|
489
|
+
yield createStreamEvent("done", { message });
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
} catch (error) {
|
|
494
|
+
yield createStreamEvent("error", {
|
|
495
|
+
error: wrapProviderError("ollama", error)
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Convert internal message format to Ollama format
|
|
501
|
+
*/
|
|
502
|
+
formatMessages(messages) {
|
|
503
|
+
return messages.map((msg) => {
|
|
504
|
+
const formatted = {
|
|
505
|
+
role: msg.role,
|
|
506
|
+
content: msg.content
|
|
507
|
+
};
|
|
508
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
509
|
+
formatted.tool_calls = msg.toolCalls.map((tc) => ({
|
|
510
|
+
function: {
|
|
511
|
+
name: tc.name,
|
|
512
|
+
arguments: tc.arguments
|
|
513
|
+
}
|
|
514
|
+
}));
|
|
515
|
+
}
|
|
516
|
+
return formatted;
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Convert tool definitions to Ollama format
|
|
521
|
+
*/
|
|
522
|
+
formatTools(tools) {
|
|
523
|
+
return tools.map((tool) => ({
|
|
524
|
+
type: "function",
|
|
525
|
+
function: {
|
|
526
|
+
name: tool.name,
|
|
527
|
+
description: tool.description,
|
|
528
|
+
parameters: {
|
|
529
|
+
type: "object",
|
|
530
|
+
properties: tool.parameters.properties,
|
|
531
|
+
required: tool.parameters.required
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}));
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Parse Ollama response into internal message format
|
|
538
|
+
*/
|
|
539
|
+
parseResponse(response) {
|
|
540
|
+
const toolCalls = [];
|
|
541
|
+
if (response.message.tool_calls) {
|
|
542
|
+
for (const tc of response.message.tool_calls) {
|
|
543
|
+
toolCalls.push({
|
|
544
|
+
id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
545
|
+
name: tc.function.name,
|
|
546
|
+
arguments: tc.function.arguments
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
id: this.generateMessageId(),
|
|
552
|
+
role: "assistant",
|
|
553
|
+
content: response.message.content,
|
|
554
|
+
provider: "ollama",
|
|
555
|
+
model: response.model,
|
|
556
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
557
|
+
timestamp: Date.now()
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Get the Ollama API endpoint
|
|
562
|
+
*/
|
|
563
|
+
getEndpoint(_model) {
|
|
564
|
+
return `${this.baseUrl}/api/chat`;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Get headers for Ollama API requests
|
|
568
|
+
*/
|
|
569
|
+
getHeaders() {
|
|
570
|
+
return {
|
|
571
|
+
"Content-Type": "application/json"
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Build request body for Ollama API
|
|
576
|
+
*/
|
|
577
|
+
buildRequestBody(messages, options, tools) {
|
|
578
|
+
const body = {
|
|
579
|
+
model: options.model || "llama2",
|
|
580
|
+
messages,
|
|
581
|
+
stream: false
|
|
582
|
+
};
|
|
583
|
+
if (options.temperature !== void 0 || options.maxTokens !== void 0) {
|
|
584
|
+
body.options = {};
|
|
585
|
+
if (options.temperature !== void 0) {
|
|
586
|
+
body.options.temperature = options.temperature;
|
|
587
|
+
}
|
|
588
|
+
if (options.maxTokens !== void 0) {
|
|
589
|
+
body.options.num_predict = options.maxTokens;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (tools && tools.length > 0) {
|
|
593
|
+
body.tools = tools;
|
|
594
|
+
}
|
|
595
|
+
return body;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* List available models from Ollama
|
|
599
|
+
*/
|
|
600
|
+
async listModels() {
|
|
601
|
+
try {
|
|
602
|
+
const response = await fetch(`${this.baseUrl}/api/tags`, {
|
|
603
|
+
method: "GET",
|
|
604
|
+
headers: this.getHeaders()
|
|
605
|
+
});
|
|
606
|
+
if (!response.ok) {
|
|
607
|
+
throw wrapProviderError("ollama", "Failed to list models", response.status);
|
|
608
|
+
}
|
|
609
|
+
const data = await response.json();
|
|
610
|
+
return data.models.map((m) => m.name);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
throw wrapProviderError("ollama", error);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Check if Ollama server is available
|
|
617
|
+
*/
|
|
618
|
+
async isAvailable() {
|
|
619
|
+
try {
|
|
620
|
+
const response = await fetch(`${this.baseUrl}/api/tags`, {
|
|
621
|
+
method: "GET",
|
|
622
|
+
headers: this.getHeaders()
|
|
623
|
+
});
|
|
624
|
+
return response.ok;
|
|
625
|
+
} catch {
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// src/core/message.ts
|
|
632
|
+
function generateMessageId() {
|
|
633
|
+
return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
634
|
+
}
|
|
635
|
+
function createMessage(role, content, options) {
|
|
636
|
+
return {
|
|
637
|
+
id: generateMessageId(),
|
|
638
|
+
role,
|
|
639
|
+
content,
|
|
640
|
+
provider: options?.provider,
|
|
641
|
+
model: options?.model,
|
|
642
|
+
toolCalls: options?.toolCalls,
|
|
643
|
+
toolCallId: options?.toolCallId,
|
|
644
|
+
timestamp: Date.now(),
|
|
645
|
+
metadata: options?.metadata
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function createUserMessage(content) {
|
|
649
|
+
return createMessage("user", content);
|
|
650
|
+
}
|
|
651
|
+
function createAssistantMessage(content, provider, model, toolCalls) {
|
|
652
|
+
return createMessage("assistant", content, { provider, model, toolCalls });
|
|
653
|
+
}
|
|
654
|
+
function createSystemMessage(content) {
|
|
655
|
+
return createMessage("system", content);
|
|
656
|
+
}
|
|
657
|
+
function createToolMessage(content, toolCallId) {
|
|
658
|
+
return createMessage("tool", content, { toolCallId });
|
|
659
|
+
}
|
|
660
|
+
function cloneMessage(message, overrides) {
|
|
661
|
+
return {
|
|
662
|
+
...message,
|
|
663
|
+
...overrides,
|
|
664
|
+
id: overrides?.id ?? generateMessageId(),
|
|
665
|
+
timestamp: overrides?.timestamp ?? Date.now()
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function filterByRole(messages, role) {
|
|
669
|
+
return messages.filter((m) => m.role === role);
|
|
670
|
+
}
|
|
671
|
+
function filterByProvider(messages, provider) {
|
|
672
|
+
return messages.filter((m) => m.provider === provider);
|
|
673
|
+
}
|
|
674
|
+
function getLastMessageByRole(messages, role) {
|
|
675
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
676
|
+
if (messages[i].role === role) {
|
|
677
|
+
return messages[i];
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return void 0;
|
|
681
|
+
}
|
|
682
|
+
function getTotalContentLength(messages) {
|
|
683
|
+
return messages.reduce((sum, msg) => sum + msg.content.length, 0);
|
|
684
|
+
}
|
|
685
|
+
function truncateMessages(messages, maxTokens, preserveSystem = true) {
|
|
686
|
+
const maxChars = maxTokens * 4;
|
|
687
|
+
const result = [];
|
|
688
|
+
let currentLength = 0;
|
|
689
|
+
if (preserveSystem) {
|
|
690
|
+
for (const msg of messages) {
|
|
691
|
+
if (msg.role === "system") {
|
|
692
|
+
result.push(msg);
|
|
693
|
+
currentLength += msg.content.length;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
698
|
+
for (let i = nonSystemMessages.length - 1; i >= 0; i--) {
|
|
699
|
+
const msg = nonSystemMessages[i];
|
|
700
|
+
if (currentLength + msg.content.length <= maxChars) {
|
|
701
|
+
result.unshift(msg);
|
|
702
|
+
currentLength += msg.content.length;
|
|
703
|
+
} else {
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return result.sort((a, b) => {
|
|
708
|
+
if (a.role === "system" && b.role !== "system") return -1;
|
|
709
|
+
if (a.role !== "system" && b.role === "system") return 1;
|
|
710
|
+
return a.timestamp - b.timestamp;
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
function formatMessagesForDisplay(messages) {
|
|
714
|
+
return messages.map((msg) => {
|
|
715
|
+
const provider = msg.provider ? ` [${msg.provider}/${msg.model}]` : "";
|
|
716
|
+
const prefix = `[${msg.role.toUpperCase()}]${provider}`;
|
|
717
|
+
return `${prefix}: ${msg.content.substring(0, 100)}${msg.content.length > 100 ? "..." : ""}`;
|
|
718
|
+
}).join("\n");
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/core/session.ts
|
|
722
|
+
function generateSessionId() {
|
|
723
|
+
return `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
724
|
+
}
|
|
725
|
+
function createSession(options) {
|
|
726
|
+
const now = Date.now();
|
|
727
|
+
return {
|
|
728
|
+
id: options?.id ?? generateSessionId(),
|
|
729
|
+
messages: [],
|
|
730
|
+
systemPrompt: options?.systemPrompt,
|
|
731
|
+
createdAt: now,
|
|
732
|
+
updatedAt: now,
|
|
733
|
+
metadata: options?.metadata ?? {}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function addMessage(session, message) {
|
|
737
|
+
return {
|
|
738
|
+
...session,
|
|
739
|
+
messages: [...session.messages, message],
|
|
740
|
+
updatedAt: Date.now()
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function addMessages(session, messages) {
|
|
744
|
+
return {
|
|
745
|
+
...session,
|
|
746
|
+
messages: [...session.messages, ...messages],
|
|
747
|
+
updatedAt: Date.now()
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
function getMessagesForProvider(session, _provider) {
|
|
751
|
+
const messages = [];
|
|
752
|
+
if (session.systemPrompt) {
|
|
753
|
+
messages.push(createSystemMessage(session.systemPrompt));
|
|
754
|
+
}
|
|
755
|
+
messages.push(...session.messages);
|
|
756
|
+
return messages;
|
|
757
|
+
}
|
|
758
|
+
function setSystemPrompt(session, prompt) {
|
|
759
|
+
return {
|
|
760
|
+
...session,
|
|
761
|
+
systemPrompt: prompt,
|
|
762
|
+
updatedAt: Date.now()
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
function clearMessages(session) {
|
|
766
|
+
return {
|
|
767
|
+
...session,
|
|
768
|
+
messages: [],
|
|
769
|
+
updatedAt: Date.now()
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
function getLastMessages(session, count) {
|
|
773
|
+
return session.messages.slice(-count);
|
|
774
|
+
}
|
|
775
|
+
function getMessagesAfter(session, timestamp) {
|
|
776
|
+
return session.messages.filter((m) => m.timestamp > timestamp);
|
|
777
|
+
}
|
|
778
|
+
function getSessionSummary(session) {
|
|
779
|
+
const providers = /* @__PURE__ */ new Set();
|
|
780
|
+
let firstMessageAt = null;
|
|
781
|
+
let lastMessageAt = null;
|
|
782
|
+
for (const msg of session.messages) {
|
|
783
|
+
if (msg.provider) {
|
|
784
|
+
providers.add(msg.provider);
|
|
785
|
+
}
|
|
786
|
+
if (firstMessageAt === null || msg.timestamp < firstMessageAt) {
|
|
787
|
+
firstMessageAt = msg.timestamp;
|
|
788
|
+
}
|
|
789
|
+
if (lastMessageAt === null || msg.timestamp > lastMessageAt) {
|
|
790
|
+
lastMessageAt = msg.timestamp;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return {
|
|
794
|
+
id: session.id,
|
|
795
|
+
messageCount: session.messages.length,
|
|
796
|
+
providers: Array.from(providers),
|
|
797
|
+
firstMessageAt,
|
|
798
|
+
lastMessageAt
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
function setSessionMetadata(session, key, value) {
|
|
802
|
+
return {
|
|
803
|
+
...session,
|
|
804
|
+
metadata: {
|
|
805
|
+
...session.metadata,
|
|
806
|
+
[key]: value
|
|
807
|
+
},
|
|
808
|
+
updatedAt: Date.now()
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
function serializeSession(session) {
|
|
812
|
+
return {
|
|
813
|
+
id: session.id,
|
|
814
|
+
messages: session.messages,
|
|
815
|
+
systemPrompt: session.systemPrompt,
|
|
816
|
+
createdAt: session.createdAt,
|
|
817
|
+
updatedAt: session.updatedAt,
|
|
818
|
+
metadata: session.metadata
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function deserializeSession(data) {
|
|
822
|
+
return {
|
|
823
|
+
id: data.id,
|
|
824
|
+
messages: data.messages,
|
|
825
|
+
systemPrompt: data.systemPrompt,
|
|
826
|
+
createdAt: data.createdAt,
|
|
827
|
+
updatedAt: data.updatedAt,
|
|
828
|
+
metadata: data.metadata ?? {}
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// src/core/chat.ts
|
|
833
|
+
function generateChatId() {
|
|
834
|
+
return `chat_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
835
|
+
}
|
|
836
|
+
var chatStates = /* @__PURE__ */ new Map();
|
|
837
|
+
function createChat(config) {
|
|
838
|
+
const mergedConfig = mergeConfig(config);
|
|
839
|
+
const chatId = config.id ?? generateChatId();
|
|
840
|
+
const providerRegistry = new ProviderRegistry();
|
|
841
|
+
if (config.providers.ollama !== void 0 || config.defaultProvider === "ollama") {
|
|
842
|
+
providerRegistry.register(new OllamaProvider(mergedConfig));
|
|
843
|
+
}
|
|
844
|
+
if (!providerRegistry.has(config.defaultProvider)) {
|
|
845
|
+
if (config.defaultProvider === "ollama") {
|
|
846
|
+
providerRegistry.register(new OllamaProvider(mergedConfig));
|
|
847
|
+
} else {
|
|
848
|
+
throw new ConfigurationError(
|
|
849
|
+
`Default provider "${config.defaultProvider}" is not configured`
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const now = Date.now();
|
|
854
|
+
const chat = {
|
|
855
|
+
id: chatId,
|
|
856
|
+
config: mergedConfig,
|
|
857
|
+
messages: [],
|
|
858
|
+
tools: /* @__PURE__ */ new Map(),
|
|
859
|
+
memory: /* @__PURE__ */ new Map(),
|
|
860
|
+
createdAt: now,
|
|
861
|
+
updatedAt: now
|
|
862
|
+
};
|
|
863
|
+
const session = createSession({
|
|
864
|
+
systemPrompt: config.systemPrompt
|
|
865
|
+
});
|
|
866
|
+
chatStates.set(chatId, {
|
|
867
|
+
chat,
|
|
868
|
+
session,
|
|
869
|
+
providerRegistry
|
|
870
|
+
});
|
|
871
|
+
return chat;
|
|
872
|
+
}
|
|
873
|
+
function getChatState(chat) {
|
|
874
|
+
const state = chatStates.get(chat.id);
|
|
875
|
+
if (!state) {
|
|
876
|
+
throw new ConfigurationError(`Chat "${chat.id}" not found`);
|
|
877
|
+
}
|
|
878
|
+
return state;
|
|
879
|
+
}
|
|
880
|
+
function getProvider(chat, providerType) {
|
|
881
|
+
const state = getChatState(chat);
|
|
882
|
+
const type = providerType ?? chat.config.defaultProvider;
|
|
883
|
+
const provider = state.providerRegistry.get(type);
|
|
884
|
+
if (!provider) {
|
|
885
|
+
throw new ConfigurationError(`Provider "${type}" is not available`);
|
|
886
|
+
}
|
|
887
|
+
return provider;
|
|
888
|
+
}
|
|
889
|
+
async function sendMessage(chat, content, options) {
|
|
890
|
+
const state = getChatState(chat);
|
|
891
|
+
const provider = getProvider(chat, options?.provider);
|
|
892
|
+
const model = options?.model ?? getDefaultModel(provider.name, chat.config);
|
|
893
|
+
const userMessage = createUserMessage(content);
|
|
894
|
+
state.session = addMessage(state.session, userMessage);
|
|
895
|
+
chat.messages.push(userMessage);
|
|
896
|
+
const messages = getMessagesForProvider(state.session, provider.name);
|
|
897
|
+
const tools = options?.tools ?? Array.from(chat.tools.values());
|
|
898
|
+
const sendOptions = {
|
|
899
|
+
...options,
|
|
900
|
+
model,
|
|
901
|
+
tools: tools.length > 0 ? tools : void 0
|
|
902
|
+
};
|
|
903
|
+
const response = await provider.sendMessage(messages, sendOptions);
|
|
904
|
+
state.session = addMessage(state.session, response);
|
|
905
|
+
chat.messages.push(response);
|
|
906
|
+
chat.updatedAt = Date.now();
|
|
907
|
+
if (response.toolCalls && response.toolCalls.length > 0) {
|
|
908
|
+
return await handleToolCalls(chat, response, sendOptions);
|
|
909
|
+
}
|
|
910
|
+
return response;
|
|
911
|
+
}
|
|
912
|
+
async function* sendMessageStream(chat, content, options) {
|
|
913
|
+
const state = getChatState(chat);
|
|
914
|
+
const provider = getProvider(chat, options?.provider);
|
|
915
|
+
const model = options?.model ?? getDefaultModel(provider.name, chat.config);
|
|
916
|
+
const userMessage = createUserMessage(content);
|
|
917
|
+
state.session = addMessage(state.session, userMessage);
|
|
918
|
+
chat.messages.push(userMessage);
|
|
919
|
+
const messages = getMessagesForProvider(state.session, provider.name);
|
|
920
|
+
const tools = options?.tools ?? Array.from(chat.tools.values());
|
|
921
|
+
const sendOptions = {
|
|
922
|
+
...options,
|
|
923
|
+
model,
|
|
924
|
+
tools: tools.length > 0 ? tools : void 0
|
|
925
|
+
};
|
|
926
|
+
for await (const event of provider.sendMessageStream(messages, sendOptions)) {
|
|
927
|
+
yield event;
|
|
928
|
+
if (event.type === "done" && event.message) {
|
|
929
|
+
state.session = addMessage(state.session, event.message);
|
|
930
|
+
chat.messages.push(event.message);
|
|
931
|
+
chat.updatedAt = Date.now();
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
async function handleToolCalls(chat, response, options) {
|
|
936
|
+
const state = getChatState(chat);
|
|
937
|
+
const provider = getProvider(chat, options.provider);
|
|
938
|
+
if (!response.toolCalls) {
|
|
939
|
+
return response;
|
|
940
|
+
}
|
|
941
|
+
for (const toolCall of response.toolCalls) {
|
|
942
|
+
const tool = chat.tools.get(toolCall.name);
|
|
943
|
+
if (!tool) {
|
|
944
|
+
const errorMessage = createAssistantMessage(
|
|
945
|
+
`Tool "${toolCall.name}" not found`,
|
|
946
|
+
provider.name,
|
|
947
|
+
options.model
|
|
948
|
+
);
|
|
949
|
+
state.session = addMessage(state.session, errorMessage);
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
try {
|
|
953
|
+
const result = await tool.handler(toolCall.arguments);
|
|
954
|
+
const toolResultMessage = {
|
|
955
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
956
|
+
role: "tool",
|
|
957
|
+
content: JSON.stringify(result),
|
|
958
|
+
toolCallId: toolCall.id,
|
|
959
|
+
timestamp: Date.now()
|
|
960
|
+
};
|
|
961
|
+
state.session = addMessage(state.session, toolResultMessage);
|
|
962
|
+
chat.messages.push(toolResultMessage);
|
|
963
|
+
} catch (error) {
|
|
964
|
+
const errorContent = error instanceof Error ? error.message : String(error);
|
|
965
|
+
const toolErrorMessage = {
|
|
966
|
+
id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
967
|
+
role: "tool",
|
|
968
|
+
content: JSON.stringify({ error: errorContent }),
|
|
969
|
+
toolCallId: toolCall.id,
|
|
970
|
+
timestamp: Date.now()
|
|
971
|
+
};
|
|
972
|
+
state.session = addMessage(state.session, toolErrorMessage);
|
|
973
|
+
chat.messages.push(toolErrorMessage);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
const messages = getMessagesForProvider(state.session, provider.name);
|
|
977
|
+
const finalResponse = await provider.sendMessage(messages, options);
|
|
978
|
+
state.session = addMessage(state.session, finalResponse);
|
|
979
|
+
chat.messages.push(finalResponse);
|
|
980
|
+
chat.updatedAt = Date.now();
|
|
981
|
+
if (finalResponse.toolCalls && finalResponse.toolCalls.length > 0) {
|
|
982
|
+
return handleToolCalls(chat, finalResponse, options);
|
|
983
|
+
}
|
|
984
|
+
return finalResponse;
|
|
985
|
+
}
|
|
986
|
+
function registerTool(chat, tool) {
|
|
987
|
+
chat.tools.set(tool.name, tool);
|
|
988
|
+
chat.updatedAt = Date.now();
|
|
989
|
+
}
|
|
990
|
+
function unregisterTool(chat, toolName) {
|
|
991
|
+
const deleted = chat.tools.delete(toolName);
|
|
992
|
+
if (deleted) {
|
|
993
|
+
chat.updatedAt = Date.now();
|
|
994
|
+
}
|
|
995
|
+
return deleted;
|
|
996
|
+
}
|
|
997
|
+
function getTools(chat) {
|
|
998
|
+
return Array.from(chat.tools.values());
|
|
999
|
+
}
|
|
1000
|
+
function setSystemPrompt2(chat, prompt) {
|
|
1001
|
+
const state = getChatState(chat);
|
|
1002
|
+
state.session.systemPrompt = prompt;
|
|
1003
|
+
chat.config.systemPrompt = prompt;
|
|
1004
|
+
chat.updatedAt = Date.now();
|
|
1005
|
+
}
|
|
1006
|
+
function getMessages(chat) {
|
|
1007
|
+
return [...chat.messages];
|
|
1008
|
+
}
|
|
1009
|
+
function clearMessages2(chat) {
|
|
1010
|
+
const state = getChatState(chat);
|
|
1011
|
+
chat.messages = [];
|
|
1012
|
+
state.session.messages = [];
|
|
1013
|
+
chat.updatedAt = Date.now();
|
|
1014
|
+
}
|
|
1015
|
+
function resetChat(chat) {
|
|
1016
|
+
const state = getChatState(chat);
|
|
1017
|
+
chat.messages = [];
|
|
1018
|
+
state.session.messages = [];
|
|
1019
|
+
chat.tools.clear();
|
|
1020
|
+
chat.memory.clear();
|
|
1021
|
+
const now = Date.now();
|
|
1022
|
+
chat.updatedAt = now;
|
|
1023
|
+
state.session.updatedAt = now;
|
|
1024
|
+
}
|
|
1025
|
+
function getSession(chat) {
|
|
1026
|
+
const state = getChatState(chat);
|
|
1027
|
+
return state.session;
|
|
1028
|
+
}
|
|
1029
|
+
function destroyChat(chat) {
|
|
1030
|
+
chatStates.delete(chat.id);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// src/memory/storage.ts
|
|
1034
|
+
var import_fs = require("fs");
|
|
1035
|
+
var import_path2 = require("path");
|
|
1036
|
+
var FileStorage = class {
|
|
1037
|
+
basePath;
|
|
1038
|
+
constructor(basePath) {
|
|
1039
|
+
this.basePath = basePath;
|
|
1040
|
+
this.ensureDirectory(basePath);
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Ensure a directory exists
|
|
1044
|
+
*/
|
|
1045
|
+
ensureDirectory(path) {
|
|
1046
|
+
if (!(0, import_fs.existsSync)(path)) {
|
|
1047
|
+
try {
|
|
1048
|
+
(0, import_fs.mkdirSync)(path, { recursive: true });
|
|
1049
|
+
} catch (error) {
|
|
1050
|
+
throw new StorageError(
|
|
1051
|
+
"write",
|
|
1052
|
+
`Failed to create directory: ${path}`,
|
|
1053
|
+
error instanceof Error ? error : void 0
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Get the full path for a key
|
|
1060
|
+
*/
|
|
1061
|
+
getFilePath(key) {
|
|
1062
|
+
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1063
|
+
return (0, import_path2.join)(this.basePath, `${sanitizedKey}.json`);
|
|
1064
|
+
}
|
|
1065
|
+
/**
|
|
1066
|
+
* Read data from storage
|
|
1067
|
+
*/
|
|
1068
|
+
read(key) {
|
|
1069
|
+
const filePath = this.getFilePath(key);
|
|
1070
|
+
if (!(0, import_fs.existsSync)(filePath)) {
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
try {
|
|
1074
|
+
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
|
|
1075
|
+
return JSON.parse(content);
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
throw new StorageError(
|
|
1078
|
+
"read",
|
|
1079
|
+
`Failed to read key: ${key}`,
|
|
1080
|
+
error instanceof Error ? error : void 0
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Write data to storage
|
|
1086
|
+
*/
|
|
1087
|
+
write(key, data) {
|
|
1088
|
+
const filePath = this.getFilePath(key);
|
|
1089
|
+
try {
|
|
1090
|
+
this.ensureDirectory((0, import_path2.dirname)(filePath));
|
|
1091
|
+
(0, import_fs.writeFileSync)(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
1092
|
+
} catch (error) {
|
|
1093
|
+
throw new StorageError(
|
|
1094
|
+
"write",
|
|
1095
|
+
`Failed to write key: ${key}`,
|
|
1096
|
+
error instanceof Error ? error : void 0
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Delete data from storage
|
|
1102
|
+
*/
|
|
1103
|
+
delete(key) {
|
|
1104
|
+
const filePath = this.getFilePath(key);
|
|
1105
|
+
if (!(0, import_fs.existsSync)(filePath)) {
|
|
1106
|
+
return false;
|
|
1107
|
+
}
|
|
1108
|
+
try {
|
|
1109
|
+
(0, import_fs.unlinkSync)(filePath);
|
|
1110
|
+
return true;
|
|
1111
|
+
} catch (error) {
|
|
1112
|
+
throw new StorageError(
|
|
1113
|
+
"delete",
|
|
1114
|
+
`Failed to delete key: ${key}`,
|
|
1115
|
+
error instanceof Error ? error : void 0
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Check if a key exists
|
|
1121
|
+
*/
|
|
1122
|
+
exists(key) {
|
|
1123
|
+
return (0, import_fs.existsSync)(this.getFilePath(key));
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Get the base path
|
|
1127
|
+
*/
|
|
1128
|
+
getBasePath() {
|
|
1129
|
+
return this.basePath;
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
var MemoryStorage = class {
|
|
1133
|
+
data = /* @__PURE__ */ new Map();
|
|
1134
|
+
read(key) {
|
|
1135
|
+
const value = this.data.get(key);
|
|
1136
|
+
return value !== void 0 ? value : null;
|
|
1137
|
+
}
|
|
1138
|
+
write(key, data) {
|
|
1139
|
+
this.data.set(key, data);
|
|
1140
|
+
}
|
|
1141
|
+
delete(key) {
|
|
1142
|
+
return this.data.delete(key);
|
|
1143
|
+
}
|
|
1144
|
+
exists(key) {
|
|
1145
|
+
return this.data.has(key);
|
|
1146
|
+
}
|
|
1147
|
+
clear() {
|
|
1148
|
+
this.data.clear();
|
|
1149
|
+
}
|
|
1150
|
+
getAll() {
|
|
1151
|
+
return new Map(this.data);
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
// src/memory/global.ts
|
|
1156
|
+
var import_path3 = require("path");
|
|
1157
|
+
var GlobalMemoryStore = class {
|
|
1158
|
+
entries = /* @__PURE__ */ new Map();
|
|
1159
|
+
storage;
|
|
1160
|
+
storageKey = "global_memory";
|
|
1161
|
+
autoSave;
|
|
1162
|
+
constructor(storagePath, autoSave = true) {
|
|
1163
|
+
this.autoSave = autoSave;
|
|
1164
|
+
if (storagePath) {
|
|
1165
|
+
this.storage = new FileStorage((0, import_path3.join)(storagePath, "memory"));
|
|
1166
|
+
this.load();
|
|
1167
|
+
} else {
|
|
1168
|
+
this.storage = new MemoryStorage();
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Load entries from storage
|
|
1173
|
+
*/
|
|
1174
|
+
load() {
|
|
1175
|
+
const data = this.storage.read(this.storageKey);
|
|
1176
|
+
if (data) {
|
|
1177
|
+
this.entries = new Map(Object.entries(data));
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Save entries to storage
|
|
1182
|
+
*/
|
|
1183
|
+
save() {
|
|
1184
|
+
if (!this.autoSave) return;
|
|
1185
|
+
const data = {};
|
|
1186
|
+
for (const [key, value] of this.entries) {
|
|
1187
|
+
data[key] = value;
|
|
1188
|
+
}
|
|
1189
|
+
this.storage.write(this.storageKey, data);
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Set a memory entry
|
|
1193
|
+
*/
|
|
1194
|
+
set(key, value) {
|
|
1195
|
+
const now = Date.now();
|
|
1196
|
+
const existing = this.entries.get(key);
|
|
1197
|
+
this.entries.set(key, {
|
|
1198
|
+
key,
|
|
1199
|
+
value,
|
|
1200
|
+
createdAt: existing?.createdAt ?? now,
|
|
1201
|
+
updatedAt: now
|
|
1202
|
+
});
|
|
1203
|
+
this.save();
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Get a memory entry value
|
|
1207
|
+
*/
|
|
1208
|
+
get(key) {
|
|
1209
|
+
const entry = this.entries.get(key);
|
|
1210
|
+
return entry?.value;
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Get a memory entry with metadata
|
|
1214
|
+
*/
|
|
1215
|
+
getEntry(key) {
|
|
1216
|
+
return this.entries.get(key);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Check if a key exists
|
|
1220
|
+
*/
|
|
1221
|
+
has(key) {
|
|
1222
|
+
return this.entries.has(key);
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Delete a memory entry
|
|
1226
|
+
*/
|
|
1227
|
+
delete(key) {
|
|
1228
|
+
const deleted = this.entries.delete(key);
|
|
1229
|
+
if (deleted) {
|
|
1230
|
+
this.save();
|
|
1231
|
+
}
|
|
1232
|
+
return deleted;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Get all memory entries
|
|
1236
|
+
*/
|
|
1237
|
+
getAll() {
|
|
1238
|
+
return new Map(this.entries);
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Get all keys
|
|
1242
|
+
*/
|
|
1243
|
+
keys() {
|
|
1244
|
+
return Array.from(this.entries.keys());
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Clear all entries
|
|
1248
|
+
*/
|
|
1249
|
+
clear() {
|
|
1250
|
+
this.entries.clear();
|
|
1251
|
+
this.save();
|
|
1252
|
+
}
|
|
1253
|
+
/**
|
|
1254
|
+
* Get entries count
|
|
1255
|
+
*/
|
|
1256
|
+
size() {
|
|
1257
|
+
return this.entries.size;
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Force save to storage
|
|
1261
|
+
*/
|
|
1262
|
+
forceSave() {
|
|
1263
|
+
const originalAutoSave = this.autoSave;
|
|
1264
|
+
this.autoSave = true;
|
|
1265
|
+
this.save();
|
|
1266
|
+
this.autoSave = originalAutoSave;
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Convert entries to a format suitable for system prompt injection
|
|
1270
|
+
*/
|
|
1271
|
+
toPromptContext() {
|
|
1272
|
+
if (this.entries.size === 0) {
|
|
1273
|
+
return "";
|
|
1274
|
+
}
|
|
1275
|
+
const lines = ["[Global Memory]"];
|
|
1276
|
+
for (const [key, entry] of this.entries) {
|
|
1277
|
+
const valueStr = typeof entry.value === "object" ? JSON.stringify(entry.value) : String(entry.value);
|
|
1278
|
+
lines.push(`- ${key}: ${valueStr}`);
|
|
1279
|
+
}
|
|
1280
|
+
return lines.join("\n");
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
var globalMemoryInstance = null;
|
|
1284
|
+
function initGlobalMemory(storagePath, autoSave = true) {
|
|
1285
|
+
globalMemoryInstance = new GlobalMemoryStore(storagePath ?? DEFAULT_STORAGE_PATH, autoSave);
|
|
1286
|
+
}
|
|
1287
|
+
function getGlobalMemoryStore() {
|
|
1288
|
+
if (!globalMemoryInstance) {
|
|
1289
|
+
globalMemoryInstance = new GlobalMemoryStore();
|
|
1290
|
+
}
|
|
1291
|
+
return globalMemoryInstance;
|
|
1292
|
+
}
|
|
1293
|
+
function setGlobalMemory(key, value) {
|
|
1294
|
+
getGlobalMemoryStore().set(key, value);
|
|
1295
|
+
}
|
|
1296
|
+
function getGlobalMemory(key) {
|
|
1297
|
+
return getGlobalMemoryStore().get(key);
|
|
1298
|
+
}
|
|
1299
|
+
function deleteGlobalMemory(key) {
|
|
1300
|
+
return getGlobalMemoryStore().delete(key);
|
|
1301
|
+
}
|
|
1302
|
+
function hasGlobalMemory(key) {
|
|
1303
|
+
return getGlobalMemoryStore().has(key);
|
|
1304
|
+
}
|
|
1305
|
+
function getGlobalMemoryKeys() {
|
|
1306
|
+
return getGlobalMemoryStore().keys();
|
|
1307
|
+
}
|
|
1308
|
+
function clearGlobalMemory() {
|
|
1309
|
+
getGlobalMemoryStore().clear();
|
|
1310
|
+
}
|
|
1311
|
+
function getGlobalMemoryContext() {
|
|
1312
|
+
return getGlobalMemoryStore().toPromptContext();
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// src/memory/chat.ts
|
|
1316
|
+
var import_path4 = require("path");
|
|
1317
|
+
var ChatMemoryStore = class {
|
|
1318
|
+
storage;
|
|
1319
|
+
autoSave;
|
|
1320
|
+
constructor(storagePath, autoSave = true) {
|
|
1321
|
+
this.autoSave = autoSave;
|
|
1322
|
+
if (storagePath) {
|
|
1323
|
+
this.storage = new FileStorage((0, import_path4.join)(storagePath, "chat_memory"));
|
|
1324
|
+
} else {
|
|
1325
|
+
this.storage = new MemoryStorage();
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Get storage key for a chat
|
|
1330
|
+
*/
|
|
1331
|
+
getChatKey(chatId) {
|
|
1332
|
+
return `chat_${chatId}`;
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Load chat memory from storage
|
|
1336
|
+
*/
|
|
1337
|
+
loadChatMemory(chatId) {
|
|
1338
|
+
const data = this.storage.read(this.getChatKey(chatId));
|
|
1339
|
+
if (data) {
|
|
1340
|
+
return new Map(Object.entries(data));
|
|
1341
|
+
}
|
|
1342
|
+
return /* @__PURE__ */ new Map();
|
|
1343
|
+
}
|
|
1344
|
+
/**
|
|
1345
|
+
* Save chat memory to storage
|
|
1346
|
+
*/
|
|
1347
|
+
saveChatMemory(chatId, entries) {
|
|
1348
|
+
if (!this.autoSave) return;
|
|
1349
|
+
const data = {};
|
|
1350
|
+
for (const [key, value] of entries) {
|
|
1351
|
+
data[key] = value;
|
|
1352
|
+
}
|
|
1353
|
+
this.storage.write(this.getChatKey(chatId), data);
|
|
1354
|
+
}
|
|
1355
|
+
/**
|
|
1356
|
+
* Set a memory entry for a chat
|
|
1357
|
+
*/
|
|
1358
|
+
set(chatId, key, value) {
|
|
1359
|
+
const entries = this.loadChatMemory(chatId);
|
|
1360
|
+
const now = Date.now();
|
|
1361
|
+
const existing = entries.get(key);
|
|
1362
|
+
entries.set(key, {
|
|
1363
|
+
key,
|
|
1364
|
+
value,
|
|
1365
|
+
createdAt: existing?.createdAt ?? now,
|
|
1366
|
+
updatedAt: now
|
|
1367
|
+
});
|
|
1368
|
+
this.saveChatMemory(chatId, entries);
|
|
1369
|
+
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Get a memory entry value for a chat
|
|
1372
|
+
*/
|
|
1373
|
+
get(chatId, key) {
|
|
1374
|
+
const entries = this.loadChatMemory(chatId);
|
|
1375
|
+
const entry = entries.get(key);
|
|
1376
|
+
return entry?.value;
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Get a memory entry with metadata
|
|
1380
|
+
*/
|
|
1381
|
+
getEntry(chatId, key) {
|
|
1382
|
+
const entries = this.loadChatMemory(chatId);
|
|
1383
|
+
return entries.get(key);
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Check if a key exists for a chat
|
|
1387
|
+
*/
|
|
1388
|
+
has(chatId, key) {
|
|
1389
|
+
const entries = this.loadChatMemory(chatId);
|
|
1390
|
+
return entries.has(key);
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Delete a memory entry for a chat
|
|
1394
|
+
*/
|
|
1395
|
+
delete(chatId, key) {
|
|
1396
|
+
const entries = this.loadChatMemory(chatId);
|
|
1397
|
+
const deleted = entries.delete(key);
|
|
1398
|
+
if (deleted) {
|
|
1399
|
+
this.saveChatMemory(chatId, entries);
|
|
1400
|
+
}
|
|
1401
|
+
return deleted;
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Get all memory entries for a chat
|
|
1405
|
+
*/
|
|
1406
|
+
getAll(chatId) {
|
|
1407
|
+
return this.loadChatMemory(chatId);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Get all keys for a chat
|
|
1411
|
+
*/
|
|
1412
|
+
keys(chatId) {
|
|
1413
|
+
const entries = this.loadChatMemory(chatId);
|
|
1414
|
+
return Array.from(entries.keys());
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Clear all entries for a chat
|
|
1418
|
+
*/
|
|
1419
|
+
clear(chatId) {
|
|
1420
|
+
this.storage.delete(this.getChatKey(chatId));
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Get entries count for a chat
|
|
1424
|
+
*/
|
|
1425
|
+
size(chatId) {
|
|
1426
|
+
const entries = this.loadChatMemory(chatId);
|
|
1427
|
+
return entries.size;
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* Convert chat memory to a format suitable for system prompt injection
|
|
1431
|
+
*/
|
|
1432
|
+
toPromptContext(chatId) {
|
|
1433
|
+
const entries = this.loadChatMemory(chatId);
|
|
1434
|
+
if (entries.size === 0) {
|
|
1435
|
+
return "";
|
|
1436
|
+
}
|
|
1437
|
+
const lines = ["[Chat Memory]"];
|
|
1438
|
+
for (const [key, entry] of entries) {
|
|
1439
|
+
const valueStr = typeof entry.value === "object" ? JSON.stringify(entry.value) : String(entry.value);
|
|
1440
|
+
lines.push(`- ${key}: ${valueStr}`);
|
|
1441
|
+
}
|
|
1442
|
+
return lines.join("\n");
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
var chatMemoryInstance = null;
|
|
1446
|
+
function initChatMemory(storagePath, autoSave = true) {
|
|
1447
|
+
chatMemoryInstance = new ChatMemoryStore(storagePath, autoSave);
|
|
1448
|
+
}
|
|
1449
|
+
function getChatMemoryStore() {
|
|
1450
|
+
if (!chatMemoryInstance) {
|
|
1451
|
+
chatMemoryInstance = new ChatMemoryStore();
|
|
1452
|
+
}
|
|
1453
|
+
return chatMemoryInstance;
|
|
1454
|
+
}
|
|
1455
|
+
function setChatMemory(chat, key, value) {
|
|
1456
|
+
getChatMemoryStore().set(chat.id, key, value);
|
|
1457
|
+
const now = Date.now();
|
|
1458
|
+
const existing = chat.memory.get(key);
|
|
1459
|
+
chat.memory.set(key, {
|
|
1460
|
+
key,
|
|
1461
|
+
value,
|
|
1462
|
+
createdAt: existing?.createdAt ?? now,
|
|
1463
|
+
updatedAt: now
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
function getChatMemory(chat, key) {
|
|
1467
|
+
const internalEntry = chat.memory.get(key);
|
|
1468
|
+
if (internalEntry) {
|
|
1469
|
+
return internalEntry.value;
|
|
1470
|
+
}
|
|
1471
|
+
return getChatMemoryStore().get(chat.id, key);
|
|
1472
|
+
}
|
|
1473
|
+
function deleteChatMemory(chat, key) {
|
|
1474
|
+
chat.memory.delete(key);
|
|
1475
|
+
return getChatMemoryStore().delete(chat.id, key);
|
|
1476
|
+
}
|
|
1477
|
+
function hasChatMemory(chat, key) {
|
|
1478
|
+
return chat.memory.has(key) || getChatMemoryStore().has(chat.id, key);
|
|
1479
|
+
}
|
|
1480
|
+
function getChatMemoryKeys(chat) {
|
|
1481
|
+
const internalKeys = Array.from(chat.memory.keys());
|
|
1482
|
+
const storedKeys = getChatMemoryStore().keys(chat.id);
|
|
1483
|
+
return [.../* @__PURE__ */ new Set([...internalKeys, ...storedKeys])];
|
|
1484
|
+
}
|
|
1485
|
+
function clearChatMemory(chat) {
|
|
1486
|
+
chat.memory.clear();
|
|
1487
|
+
getChatMemoryStore().clear(chat.id);
|
|
1488
|
+
}
|
|
1489
|
+
function getChatMemoryContext(chat) {
|
|
1490
|
+
return getChatMemoryStore().toPromptContext(chat.id);
|
|
1491
|
+
}
|
|
1492
|
+
function syncChatMemory(chat) {
|
|
1493
|
+
for (const [key, entry] of chat.memory) {
|
|
1494
|
+
getChatMemoryStore().set(chat.id, key, entry.value);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// src/memory/manager.ts
|
|
1499
|
+
function initMemory(config = {}) {
|
|
1500
|
+
initGlobalMemory(config.storagePath, config.autoSave);
|
|
1501
|
+
initChatMemory(config.storagePath, config.autoSave);
|
|
1502
|
+
}
|
|
1503
|
+
function getMemoryContext(chat) {
|
|
1504
|
+
const globalContext = getGlobalMemoryContext();
|
|
1505
|
+
const chatContext = getChatMemoryContext(chat);
|
|
1506
|
+
const parts = [];
|
|
1507
|
+
if (globalContext) {
|
|
1508
|
+
parts.push(globalContext);
|
|
1509
|
+
}
|
|
1510
|
+
if (chatContext) {
|
|
1511
|
+
parts.push(chatContext);
|
|
1512
|
+
}
|
|
1513
|
+
return parts.join("\n\n");
|
|
1514
|
+
}
|
|
1515
|
+
function buildSystemPromptWithMemory(basePrompt, chat) {
|
|
1516
|
+
const memoryContext = getMemoryContext(chat);
|
|
1517
|
+
if (!memoryContext) {
|
|
1518
|
+
return basePrompt;
|
|
1519
|
+
}
|
|
1520
|
+
return `${basePrompt}
|
|
1521
|
+
|
|
1522
|
+
---
|
|
1523
|
+
${memoryContext}
|
|
1524
|
+
---`;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
// src/acontext/observer.ts
|
|
1528
|
+
var observerStates = /* @__PURE__ */ new Map();
|
|
1529
|
+
var observerCallbacks = /* @__PURE__ */ new Map();
|
|
1530
|
+
var DEFAULT_OBSERVER_CONFIG = {
|
|
1531
|
+
extractTasks: true,
|
|
1532
|
+
extractPreferences: true,
|
|
1533
|
+
autoLearn: false
|
|
1534
|
+
};
|
|
1535
|
+
function generateId(prefix) {
|
|
1536
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1537
|
+
}
|
|
1538
|
+
function getState(chatId) {
|
|
1539
|
+
let state = observerStates.get(chatId);
|
|
1540
|
+
if (!state) {
|
|
1541
|
+
state = {
|
|
1542
|
+
enabled: false,
|
|
1543
|
+
config: { ...DEFAULT_OBSERVER_CONFIG },
|
|
1544
|
+
tasks: [],
|
|
1545
|
+
preferences: []
|
|
1546
|
+
};
|
|
1547
|
+
observerStates.set(chatId, state);
|
|
1548
|
+
}
|
|
1549
|
+
return state;
|
|
1550
|
+
}
|
|
1551
|
+
function emitEvent(chatId, event) {
|
|
1552
|
+
const callbacks = observerCallbacks.get(chatId) || [];
|
|
1553
|
+
for (const callback of callbacks) {
|
|
1554
|
+
try {
|
|
1555
|
+
callback(event);
|
|
1556
|
+
} catch (error) {
|
|
1557
|
+
console.error("Observer callback error:", error);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
function enableObserver(chat, config) {
|
|
1562
|
+
const state = getState(chat.id);
|
|
1563
|
+
state.enabled = true;
|
|
1564
|
+
state.config = { ...DEFAULT_OBSERVER_CONFIG, ...config };
|
|
1565
|
+
observerStates.set(chat.id, state);
|
|
1566
|
+
}
|
|
1567
|
+
function disableObserver(chat) {
|
|
1568
|
+
const state = getState(chat.id);
|
|
1569
|
+
state.enabled = false;
|
|
1570
|
+
observerStates.set(chat.id, state);
|
|
1571
|
+
}
|
|
1572
|
+
function isObserverEnabled(chat) {
|
|
1573
|
+
return getState(chat.id).enabled;
|
|
1574
|
+
}
|
|
1575
|
+
function getObserverConfig(chat) {
|
|
1576
|
+
return { ...getState(chat.id).config };
|
|
1577
|
+
}
|
|
1578
|
+
function onObserverEvent(chat, callback) {
|
|
1579
|
+
const callbacks = observerCallbacks.get(chat.id) || [];
|
|
1580
|
+
callbacks.push(callback);
|
|
1581
|
+
observerCallbacks.set(chat.id, callbacks);
|
|
1582
|
+
return () => {
|
|
1583
|
+
const currentCallbacks = observerCallbacks.get(chat.id) || [];
|
|
1584
|
+
const index = currentCallbacks.indexOf(callback);
|
|
1585
|
+
if (index > -1) {
|
|
1586
|
+
currentCallbacks.splice(index, 1);
|
|
1587
|
+
observerCallbacks.set(chat.id, currentCallbacks);
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function analyzeMessages(chat, messages) {
|
|
1592
|
+
const state = getState(chat.id);
|
|
1593
|
+
if (!state.enabled) {
|
|
1594
|
+
return { tasks: [], preferences: [] };
|
|
1595
|
+
}
|
|
1596
|
+
const messagesToAnalyze = messages || chat.messages;
|
|
1597
|
+
const tasks = [];
|
|
1598
|
+
const preferences = [];
|
|
1599
|
+
for (const msg of messagesToAnalyze) {
|
|
1600
|
+
if (msg.role === "user") {
|
|
1601
|
+
const taskPatterns = [
|
|
1602
|
+
/(?:해줘|해주세요|만들어|작성해|구현해|추가해|수정해|삭제해|찾아)/,
|
|
1603
|
+
/(?:please|create|make|write|implement|add|update|delete|find)/i
|
|
1604
|
+
];
|
|
1605
|
+
for (const pattern of taskPatterns) {
|
|
1606
|
+
if (pattern.test(msg.content)) {
|
|
1607
|
+
const description = msg.content.substring(0, 100).trim();
|
|
1608
|
+
tasks.push({
|
|
1609
|
+
description,
|
|
1610
|
+
status: "pending",
|
|
1611
|
+
progress: 0
|
|
1612
|
+
});
|
|
1613
|
+
break;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
const preferencePatterns = [
|
|
1617
|
+
{ pattern: /한국어|korean/i, key: "language", value: "ko" },
|
|
1618
|
+
{ pattern: /영어|english/i, key: "language", value: "en" },
|
|
1619
|
+
{ pattern: /간단히|짧게|briefly/i, key: "response_length", value: "short" },
|
|
1620
|
+
{ pattern: /자세히|상세히|in detail/i, key: "response_length", value: "detailed" },
|
|
1621
|
+
{ pattern: /코드로|with code/i, key: "include_code", value: true }
|
|
1622
|
+
];
|
|
1623
|
+
for (const { pattern, key, value } of preferencePatterns) {
|
|
1624
|
+
if (pattern.test(msg.content)) {
|
|
1625
|
+
preferences.push({
|
|
1626
|
+
key,
|
|
1627
|
+
value,
|
|
1628
|
+
confidence: 0.7
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
if (msg.role === "assistant") {
|
|
1634
|
+
const completionPatterns = [
|
|
1635
|
+
/완료|완성|done|finished|completed/i
|
|
1636
|
+
];
|
|
1637
|
+
for (const pattern of completionPatterns) {
|
|
1638
|
+
if (pattern.test(msg.content) && tasks.length > 0) {
|
|
1639
|
+
for (let i = tasks.length - 1; i >= 0; i--) {
|
|
1640
|
+
if (tasks[i].status === "pending") {
|
|
1641
|
+
tasks[i].status = "completed";
|
|
1642
|
+
tasks[i].progress = 100;
|
|
1643
|
+
break;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
return { tasks, preferences };
|
|
1651
|
+
}
|
|
1652
|
+
function processMessages(chat, newMessages) {
|
|
1653
|
+
const state = getState(chat.id);
|
|
1654
|
+
if (!state.enabled) return;
|
|
1655
|
+
const extraction = analyzeMessages(chat, newMessages);
|
|
1656
|
+
const now = Date.now();
|
|
1657
|
+
if (state.config.extractTasks) {
|
|
1658
|
+
for (const extractedTask of extraction.tasks) {
|
|
1659
|
+
const task = {
|
|
1660
|
+
id: generateId("task"),
|
|
1661
|
+
description: extractedTask.description,
|
|
1662
|
+
status: extractedTask.status,
|
|
1663
|
+
progress: extractedTask.progress,
|
|
1664
|
+
createdAt: now,
|
|
1665
|
+
updatedAt: now
|
|
1666
|
+
};
|
|
1667
|
+
state.tasks.push(task);
|
|
1668
|
+
emitEvent(chat.id, {
|
|
1669
|
+
type: "task_detected",
|
|
1670
|
+
timestamp: now,
|
|
1671
|
+
data: task
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
if (state.config.extractPreferences) {
|
|
1676
|
+
for (const extractedPref of extraction.preferences) {
|
|
1677
|
+
const existingIndex = state.preferences.findIndex((p) => p.key === extractedPref.key);
|
|
1678
|
+
const preference = {
|
|
1679
|
+
key: extractedPref.key,
|
|
1680
|
+
value: extractedPref.value,
|
|
1681
|
+
confidence: extractedPref.confidence,
|
|
1682
|
+
source: `message_${newMessages[0]?.id || "unknown"}`
|
|
1683
|
+
};
|
|
1684
|
+
if (existingIndex >= 0) {
|
|
1685
|
+
state.preferences[existingIndex] = preference;
|
|
1686
|
+
} else {
|
|
1687
|
+
state.preferences.push(preference);
|
|
1688
|
+
}
|
|
1689
|
+
emitEvent(chat.id, {
|
|
1690
|
+
type: "preference_detected",
|
|
1691
|
+
timestamp: now,
|
|
1692
|
+
data: preference
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
observerStates.set(chat.id, state);
|
|
1697
|
+
}
|
|
1698
|
+
function getObservedTasks(chat) {
|
|
1699
|
+
return [...getState(chat.id).tasks];
|
|
1700
|
+
}
|
|
1701
|
+
function getObservedPreferences(chat) {
|
|
1702
|
+
return [...getState(chat.id).preferences];
|
|
1703
|
+
}
|
|
1704
|
+
function updateTaskStatus(chat, taskId, status, progress) {
|
|
1705
|
+
const state = getState(chat.id);
|
|
1706
|
+
const task = state.tasks.find((t) => t.id === taskId);
|
|
1707
|
+
if (!task) return false;
|
|
1708
|
+
task.status = status;
|
|
1709
|
+
if (progress !== void 0) {
|
|
1710
|
+
task.progress = progress;
|
|
1711
|
+
}
|
|
1712
|
+
task.updatedAt = Date.now();
|
|
1713
|
+
emitEvent(chat.id, {
|
|
1714
|
+
type: "task_updated",
|
|
1715
|
+
timestamp: Date.now(),
|
|
1716
|
+
data: task
|
|
1717
|
+
});
|
|
1718
|
+
observerStates.set(chat.id, state);
|
|
1719
|
+
return true;
|
|
1720
|
+
}
|
|
1721
|
+
function clearObserverState(chat) {
|
|
1722
|
+
observerStates.delete(chat.id);
|
|
1723
|
+
observerCallbacks.delete(chat.id);
|
|
1724
|
+
}
|
|
1725
|
+
function getObserverState(chat) {
|
|
1726
|
+
return { ...getState(chat.id) };
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// src/acontext/learner.ts
|
|
1730
|
+
var import_path5 = require("path");
|
|
1731
|
+
var SkillLearner = class {
|
|
1732
|
+
storage;
|
|
1733
|
+
skills = /* @__PURE__ */ new Map();
|
|
1734
|
+
storageKey = "learned_skills";
|
|
1735
|
+
constructor(storagePath) {
|
|
1736
|
+
if (storagePath) {
|
|
1737
|
+
this.storage = new FileStorage((0, import_path5.join)(storagePath, "acontext"));
|
|
1738
|
+
this.load();
|
|
1739
|
+
} else {
|
|
1740
|
+
this.storage = new MemoryStorage();
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* Load skills from storage
|
|
1745
|
+
*/
|
|
1746
|
+
load() {
|
|
1747
|
+
const data = this.storage.read(this.storageKey);
|
|
1748
|
+
if (data) {
|
|
1749
|
+
this.skills = new Map(Object.entries(data));
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Save skills to storage
|
|
1754
|
+
*/
|
|
1755
|
+
save() {
|
|
1756
|
+
const data = {};
|
|
1757
|
+
for (const [key, value] of this.skills) {
|
|
1758
|
+
data[key] = value;
|
|
1759
|
+
}
|
|
1760
|
+
this.storage.write(this.storageKey, data);
|
|
1761
|
+
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Generate a unique skill ID
|
|
1764
|
+
*/
|
|
1765
|
+
generateId() {
|
|
1766
|
+
return `skill_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Extract category from task description
|
|
1770
|
+
*/
|
|
1771
|
+
extractCategory(description) {
|
|
1772
|
+
const categoryPatterns = [
|
|
1773
|
+
[/여행|travel|trip/i, "travel"],
|
|
1774
|
+
[/코드|code|programming|개발/i, "coding"],
|
|
1775
|
+
[/글|write|작성|문서/i, "writing"],
|
|
1776
|
+
[/분석|analyze|analysis/i, "analysis"],
|
|
1777
|
+
[/검색|search|find|찾/i, "search"],
|
|
1778
|
+
[/번역|translate/i, "translation"],
|
|
1779
|
+
[/요약|summarize|summary/i, "summarization"]
|
|
1780
|
+
];
|
|
1781
|
+
for (const [pattern, category] of categoryPatterns) {
|
|
1782
|
+
if (pattern.test(description)) {
|
|
1783
|
+
return category;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
return "general";
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* Learn from a completed conversation
|
|
1790
|
+
*/
|
|
1791
|
+
learn(input) {
|
|
1792
|
+
const now = Date.now();
|
|
1793
|
+
const skillId = this.generateId();
|
|
1794
|
+
const category = this.extractCategory(input.taskDescription);
|
|
1795
|
+
const steps = input.steps.map((step, index) => ({
|
|
1796
|
+
order: index + 1,
|
|
1797
|
+
action: this.summarizeAction(step),
|
|
1798
|
+
expectedInput: this.extractInputPattern(step.userInput),
|
|
1799
|
+
expectedOutput: this.extractOutputPattern(step.assistantOutput)
|
|
1800
|
+
}));
|
|
1801
|
+
const skill = {
|
|
1802
|
+
id: skillId,
|
|
1803
|
+
name: this.generateSkillName(input.taskDescription),
|
|
1804
|
+
description: input.taskDescription,
|
|
1805
|
+
category,
|
|
1806
|
+
steps,
|
|
1807
|
+
successRate: input.outcome === "success" ? 1 : input.outcome === "partial" ? 0.5 : 0,
|
|
1808
|
+
usageCount: 0,
|
|
1809
|
+
createdAt: now,
|
|
1810
|
+
updatedAt: now
|
|
1811
|
+
};
|
|
1812
|
+
this.skills.set(skillId, skill);
|
|
1813
|
+
this.save();
|
|
1814
|
+
return skill;
|
|
1815
|
+
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Summarize an action from a learning step
|
|
1818
|
+
*/
|
|
1819
|
+
summarizeAction(step) {
|
|
1820
|
+
const summary = step.assistantOutput.substring(0, 100).trim();
|
|
1821
|
+
if (step.toolsUsed && step.toolsUsed.length > 0) {
|
|
1822
|
+
return `${summary} [Tools: ${step.toolsUsed.join(", ")}]`;
|
|
1823
|
+
}
|
|
1824
|
+
return summary;
|
|
1825
|
+
}
|
|
1826
|
+
/**
|
|
1827
|
+
* Extract input pattern from user input
|
|
1828
|
+
*/
|
|
1829
|
+
extractInputPattern(input) {
|
|
1830
|
+
return input.substring(0, 50).trim();
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Extract output pattern from assistant output
|
|
1834
|
+
*/
|
|
1835
|
+
extractOutputPattern(output) {
|
|
1836
|
+
return output.substring(0, 50).trim();
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Generate a skill name from task description
|
|
1840
|
+
*/
|
|
1841
|
+
generateSkillName(description) {
|
|
1842
|
+
const name = description.substring(0, 30).trim();
|
|
1843
|
+
return name.replace(/[^a-zA-Z0-9가-힣\s]/g, "").trim() || "Unnamed Skill";
|
|
1844
|
+
}
|
|
1845
|
+
/**
|
|
1846
|
+
* Get a skill by ID
|
|
1847
|
+
*/
|
|
1848
|
+
getSkill(skillId) {
|
|
1849
|
+
return this.skills.get(skillId);
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* Get all skills
|
|
1853
|
+
*/
|
|
1854
|
+
getAllSkills() {
|
|
1855
|
+
return Array.from(this.skills.values());
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Search skills by category
|
|
1859
|
+
*/
|
|
1860
|
+
getSkillsByCategory(category) {
|
|
1861
|
+
return this.getAllSkills().filter((s) => s.category === category);
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Search skills by keyword
|
|
1865
|
+
*/
|
|
1866
|
+
searchSkills(keyword) {
|
|
1867
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
1868
|
+
return this.getAllSkills().filter(
|
|
1869
|
+
(s) => s.name.toLowerCase().includes(lowerKeyword) || s.description.toLowerCase().includes(lowerKeyword) || s.category.toLowerCase().includes(lowerKeyword)
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Update skill success rate
|
|
1874
|
+
*/
|
|
1875
|
+
updateSuccessRate(skillId, success) {
|
|
1876
|
+
const skill = this.skills.get(skillId);
|
|
1877
|
+
if (!skill) return;
|
|
1878
|
+
skill.usageCount += 1;
|
|
1879
|
+
skill.successRate = (skill.successRate * (skill.usageCount - 1) + (success ? 1 : 0)) / skill.usageCount;
|
|
1880
|
+
skill.updatedAt = Date.now();
|
|
1881
|
+
this.skills.set(skillId, skill);
|
|
1882
|
+
this.save();
|
|
1883
|
+
}
|
|
1884
|
+
/**
|
|
1885
|
+
* Delete a skill
|
|
1886
|
+
*/
|
|
1887
|
+
deleteSkill(skillId) {
|
|
1888
|
+
const deleted = this.skills.delete(skillId);
|
|
1889
|
+
if (deleted) {
|
|
1890
|
+
this.save();
|
|
1891
|
+
}
|
|
1892
|
+
return deleted;
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Clear all skills
|
|
1896
|
+
*/
|
|
1897
|
+
clearSkills() {
|
|
1898
|
+
this.skills.clear();
|
|
1899
|
+
this.save();
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
var learnerInstance = null;
|
|
1903
|
+
function initLearner(storagePath) {
|
|
1904
|
+
learnerInstance = new SkillLearner(storagePath ?? DEFAULT_STORAGE_PATH);
|
|
1905
|
+
}
|
|
1906
|
+
function getLearner() {
|
|
1907
|
+
if (!learnerInstance) {
|
|
1908
|
+
learnerInstance = new SkillLearner();
|
|
1909
|
+
}
|
|
1910
|
+
return learnerInstance;
|
|
1911
|
+
}
|
|
1912
|
+
function learnFromConversation(chat, taskDescription, outcome = "success") {
|
|
1913
|
+
const steps = [];
|
|
1914
|
+
let currentUserInput = "";
|
|
1915
|
+
for (const msg of chat.messages) {
|
|
1916
|
+
if (msg.role === "user") {
|
|
1917
|
+
currentUserInput = msg.content;
|
|
1918
|
+
} else if (msg.role === "assistant" && currentUserInput) {
|
|
1919
|
+
steps.push({
|
|
1920
|
+
userInput: currentUserInput,
|
|
1921
|
+
assistantOutput: msg.content,
|
|
1922
|
+
toolsUsed: msg.toolCalls?.map((tc) => tc.name)
|
|
1923
|
+
});
|
|
1924
|
+
currentUserInput = "";
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
return getLearner().learn({
|
|
1928
|
+
taskDescription,
|
|
1929
|
+
conversationSummary: `Conversation with ${chat.messages.length} messages`,
|
|
1930
|
+
outcome,
|
|
1931
|
+
steps
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
function getLearnedSkills(category) {
|
|
1935
|
+
if (category) {
|
|
1936
|
+
return getLearner().getSkillsByCategory(category);
|
|
1937
|
+
}
|
|
1938
|
+
return getLearner().getAllSkills();
|
|
1939
|
+
}
|
|
1940
|
+
function searchLearnedSkills(keyword) {
|
|
1941
|
+
return getLearner().searchSkills(keyword);
|
|
1942
|
+
}
|
|
1943
|
+
function getLearnedSkill(skillId) {
|
|
1944
|
+
return getLearner().getSkill(skillId);
|
|
1945
|
+
}
|
|
1946
|
+
function recordSkillUsage(skillId, success) {
|
|
1947
|
+
getLearner().updateSuccessRate(skillId, success);
|
|
1948
|
+
}
|
|
1949
|
+
function deleteLearnedSkill(skillId) {
|
|
1950
|
+
return getLearner().deleteSkill(skillId);
|
|
1951
|
+
}
|
|
1952
|
+
function clearLearnedSkills() {
|
|
1953
|
+
getLearner().clearSkills();
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
// src/acontext/skills.ts
|
|
1957
|
+
function findRelevantSkills(query, options = {}) {
|
|
1958
|
+
const { category, minSuccessRate = 0, limit = 10 } = options;
|
|
1959
|
+
let skills = getLearnedSkills(category);
|
|
1960
|
+
if (minSuccessRate > 0) {
|
|
1961
|
+
skills = skills.filter((s) => s.successRate >= minSuccessRate);
|
|
1962
|
+
}
|
|
1963
|
+
const scoredSkills = skills.map((skill) => ({
|
|
1964
|
+
skill,
|
|
1965
|
+
score: calculateRelevanceScore(query, skill)
|
|
1966
|
+
}));
|
|
1967
|
+
scoredSkills.sort((a, b) => b.score - a.score);
|
|
1968
|
+
return scoredSkills.filter((s) => s.score > 0).slice(0, limit).map((s) => s.skill);
|
|
1969
|
+
}
|
|
1970
|
+
function calculateRelevanceScore(query, skill) {
|
|
1971
|
+
const lowerQuery = query.toLowerCase();
|
|
1972
|
+
const words = lowerQuery.split(/\s+/).filter((w) => w.length > 1);
|
|
1973
|
+
let score = 0;
|
|
1974
|
+
const lowerName = skill.name.toLowerCase();
|
|
1975
|
+
for (const word of words) {
|
|
1976
|
+
if (lowerName.includes(word)) {
|
|
1977
|
+
score += 3;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
const lowerDesc = skill.description.toLowerCase();
|
|
1981
|
+
for (const word of words) {
|
|
1982
|
+
if (lowerDesc.includes(word)) {
|
|
1983
|
+
score += 2;
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
if (lowerQuery.includes(skill.category)) {
|
|
1987
|
+
score += 2;
|
|
1988
|
+
}
|
|
1989
|
+
score *= 0.5 + skill.successRate * 0.5;
|
|
1990
|
+
score += Math.min(skill.usageCount * 0.1, 1);
|
|
1991
|
+
return score;
|
|
1992
|
+
}
|
|
1993
|
+
function applySkill(_chat, skill) {
|
|
1994
|
+
if (!skill || !skill.steps || skill.steps.length === 0) {
|
|
1995
|
+
return {
|
|
1996
|
+
applied: false,
|
|
1997
|
+
skillId: skill?.id || "unknown",
|
|
1998
|
+
error: "Invalid skill or no steps available"
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
try {
|
|
2002
|
+
const suggestedPrompt = generateSkillPrompt(skill);
|
|
2003
|
+
return {
|
|
2004
|
+
applied: true,
|
|
2005
|
+
skillId: skill.id,
|
|
2006
|
+
suggestedPrompt
|
|
2007
|
+
};
|
|
2008
|
+
} catch (error) {
|
|
2009
|
+
return {
|
|
2010
|
+
applied: false,
|
|
2011
|
+
skillId: skill.id,
|
|
2012
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
function generateSkillPrompt(skill) {
|
|
2017
|
+
const lines = [
|
|
2018
|
+
`[Skill: ${skill.name}]`,
|
|
2019
|
+
"",
|
|
2020
|
+
"Follow these steps:"
|
|
2021
|
+
];
|
|
2022
|
+
for (const step of skill.steps) {
|
|
2023
|
+
lines.push(`${step.order}. ${step.action}`);
|
|
2024
|
+
if (step.expectedInput) {
|
|
2025
|
+
lines.push(` Input pattern: ${step.expectedInput}`);
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return lines.join("\n");
|
|
2029
|
+
}
|
|
2030
|
+
function applySkillToSystemPrompt(chat, skill, baseSystemPrompt) {
|
|
2031
|
+
const result = applySkill(chat, skill);
|
|
2032
|
+
if (!result.applied || !result.suggestedPrompt) {
|
|
2033
|
+
return baseSystemPrompt;
|
|
2034
|
+
}
|
|
2035
|
+
return `${baseSystemPrompt}
|
|
2036
|
+
|
|
2037
|
+
---
|
|
2038
|
+
${result.suggestedPrompt}
|
|
2039
|
+
---`;
|
|
2040
|
+
}
|
|
2041
|
+
function getBestSkill(query, options = {}) {
|
|
2042
|
+
const skills = findRelevantSkills(query, { ...options, limit: 1 });
|
|
2043
|
+
return skills[0];
|
|
2044
|
+
}
|
|
2045
|
+
function suggestSkills(chat, limit = 3) {
|
|
2046
|
+
const recentMessages = chat.messages.slice(-5);
|
|
2047
|
+
const context = recentMessages.map((m) => m.content).join(" ");
|
|
2048
|
+
return findRelevantSkills(context, { limit, minSuccessRate: 0.3 });
|
|
2049
|
+
}
|
|
2050
|
+
function recordSkillApplication(skillId, success) {
|
|
2051
|
+
recordSkillUsage(skillId, success);
|
|
2052
|
+
}
|
|
2053
|
+
var skillApplicationHistory = /* @__PURE__ */ new Map();
|
|
2054
|
+
function trackSkillApplication(chatId, skillId) {
|
|
2055
|
+
const history = skillApplicationHistory.get(chatId) || [];
|
|
2056
|
+
history.push({
|
|
2057
|
+
skillId,
|
|
2058
|
+
appliedAt: Date.now()
|
|
2059
|
+
});
|
|
2060
|
+
skillApplicationHistory.set(chatId, history);
|
|
2061
|
+
}
|
|
2062
|
+
function updateSkillApplicationSuccess(chatId, skillId, success) {
|
|
2063
|
+
const history = skillApplicationHistory.get(chatId) || [];
|
|
2064
|
+
let application;
|
|
2065
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
2066
|
+
if (history[i].skillId === skillId) {
|
|
2067
|
+
application = history[i];
|
|
2068
|
+
break;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
if (application) {
|
|
2072
|
+
application.success = success;
|
|
2073
|
+
recordSkillApplication(skillId, success);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
function getSkillApplicationHistory(chatId) {
|
|
2077
|
+
return [...skillApplicationHistory.get(chatId) || []];
|
|
2078
|
+
}
|
|
2079
|
+
function clearSkillApplicationHistory(chatId) {
|
|
2080
|
+
skillApplicationHistory.delete(chatId);
|
|
2081
|
+
}
|
|
2082
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2083
|
+
0 && (module.exports = {
|
|
2084
|
+
BaseProvider,
|
|
2085
|
+
ChatLLMError,
|
|
2086
|
+
ConfigurationError,
|
|
2087
|
+
DEFAULT_MODELS,
|
|
2088
|
+
DEFAULT_OLLAMA_BASE_URL,
|
|
2089
|
+
DEFAULT_PERSONALIZATION,
|
|
2090
|
+
DEFAULT_STORAGE_PATH,
|
|
2091
|
+
FileStorage,
|
|
2092
|
+
MemoryStorage,
|
|
2093
|
+
OllamaProvider,
|
|
2094
|
+
ProviderError,
|
|
2095
|
+
ProviderRegistry,
|
|
2096
|
+
StorageError,
|
|
2097
|
+
StreamError,
|
|
2098
|
+
ToolExecutionError,
|
|
2099
|
+
ValidationError,
|
|
2100
|
+
addSessionMessage,
|
|
2101
|
+
addSessionMessages,
|
|
2102
|
+
analyzeMessages,
|
|
2103
|
+
applySkill,
|
|
2104
|
+
applySkillToSystemPrompt,
|
|
2105
|
+
buildSystemPromptWithMemory,
|
|
2106
|
+
clearChatMemory,
|
|
2107
|
+
clearGlobalMemory,
|
|
2108
|
+
clearLearnedSkills,
|
|
2109
|
+
clearMessages,
|
|
2110
|
+
clearObserverState,
|
|
2111
|
+
clearSessionMessages,
|
|
2112
|
+
clearSkillApplicationHistory,
|
|
2113
|
+
cloneMessage,
|
|
2114
|
+
collectStreamContent,
|
|
2115
|
+
createAssistantMessage,
|
|
2116
|
+
createChat,
|
|
2117
|
+
createMessage,
|
|
2118
|
+
createSession,
|
|
2119
|
+
createStreamEvent,
|
|
2120
|
+
createSystemMessage,
|
|
2121
|
+
createToolMessage,
|
|
2122
|
+
createUserMessage,
|
|
2123
|
+
deleteChatMemory,
|
|
2124
|
+
deleteGlobalMemory,
|
|
2125
|
+
deleteLearnedSkill,
|
|
2126
|
+
deserializeSession,
|
|
2127
|
+
destroyChat,
|
|
2128
|
+
disableObserver,
|
|
2129
|
+
enableObserver,
|
|
2130
|
+
filterByProvider,
|
|
2131
|
+
filterByRole,
|
|
2132
|
+
findRelevantSkills,
|
|
2133
|
+
formatMessagesForDisplay,
|
|
2134
|
+
getBestSkill,
|
|
2135
|
+
getChatMemory,
|
|
2136
|
+
getChatMemoryContext,
|
|
2137
|
+
getChatMemoryKeys,
|
|
2138
|
+
getDefaultModel,
|
|
2139
|
+
getGlobalMemory,
|
|
2140
|
+
getGlobalMemoryContext,
|
|
2141
|
+
getGlobalMemoryKeys,
|
|
2142
|
+
getGlobalMemoryStore,
|
|
2143
|
+
getLastMessageByRole,
|
|
2144
|
+
getLastMessages,
|
|
2145
|
+
getLearnedSkill,
|
|
2146
|
+
getLearnedSkills,
|
|
2147
|
+
getMemoryContext,
|
|
2148
|
+
getMessages,
|
|
2149
|
+
getMessagesAfter,
|
|
2150
|
+
getMessagesForProvider,
|
|
2151
|
+
getObservedPreferences,
|
|
2152
|
+
getObservedTasks,
|
|
2153
|
+
getObserverConfig,
|
|
2154
|
+
getObserverState,
|
|
2155
|
+
getProviderBaseUrl,
|
|
2156
|
+
getSession,
|
|
2157
|
+
getSessionSummary,
|
|
2158
|
+
getSkillApplicationHistory,
|
|
2159
|
+
getTools,
|
|
2160
|
+
getTotalContentLength,
|
|
2161
|
+
hasChatMemory,
|
|
2162
|
+
hasGlobalMemory,
|
|
2163
|
+
initChatMemory,
|
|
2164
|
+
initGlobalMemory,
|
|
2165
|
+
initLearner,
|
|
2166
|
+
initMemory,
|
|
2167
|
+
isObserverEnabled,
|
|
2168
|
+
isProviderError,
|
|
2169
|
+
isStorageError,
|
|
2170
|
+
isToolExecutionError,
|
|
2171
|
+
learnFromConversation,
|
|
2172
|
+
mergeConfig,
|
|
2173
|
+
onObserverEvent,
|
|
2174
|
+
parseNDJSONStream,
|
|
2175
|
+
parseSSEStream,
|
|
2176
|
+
processMessages,
|
|
2177
|
+
recordSkillApplication,
|
|
2178
|
+
recordSkillUsage,
|
|
2179
|
+
registerTool,
|
|
2180
|
+
resetChat,
|
|
2181
|
+
searchLearnedSkills,
|
|
2182
|
+
sendMessage,
|
|
2183
|
+
sendMessageStream,
|
|
2184
|
+
serializeSession,
|
|
2185
|
+
setChatMemory,
|
|
2186
|
+
setGlobalMemory,
|
|
2187
|
+
setSessionMetadata,
|
|
2188
|
+
setSessionSystemPrompt,
|
|
2189
|
+
setSystemPrompt,
|
|
2190
|
+
suggestSkills,
|
|
2191
|
+
syncChatMemory,
|
|
2192
|
+
trackSkillApplication,
|
|
2193
|
+
truncateMessages,
|
|
2194
|
+
unregisterTool,
|
|
2195
|
+
updateSkillApplicationSuccess,
|
|
2196
|
+
updateTaskStatus,
|
|
2197
|
+
validateProviderConfig,
|
|
2198
|
+
wrapProviderError
|
|
2199
|
+
});
|