@xalia/agent 0.6.0 → 0.6.2

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.
Files changed (112) hide show
  1. package/dist/agent/src/agent/agent.js +103 -54
  2. package/dist/agent/src/agent/agentUtils.js +22 -21
  3. package/dist/agent/src/agent/compressingContextManager.js +3 -2
  4. package/dist/agent/src/agent/dummyLLM.js +1 -3
  5. package/dist/agent/src/agent/imageGenLLM.js +67 -0
  6. package/dist/agent/src/agent/imageGenerator.js +43 -0
  7. package/dist/agent/src/agent/llm.js +27 -0
  8. package/dist/agent/src/agent/mcpServerManager.js +18 -6
  9. package/dist/agent/src/agent/nullAgentEventHandler.js +6 -0
  10. package/dist/agent/src/agent/openAILLM.js +3 -3
  11. package/dist/agent/src/agent/openAILLMStreaming.js +41 -6
  12. package/dist/agent/src/chat/client/chatClient.js +84 -13
  13. package/dist/agent/src/chat/client/sessionClient.js +47 -6
  14. package/dist/agent/src/chat/client/sessionFiles.js +102 -0
  15. package/dist/agent/src/chat/data/apiKeyManager.js +38 -7
  16. package/dist/agent/src/chat/data/database.js +83 -70
  17. package/dist/agent/src/chat/data/dbSessionFileModels.js +49 -0
  18. package/dist/agent/src/chat/data/dbSessionFiles.js +76 -0
  19. package/dist/agent/src/chat/data/dbSessionMessages.js +57 -0
  20. package/dist/agent/src/chat/data/mimeTypes.js +44 -0
  21. package/dist/agent/src/chat/protocol/messages.js +21 -0
  22. package/dist/agent/src/chat/server/chatContextManager.js +14 -7
  23. package/dist/agent/src/chat/server/connectionManager.js +14 -36
  24. package/dist/agent/src/chat/server/connectionManager.test.js +2 -16
  25. package/dist/agent/src/chat/server/conversation.js +69 -45
  26. package/dist/agent/src/chat/server/imageGeneratorTools.js +111 -0
  27. package/dist/agent/src/chat/server/openSession.js +205 -43
  28. package/dist/agent/src/chat/server/server.js +5 -8
  29. package/dist/agent/src/chat/server/sessionFileManager.js +171 -38
  30. package/dist/agent/src/chat/server/sessionRegistry.js +199 -32
  31. package/dist/agent/src/chat/server/test-utils/mockFactories.js +12 -11
  32. package/dist/agent/src/chat/server/tools.js +27 -6
  33. package/dist/agent/src/chat/utils/multiAsyncQueue.js +9 -1
  34. package/dist/agent/src/test/agent.test.js +15 -11
  35. package/dist/agent/src/test/chatContextManager.test.js +4 -0
  36. package/dist/agent/src/test/clientServerConnection.test.js +2 -2
  37. package/dist/agent/src/test/db.test.js +33 -70
  38. package/dist/agent/src/test/dbSessionFiles.test.js +179 -0
  39. package/dist/agent/src/test/dbSessionMessages.test.js +67 -0
  40. package/dist/agent/src/test/dbTestTools.js +6 -5
  41. package/dist/agent/src/test/imageLoad.test.js +1 -1
  42. package/dist/agent/src/test/mcpServerManager.test.js +1 -1
  43. package/dist/agent/src/test/multiAsyncQueue.test.js +50 -0
  44. package/dist/agent/src/test/testTools.js +12 -0
  45. package/dist/agent/src/tool/agentChat.js +25 -6
  46. package/dist/agent/src/tool/agentMain.js +1 -1
  47. package/dist/agent/src/tool/chatMain.js +113 -4
  48. package/dist/agent/src/tool/commandPrompt.js +7 -3
  49. package/dist/agent/src/tool/files.js +23 -15
  50. package/dist/agent/src/tool/options.js +2 -2
  51. package/package.json +1 -1
  52. package/scripts/test_chat +124 -66
  53. package/src/agent/agent.ts +145 -38
  54. package/src/agent/agentUtils.ts +27 -21
  55. package/src/agent/compressingContextManager.ts +5 -4
  56. package/src/agent/context.ts +1 -1
  57. package/src/agent/dummyLLM.ts +1 -3
  58. package/src/agent/iAgentEventHandler.ts +15 -2
  59. package/src/agent/imageGenLLM.ts +99 -0
  60. package/src/agent/imageGenerator.ts +60 -0
  61. package/src/agent/llm.ts +128 -4
  62. package/src/agent/mcpServerManager.ts +26 -7
  63. package/src/agent/nullAgentEventHandler.ts +6 -0
  64. package/src/agent/openAILLM.ts +3 -8
  65. package/src/agent/openAILLMStreaming.ts +60 -14
  66. package/src/chat/client/chatClient.ts +119 -14
  67. package/src/chat/client/sessionClient.ts +75 -9
  68. package/src/chat/client/sessionFiles.ts +145 -0
  69. package/src/chat/data/apiKeyManager.ts +55 -7
  70. package/src/chat/data/dataModels.ts +16 -7
  71. package/src/chat/data/database.ts +107 -92
  72. package/src/chat/data/dbSessionFileModels.ts +91 -0
  73. package/src/chat/data/dbSessionFiles.ts +99 -0
  74. package/src/chat/data/dbSessionMessages.ts +68 -0
  75. package/src/chat/data/mimeTypes.ts +58 -0
  76. package/src/chat/protocol/messages.ts +127 -13
  77. package/src/chat/server/chatContextManager.ts +36 -13
  78. package/src/chat/server/connectionManager.test.ts +1 -22
  79. package/src/chat/server/connectionManager.ts +18 -53
  80. package/src/chat/server/conversation.ts +96 -57
  81. package/src/chat/server/imageGeneratorTools.ts +138 -0
  82. package/src/chat/server/openSession.ts +287 -49
  83. package/src/chat/server/server.ts +5 -11
  84. package/src/chat/server/sessionFileManager.ts +223 -63
  85. package/src/chat/server/sessionRegistry.ts +285 -41
  86. package/src/chat/server/test-utils/mockFactories.ts +13 -13
  87. package/src/chat/server/tools.ts +43 -8
  88. package/src/chat/utils/agentSessionMap.ts +2 -2
  89. package/src/chat/utils/multiAsyncQueue.ts +11 -1
  90. package/src/test/agent.test.ts +23 -14
  91. package/src/test/chatContextManager.test.ts +7 -2
  92. package/src/test/clientServerConnection.test.ts +3 -3
  93. package/src/test/compressingContextManager.test.ts +1 -1
  94. package/src/test/context.test.ts +2 -1
  95. package/src/test/conversation.test.ts +1 -1
  96. package/src/test/db.test.ts +41 -83
  97. package/src/test/dbSessionFiles.test.ts +258 -0
  98. package/src/test/dbSessionMessages.test.ts +85 -0
  99. package/src/test/dbTestTools.ts +9 -5
  100. package/src/test/imageLoad.test.ts +2 -2
  101. package/src/test/mcpServerManager.test.ts +3 -1
  102. package/src/test/multiAsyncQueue.test.ts +58 -0
  103. package/src/test/testTools.ts +15 -1
  104. package/src/tool/agentChat.ts +35 -7
  105. package/src/tool/agentMain.ts +7 -7
  106. package/src/tool/chatMain.ts +126 -5
  107. package/src/tool/commandPrompt.ts +10 -5
  108. package/src/tool/files.ts +30 -13
  109. package/src/tool/options.ts +1 -1
  110. package/test_data/dummyllm_script_image_gen.json +19 -0
  111. package/test_data/dummyllm_script_invoke_image_gen_tool.json +30 -0
  112. package/test_data/image_gen_test_profile.json +5 -0
@@ -1,18 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ChatSessionFileManager = exports.MemoryFileManager = exports.SESSION_FILE_TYPES = void 0;
3
+ exports.ChatSessionFileManager = exports.MemoryFileManager = void 0;
4
4
  exports.listFilesForLLM = listFilesForLLM;
5
5
  exports.fileManagerTool = fileManagerTool;
6
6
  const uuid_1 = require("uuid");
7
+ const assert_1 = require("assert");
7
8
  const tools_1 = require("./tools");
8
- exports.SESSION_FILE_TYPES = ["text", "image", "pdf"];
9
+ const dbSessionFiles_1 = require("../data/dbSessionFiles");
10
+ const dbSessionFileModels_1 = require("../data/dbSessionFileModels");
9
11
  /**
10
12
  * In-memory implementation of ISessionFileManager
11
13
  */
12
14
  class MemoryFileManager {
13
15
  constructor() {
14
16
  this.files = new Map();
15
- this.eventHandler = undefined;
17
+ this.eventHandlers = [];
16
18
  }
17
19
  // ISessionFileManager.listFiles
18
20
  listFiles() {
@@ -23,7 +25,22 @@ class MemoryFileManager {
23
25
  return new Promise((r, e) => {
24
26
  const entry = this.files.get(name);
25
27
  if (entry) {
26
- r(entry.content);
28
+ r(entry.data_url);
29
+ }
30
+ else {
31
+ e(new Error(`no such file ${name}`));
32
+ }
33
+ });
34
+ }
35
+ // ISessionFileManager.deleteFile
36
+ deleteFile(name) {
37
+ return new Promise((r, e) => {
38
+ if (this.files.has(name)) {
39
+ this.files.delete(name);
40
+ this.eventHandlers.forEach((eh) => {
41
+ eh.onFileDeleted(name);
42
+ });
43
+ r();
27
44
  }
28
45
  else {
29
46
  e(new Error(`no such file ${name}`));
@@ -31,7 +48,7 @@ class MemoryFileManager {
31
48
  });
32
49
  }
33
50
  // ISessionFileManager.addFile
34
- putFileContent(name, file_type, summary, content) {
51
+ putFileContent(name, summary, data_url) {
35
52
  return new Promise((r, e) => {
36
53
  if (!name) {
37
54
  name = (0, uuid_1.v4)();
@@ -43,29 +60,114 @@ class MemoryFileManager {
43
60
  e(new Error("summary must no contain new-lines"));
44
61
  }
45
62
  else {
46
- const entry = { name, file_type, summary, content };
47
- this.eventHandler?.onFileChange(entry);
63
+ const mime_type = (0, dbSessionFileModels_1.getSessionFileMimeTypeFromDataUrl)(data_url);
64
+ const is_new = !this.files.has(name);
65
+ const entry = { name, mime_type, summary, data_url };
66
+ this.eventHandlers.forEach((eh) => {
67
+ eh.onFileChanged(entry, is_new);
68
+ });
48
69
  this.files.set(name, entry);
49
- r(name);
70
+ r({ name, mime_type, summary });
50
71
  }
51
72
  });
52
73
  }
53
74
  // ISessionFileManager.setEventHandler
54
- setEventHandler(eventHandler) {
55
- this.eventHandler = eventHandler;
75
+ addEventHandler(eventHandler) {
76
+ this.eventHandlers.push(eventHandler);
56
77
  }
57
78
  }
58
79
  exports.MemoryFileManager = MemoryFileManager;
59
80
  /**
60
81
  * Implementation of ISessionFileManager which can store files in the DB.
61
82
  */
62
- class ChatSessionFileManager extends MemoryFileManager {
63
- // private readonly db: Database;
64
- // private readonly sessionUUID: string;
65
- constructor(_sessionUUID, _db) {
66
- super();
67
- // this.db = db;
68
- // this.sessionUUID = sessionUUID;
83
+ class ChatSessionFileManager {
84
+ constructor(sfc, sessionUUID, fileMap) {
85
+ this.sessionUUID = sessionUUID;
86
+ this.sfc = sfc;
87
+ this.eventHandlers = [];
88
+ this.fileMap = fileMap;
89
+ this.fileDataCache = new Map();
90
+ }
91
+ static async init(db, sessionUUID) {
92
+ const sfc = db.createTypedClient(dbSessionFiles_1.DbSessionFiles);
93
+ const fileList = await sfc.getFilesForSession(sessionUUID);
94
+ const fileMap = new Map(fileList.map((f) => [f.name, f]));
95
+ return new ChatSessionFileManager(sfc, sessionUUID, fileMap);
96
+ }
97
+ listFiles() {
98
+ return Array.from(this.fileMap.values());
99
+ }
100
+ async getFileContent(name) {
101
+ const cached = this.fileDataCache.get(name);
102
+ if (cached) {
103
+ return cached;
104
+ }
105
+ if (!this.fileMap.has(name)) {
106
+ throw new Error(`no file '${name}' in session '${this.sessionUUID}'`);
107
+ }
108
+ const fileData = await this.sfc.getFileContent(this.sessionUUID, name);
109
+ if (!fileData) {
110
+ // logically should not happen
111
+ throw new Error(`empty file '${name}' in session '${this.sessionUUID}'`);
112
+ }
113
+ this.fileDataCache.set(name, fileData);
114
+ return fileData;
115
+ }
116
+ // ISessionFileManager.deleteFile
117
+ async deleteFile(name) {
118
+ if (this.fileMap.has(name)) {
119
+ this.fileMap.delete(name);
120
+ this.fileDataCache.delete(name);
121
+ await this.sfc.deleteFile(this.sessionUUID, name);
122
+ this.eventHandlers.forEach((eh) => {
123
+ eh.onFileDeleted(name);
124
+ });
125
+ }
126
+ else {
127
+ throw new Error(`no file '${name}' in session '${this.sessionUUID}'`);
128
+ }
129
+ }
130
+ // ISessionFileManager.addFile
131
+ async putFileContent(name, summary, data_url) {
132
+ if (!name) {
133
+ // TODO: have the LLM fill in the name?
134
+ name = (0, uuid_1.v4)();
135
+ }
136
+ // TODO: have the LLM create summary if missing?
137
+ // If we have an existing entry in the cache then update it, otherwise
138
+ // create a new one.
139
+ const mime_type = (0, dbSessionFileModels_1.getSessionFileMimeTypeFromDataUrl)(data_url);
140
+ const existingDesc = this.fileMap.get(name);
141
+ if (existingDesc) {
142
+ existingDesc.summary = summary;
143
+ existingDesc.mime_type = mime_type;
144
+ }
145
+ else {
146
+ this.fileMap.set(name, { name, summary, mime_type });
147
+ }
148
+ const is_new = !this.fileMap.has(name);
149
+ this.fileDataCache.set(name, data_url);
150
+ await this.sfc.setFileContent(this.sessionUUID, name, summary, data_url);
151
+ this.eventHandlers.forEach((eh) => {
152
+ (0, assert_1.strict)(typeof name === "string");
153
+ eh.onFileChanged({ name, summary, mime_type, data_url }, is_new);
154
+ });
155
+ return { name, mime_type, summary };
156
+ }
157
+ // ISessionFileManager.setEventHandler
158
+ addEventHandler(eventHandler) {
159
+ this.eventHandlers.push(eventHandler);
160
+ }
161
+ clearAllFiles() {
162
+ this.fileMap.clear();
163
+ this.fileDataCache.clear();
164
+ return this.sfc.clearFiles(this.sessionUUID);
165
+ }
166
+ getSessionFileRelativeUrl(name) {
167
+ return `file+session:/./${name}`;
168
+ }
169
+ getSessionFileAbsoluteUrl(name) {
170
+ return `file+session://${this.sessionUUID}/${name}`;
69
171
  }
70
172
  }
71
173
  exports.ChatSessionFileManager = ChatSessionFileManager;
@@ -79,7 +181,7 @@ function listFilesForLLM(fm) {
79
181
  }
80
182
  let summary = "Available files:\nname,type,summary\n";
81
183
  for (const f of files) {
82
- summary += `${f.name},${f.file_type},${f.summary || ""}\n`;
184
+ summary += `${f.name},${f.mime_type},${f.summary || ""}\n`;
83
185
  }
84
186
  return summary;
85
187
  }
@@ -87,7 +189,7 @@ const GET_FILE_CONTENT_TOOL = {
87
189
  type: "function",
88
190
  function: {
89
191
  name: "get_file_content",
90
- description: "Obtain the contents of a file listed in the system prompt",
192
+ description: "Obtain contents of file listed in system prompt, as data-url",
91
193
  parameters: {
92
194
  type: "object",
93
195
  properties: {
@@ -112,49 +214,80 @@ const PUT_FILE_CONTENT_TOOL = {
112
214
  type: "string",
113
215
  description: "File name",
114
216
  },
115
- file_type: {
116
- type: "string",
117
- enum: exports.SESSION_FILE_TYPES,
118
- },
119
217
  summary: {
120
218
  type: "string",
121
219
  description: "Content summary",
122
220
  },
123
- content: {
221
+ data_url: {
124
222
  type: "string",
125
- description: "Content (text/dataurl)",
223
+ description: "Content data-url: `data:<mime-type>[;<format>],<encoding>`",
126
224
  },
127
225
  },
128
- required: ["name", "type", "summary", "content"],
226
+ required: ["name", "summary", "data_url"],
227
+ },
228
+ },
229
+ };
230
+ const DELETE_FILE_CONTENT_TOOL = {
231
+ type: "function",
232
+ function: {
233
+ name: "delete_file_content",
234
+ description: "Delete file",
235
+ parameters: {
236
+ type: "object",
237
+ properties: {
238
+ name: {
239
+ type: "string",
240
+ description: "File name",
241
+ },
242
+ },
243
+ required: ["name"],
129
244
  },
130
245
  },
131
246
  };
132
247
  function fileManagerTool(fileManager) {
133
- // get_file_content
134
- const getName = (0, tools_1.makeParseArgsFn)(["name"]);
248
+ // `get_file_content` tool
249
+ //
250
+ // LLM can read data from the file. To keep the context small, we overwrite
251
+ // the data with the session file url after the LLM has seen it.
252
+ const parseName = (0, tools_1.makeParseArgsFn)(["name"]);
135
253
  const getFileContentFn = async (_agent, args) => {
136
- const { name } = getName(args);
254
+ const { name } = parseName(args);
137
255
  const response = await fileManager.getFileContent(name);
138
- return { response };
256
+ const overwriteResponse = fileManager.getSessionFileRelativeUrl(name);
257
+ return { response, overwriteResponse };
139
258
  };
140
- // set_file_content
141
- const putArgs = ["name", "file_type", "summary", "content"];
142
- const getNameSummaryContent = (0, tools_1.makeParseArgsFn)(putArgs);
259
+ // `set_file_content` tool
260
+ //
261
+ // Allows LLM to write data (as a data-url) to the session file manager. The
262
+ // data is replaced by a session file url after being saved.
263
+ const putArgs = ["name", "summary", "data_url"];
264
+ const parseNameSummaryDataUrl = (0, tools_1.makeParseArgsFn)(putArgs);
143
265
  const putFileContentFn = async (_, args) => {
144
- const { name, file_type, summary, content } = getNameSummaryContent(args);
145
- const response = await fileManager.putFileContent(name, file_type, summary, content);
146
- return { response };
266
+ const parsed = parseNameSummaryDataUrl(args);
267
+ const { name, summary, data_url } = parsed;
268
+ const desc = await fileManager.putFileContent(name, summary, data_url);
269
+ parsed.data_url = fileManager.getSessionFileRelativeUrl(name);
270
+ return { response: desc.name, overwriteArgs: JSON.stringify(parsed) };
271
+ };
272
+ // `delete_file_content` tool
273
+ //
274
+ // Allows LLM to write data (as a data-url) to the session file manager. The
275
+ // data is replaced by a session file url after being saved.
276
+ const deleteFileFn = async (_, args) => {
277
+ const { name } = parseName(args);
278
+ await fileManager.deleteFile(name);
279
+ return { response: "" };
147
280
  };
148
- const provider = {
281
+ return {
149
282
  setup: (agent) => {
150
283
  agent.addAgentTool(GET_FILE_CONTENT_TOOL, getFileContentFn);
151
284
  agent.addAgentTool(PUT_FILE_CONTENT_TOOL, putFileContentFn);
285
+ agent.addAgentTool(DELETE_FILE_CONTENT_TOOL, deleteFileFn);
152
286
  return new Promise((r) => {
153
287
  r();
154
288
  });
155
289
  },
156
290
  };
157
- return provider;
158
291
  }
159
292
  function isSingleLine(str) {
160
293
  return !/[\r\n]/.test(str);