@xalia/agent 0.6.8 → 0.6.10
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/.env.development +6 -0
- package/.env.test +7 -0
- package/README.md +11 -0
- package/context_system.md +498 -0
- package/dist/agent/src/agent/agent.js +169 -87
- package/dist/agent/src/agent/agentUtils.js +24 -18
- package/dist/agent/src/agent/compressingContextManager.js +10 -14
- package/dist/agent/src/agent/context.js +101 -127
- package/dist/agent/src/agent/contextWithWorkspace.js +133 -0
- package/dist/agent/src/agent/documentSummarizer.js +126 -0
- package/dist/agent/src/agent/dummyLLM.js +25 -22
- package/dist/agent/src/agent/imageGenLLM.js +22 -25
- package/dist/agent/src/agent/imageGenerator.js +2 -10
- package/dist/agent/src/agent/llm.js +1 -1
- package/dist/agent/src/agent/openAILLM.js +15 -12
- package/dist/agent/src/agent/openAILLMStreaming.js +73 -39
- package/dist/agent/src/agent/repeatLLM.js +16 -7
- package/dist/agent/src/agent/sudoMcpServerManager.js +21 -9
- package/dist/agent/src/agent/tokenCounter.js +390 -0
- package/dist/agent/src/agent/tokenCounter.test.js +206 -0
- package/dist/agent/src/agent/toolSettings.js +17 -0
- package/dist/agent/src/agent/tools/calculatorTool.js +45 -0
- package/dist/agent/src/agent/tools/contentExtractors/pdfToText.js +55 -0
- package/dist/agent/src/agent/tools/datetimeTool.js +38 -0
- package/dist/agent/src/agent/tools/fileManager/fileManagerTool.js +156 -0
- package/dist/agent/src/agent/tools/fileManager/index.js +31 -0
- package/dist/agent/src/agent/tools/fileManager/memoryFileManager.js +102 -0
- package/dist/agent/src/{chat/data → agent/tools/fileManager}/mimeTypes.js +3 -1
- package/dist/agent/src/agent/tools/fileManager/prompt.js +33 -0
- package/dist/agent/src/{chat/data/dbSessionFileModels.js → agent/tools/fileManager/types.js} +7 -0
- package/dist/agent/src/agent/tools/index.js +64 -0
- package/dist/agent/src/agent/tools/openUrlTool.js +57 -0
- package/dist/agent/src/agent/tools/renderTool.js +89 -0
- package/dist/agent/src/agent/tools/utils.js +61 -0
- package/dist/agent/src/{chat/utils/search.js → agent/tools/webSearch.js} +1 -2
- package/dist/agent/src/agent/tools/webSearchTool.js +40 -0
- package/dist/agent/src/chat/client/chatClient.js +63 -2
- package/dist/agent/src/chat/client/connection.js +6 -1
- package/dist/agent/src/chat/client/index.js +4 -1
- package/dist/agent/src/chat/client/sessionClient.js +28 -9
- package/dist/agent/src/chat/constants.js +8 -0
- package/dist/agent/src/chat/data/dbSessionFiles.js +11 -6
- package/dist/agent/src/chat/data/dbSessionMessages.js +11 -0
- package/dist/agent/src/chat/protocol/messages.js +9 -0
- package/dist/agent/src/chat/server/chatContextManager.js +186 -156
- package/dist/agent/src/chat/server/conversation.js +3 -0
- package/dist/agent/src/chat/server/imageGeneratorTools.js +39 -16
- package/dist/agent/src/chat/server/openAIRouterLLM.js +111 -0
- package/dist/agent/src/chat/server/openSession.js +253 -91
- package/dist/agent/src/chat/server/promptRefiner.js +86 -0
- package/dist/agent/src/chat/server/server.js +10 -2
- package/dist/agent/src/chat/server/sessionFileManager.js +22 -221
- package/dist/agent/src/chat/server/sessionRegistry.js +152 -6
- package/dist/agent/src/chat/server/sessionRegistry.test.js +1 -1
- package/dist/agent/src/chat/server/titleGenerator.js +112 -0
- package/dist/agent/src/chat/server/titleGenerator.test.js +113 -0
- package/dist/agent/src/chat/server/tools.js +64 -253
- package/dist/agent/src/chat/utils/approvalManager.js +6 -3
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +3 -0
- package/dist/agent/src/test/agent.test.js +16 -17
- package/dist/agent/src/test/chatContextManager.test.js +44 -30
- package/dist/agent/src/test/clientServerConnection.test.js +1 -2
- package/dist/agent/src/test/compressingContextManager.test.js +22 -36
- package/dist/agent/src/test/context.test.js +55 -17
- package/dist/agent/src/test/contextTestTools.js +87 -0
- package/dist/agent/src/test/dbMcpServerConfigs.test.js +4 -4
- package/dist/agent/src/test/dbSessionFiles.test.js +17 -17
- package/dist/agent/src/test/testTools.js +6 -1
- package/dist/agent/src/test/tools.test.js +27 -9
- package/dist/agent/src/tool/agentChat.js +5 -2
- package/dist/agent/src/tool/chatMain.js +56 -15
- package/dist/agent/src/tool/commandPrompt.js +2 -2
- package/dist/agent/src/tool/files.js +7 -8
- package/package.json +4 -1
- package/scripts/test_chat +195 -173
- package/src/agent/agent.ts +257 -137
- package/src/agent/agentUtils.ts +32 -20
- package/src/agent/compressingContextManager.ts +13 -44
- package/src/agent/context.ts +165 -159
- package/src/agent/contextWithWorkspace.ts +162 -0
- package/src/agent/documentSummarizer.ts +157 -0
- package/src/agent/dummyLLM.ts +27 -23
- package/src/agent/imageGenLLM.ts +28 -32
- package/src/agent/imageGenerator.ts +3 -18
- package/src/agent/llm.ts +2 -2
- package/src/agent/openAILLM.ts +17 -13
- package/src/agent/openAILLMStreaming.ts +99 -43
- package/src/agent/repeatLLM.ts +19 -7
- package/src/agent/sudoMcpServerManager.ts +41 -20
- package/src/agent/test_data/harrypotter.txt +6065 -0
- package/src/agent/tokenCounter.test.ts +243 -0
- package/src/agent/tokenCounter.ts +483 -0
- package/src/agent/toolSettings.ts +24 -0
- package/src/agent/tools/calculatorTool.ts +50 -0
- package/src/agent/tools/contentExtractors/pdfToText.ts +60 -0
- package/src/agent/tools/datetimeTool.ts +41 -0
- package/src/agent/tools/fileManager/fileManagerTool.ts +199 -0
- package/src/agent/tools/fileManager/index.ts +50 -0
- package/src/agent/tools/fileManager/memoryFileManager.ts +120 -0
- package/src/{chat/data → agent/tools/fileManager}/mimeTypes.ts +3 -1
- package/src/agent/tools/fileManager/prompt.ts +38 -0
- package/src/{chat/data/dbSessionFileModels.ts → agent/tools/fileManager/types.ts} +76 -0
- package/src/agent/tools/index.ts +49 -0
- package/src/agent/tools/openUrlTool.ts +62 -0
- package/src/agent/tools/renderTool.ts +92 -0
- package/src/agent/tools/utils.ts +74 -0
- package/src/{chat/utils/search.ts → agent/tools/webSearch.ts} +0 -1
- package/src/agent/tools/webSearchTool.ts +44 -0
- package/src/chat/client/chatClient.ts +92 -3
- package/src/chat/client/connection.ts +11 -1
- package/src/chat/client/index.ts +3 -0
- package/src/chat/client/sessionClient.ts +40 -11
- package/src/chat/client/sessionFiles.ts +1 -1
- package/src/chat/constants.ts +6 -0
- package/src/chat/data/dataModels.ts +12 -0
- package/src/chat/data/dbSessionFiles.ts +12 -4
- package/src/chat/data/dbSessionMessages.ts +34 -0
- package/src/chat/protocol/messages.ts +94 -14
- package/src/chat/server/chatContextManager.ts +255 -221
- package/src/chat/server/connectionManager.ts +1 -1
- package/src/chat/server/conversation.ts +3 -0
- package/src/chat/server/imageGeneratorTools.ts +62 -30
- package/src/chat/server/openAIRouterLLM.ts +168 -0
- package/src/chat/server/openSession.ts +381 -138
- package/src/chat/server/promptRefiner.ts +106 -0
- package/src/chat/server/server.ts +9 -2
- package/src/chat/server/sessionFileManager.ts +35 -306
- package/src/chat/server/sessionRegistry.test.ts +0 -1
- package/src/chat/server/sessionRegistry.ts +228 -4
- package/src/chat/server/titleGenerator.test.ts +103 -0
- package/src/chat/server/titleGenerator.ts +143 -0
- package/src/chat/server/tools.ts +92 -281
- package/src/chat/utils/approvalManager.ts +9 -3
- package/src/chat/utils/multiAsyncQueue.ts +4 -0
- package/src/test/agent.test.ts +25 -30
- package/src/test/chatContextManager.test.ts +68 -38
- package/src/test/clientServerConnection.test.ts +0 -2
- package/src/test/compressingContextManager.test.ts +29 -34
- package/src/test/context.test.ts +59 -15
- package/src/test/contextTestTools.ts +95 -0
- package/src/test/dbMcpServerConfigs.test.ts +4 -4
- package/src/test/dbSessionFiles.test.ts +16 -16
- package/src/test/testTools.ts +8 -3
- package/src/test/tools.test.ts +30 -5
- package/src/tool/agentChat.ts +12 -3
- package/src/tool/chatMain.ts +59 -18
- package/src/tool/commandPrompt.ts +2 -2
- package/src/tool/files.ts +1 -3
- package/dist/agent/src/agent/tools.js +0 -44
- package/src/agent/tools.ts +0 -57
- /package/dist/agent/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.js +0 -0
- /package/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Agent = exports.DEFAULT_LLM_URL = exports.AgentProfile = void 0;
|
|
3
|
+
exports.Agent = exports.AgentEx = exports.USER_STOP_MESSAGE = exports.DEFAULT_LLM_URL = exports.AgentProfile = void 0;
|
|
4
4
|
exports.createUserMessage = createUserMessage;
|
|
5
5
|
exports.createUserMessageEnsure = createUserMessageEnsure;
|
|
6
6
|
exports.completionToAssistantMessageParam = completionToAssistantMessageParam;
|
|
@@ -9,52 +9,49 @@ var sdk_1 = require("@xalia/xmcp/sdk");
|
|
|
9
9
|
Object.defineProperty(exports, "AgentProfile", { enumerable: true, get: function () { return sdk_1.AgentProfile; } });
|
|
10
10
|
const sdk_2 = require("@xalia/xmcp/sdk");
|
|
11
11
|
const mcpServerManager_1 = require("./mcpServerManager");
|
|
12
|
+
const toolSettings_1 = require("./toolSettings");
|
|
12
13
|
exports.DEFAULT_LLM_URL = "http://localhost:5001/v1";
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The message to append to the agent output if the agent is interrupted by a
|
|
16
|
+
* signal from the user.
|
|
17
|
+
*/
|
|
18
|
+
exports.USER_STOP_MESSAGE = " AGENT INTERRUPTED";
|
|
14
19
|
const logger = (0, sdk_2.getLogger)();
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
/**
|
|
21
|
+
* An agent attached to an ILLM which updates a context via an
|
|
22
|
+
* IContextTransaction interface (where IContextTransaction is like a DB tx or
|
|
23
|
+
* DB writer, for staging changes and reading back state as-if those changes
|
|
24
|
+
* were applied).
|
|
25
|
+
*/
|
|
26
|
+
class AgentEx {
|
|
27
|
+
constructor(mcpServerManager, llm) {
|
|
17
28
|
/// The full list of tools, ready to pass to the LLM
|
|
18
29
|
this.tools = [];
|
|
19
30
|
/// Handlers for "agent" (or "built-in") tools. These do not require
|
|
20
31
|
/// approval from the user.
|
|
21
32
|
this.agentTools = new Map();
|
|
22
|
-
this.eventHandler = eventHandler;
|
|
23
33
|
this.mcpServerManager = mcpServerManager;
|
|
24
34
|
this.llm = llm;
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
static initializeWithLLM(eventHandler, llm, contextManager, mcpServerManager) {
|
|
28
|
-
return new Agent(eventHandler, mcpServerManager ?? new mcpServerManager_1.McpServerManager(), llm, contextManager);
|
|
35
|
+
this.stopFlag = false;
|
|
36
|
+
this.stopFn = undefined;
|
|
29
37
|
}
|
|
30
38
|
async shutdown() {
|
|
39
|
+
this.stop("shutting down");
|
|
31
40
|
return this.mcpServerManager.shutdown();
|
|
32
41
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
(0, assert_1.strict)(llmMessages[0].role === "system", "first message must have system role");
|
|
39
|
-
return [...llmMessages.slice(1)];
|
|
42
|
+
stop(msg) {
|
|
43
|
+
this.stopFlag = true;
|
|
44
|
+
if (this.stopFn) {
|
|
45
|
+
this.stopFn(msg || exports.USER_STOP_MESSAGE);
|
|
46
|
+
}
|
|
40
47
|
}
|
|
41
48
|
getMcpServerManager() {
|
|
42
49
|
return this.mcpServerManager;
|
|
43
50
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const userMessage = createUserMessage(msg, imageB64, name);
|
|
49
|
-
if (!userMessage) {
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
52
|
-
return this.userMessageRaw(userMessage);
|
|
53
|
-
}
|
|
54
|
-
async userMessageRaw(userMessage) {
|
|
55
|
-
return this.userMessagesRaw([userMessage]);
|
|
56
|
-
}
|
|
57
|
-
async userMessagesRaw(userMessages) {
|
|
51
|
+
// TODO: rename
|
|
52
|
+
async userMessagesRaw(contextTx, eventHandler) {
|
|
53
|
+
this.stopFlag = false;
|
|
54
|
+
// New user messages have already been added to the `contextTx`.
|
|
58
55
|
// Image and audio handling
|
|
59
56
|
//
|
|
60
57
|
// `ChatCompletions` (responses from the LLM) can contain `audio` and
|
|
@@ -63,19 +60,19 @@ class Agent {
|
|
|
63
60
|
//
|
|
64
61
|
// As such, our current approach is to extract all assistant-generated
|
|
65
62
|
// media and return it separately.
|
|
66
|
-
// Note: `getLLMContext` returns a copy to we can mutate this array
|
|
67
|
-
const context = this.contextManager.getLLMContext();
|
|
68
|
-
const newMessagesIdx = context.length;
|
|
69
|
-
// Add the new user messages
|
|
70
|
-
context.push(...userMessages);
|
|
71
63
|
const images = [];
|
|
72
64
|
// We convert the `ChatCompletionsMessage` into a
|
|
73
65
|
// `ChatCompletionAssistantMessageParam` and extract image data.
|
|
74
|
-
let completion = await this.chatCompletion(
|
|
75
|
-
let message = this.processCompletion(completion, images);
|
|
76
|
-
|
|
66
|
+
let completion = await this.chatCompletion(contextTx.getLLMContext(), eventHandler);
|
|
67
|
+
let message = this.processCompletion(completion, images, eventHandler);
|
|
68
|
+
contextTx.addMessage(message);
|
|
77
69
|
// While there are tool calls to make, invoke them and loop
|
|
78
70
|
while (message.tool_calls && message.tool_calls.length > 0) {
|
|
71
|
+
// Signal the event handler of the assistant message with tool calls
|
|
72
|
+
// BEFORE processing tool results. This ensures the order of messages
|
|
73
|
+
// in pendingMessages matches the order in the LLM context:
|
|
74
|
+
// [user, assistant(tool_calls), tool_result, assistant(final)]
|
|
75
|
+
eventHandler.onCompletion(message);
|
|
79
76
|
// TODO: Execute all tool calls in parallel
|
|
80
77
|
// [indexInContext, ToolCallResult][]
|
|
81
78
|
const toolCallResults = [];
|
|
@@ -83,8 +80,7 @@ class Agent {
|
|
|
83
80
|
// Execute the tool call, add the result to the context as an LLM
|
|
84
81
|
// mesage, and record the index of the message alongside the result in
|
|
85
82
|
// `toolCallResults`.
|
|
86
|
-
const result = await this.doToolCall(toolCall);
|
|
87
|
-
toolCallResults.push([context.length, result]);
|
|
83
|
+
const result = await this.doToolCall(toolCall, eventHandler);
|
|
88
84
|
const toolResult = {
|
|
89
85
|
role: "tool",
|
|
90
86
|
tool_call_id: toolCall.id,
|
|
@@ -94,7 +90,13 @@ class Agent {
|
|
|
94
90
|
? { structuredContent: result.structuredContent }
|
|
95
91
|
: {}),
|
|
96
92
|
};
|
|
97
|
-
|
|
93
|
+
const toolResultHandle = contextTx.addMessage(toolResult);
|
|
94
|
+
toolCallResults.push([toolResultHandle, result]);
|
|
95
|
+
// Immediately broadcast the tool result to the frontend for UI
|
|
96
|
+
// feedback. This ensures the frontend knows the tool executed
|
|
97
|
+
// successfully without waiting for the next LLM completion to
|
|
98
|
+
// finish streaming
|
|
99
|
+
eventHandler.onToolCallResult(toolResult);
|
|
98
100
|
// If the tool call requested that its args be redacted, this can be
|
|
99
101
|
// done now - before the next LLM invocation.
|
|
100
102
|
if (result.overwriteArgs) {
|
|
@@ -103,63 +105,57 @@ class Agent {
|
|
|
103
105
|
logger.debug(`agent message after update ${JSON.stringify(message)}`);
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
|
-
// Now that any args have been overwritten, signal the event handler of
|
|
107
|
-
// the prevoius completion.
|
|
108
|
-
this.eventHandler.onCompletion(message);
|
|
109
108
|
// Get a new completion using the untouched tool call results. Note
|
|
110
109
|
// that, since we are deferring the `onToolCallResult` calls (so they
|
|
111
110
|
// can be redacted), we must take care that the errors in
|
|
112
111
|
// `chatCompletion` do not disrupt this, so the caller has a consistent
|
|
113
112
|
// view of the conversation state.
|
|
114
113
|
try {
|
|
115
|
-
completion = await this.chatCompletion(
|
|
116
|
-
message = this.processCompletion(completion, images);
|
|
117
|
-
|
|
114
|
+
completion = await this.chatCompletion(contextTx.getLLMContext(), eventHandler);
|
|
115
|
+
message = this.processCompletion(completion, images, eventHandler);
|
|
116
|
+
contextTx.addMessage(message);
|
|
118
117
|
}
|
|
119
118
|
finally {
|
|
120
119
|
// Now that the tool call results have been passed to the LLM, perform
|
|
121
|
-
// any updates on them.
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
// context state when the error occured.
|
|
125
|
-
toolCallResults.forEach(([indexInContext, tcr]) => {
|
|
126
|
-
const ctxMsg = context[indexInContext];
|
|
120
|
+
// any updates on them if overwriteResponse was requested. If so, send
|
|
121
|
+
// the updated tool result to the frontend to replace the original.
|
|
122
|
+
toolCallResults.forEach(([handle, tcr]) => {
|
|
127
123
|
if (tcr.overwriteResponse) {
|
|
124
|
+
const ctxMsg = contextTx.getMessage(handle);
|
|
128
125
|
ctxMsg.content = tcr.overwriteResponse;
|
|
126
|
+
(0, assert_1.strict)(ctxMsg.role === "tool");
|
|
127
|
+
eventHandler.onToolCallResult(ctxMsg);
|
|
129
128
|
}
|
|
130
|
-
(0, assert_1.strict)(ctxMsg.role === "tool");
|
|
131
|
-
this.eventHandler.onToolCallResult(ctxMsg);
|
|
132
129
|
});
|
|
133
130
|
// Note, if an error DID occur, the ContextManager does not see any of
|
|
134
131
|
// the new context.
|
|
135
132
|
}
|
|
136
133
|
}
|
|
137
134
|
// Signal the event handler of the final completion.
|
|
138
|
-
|
|
139
|
-
// Add all new new messages to the context
|
|
140
|
-
this.contextManager.addMessages(context.slice(newMessagesIdx));
|
|
135
|
+
eventHandler.onCompletion(message);
|
|
141
136
|
return { message, images: images.length === 0 ? undefined : images };
|
|
142
137
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
138
|
+
async chatCompletion(context, eventHandler) {
|
|
139
|
+
if (this.stopFlag) {
|
|
140
|
+
return {
|
|
141
|
+
id: "user_stopped",
|
|
142
|
+
choices: [
|
|
143
|
+
{
|
|
144
|
+
finish_reason: "stop",
|
|
145
|
+
index: 0,
|
|
146
|
+
message: {
|
|
147
|
+
content: exports.USER_STOP_MESSAGE,
|
|
148
|
+
role: "assistant",
|
|
149
|
+
refusal: null,
|
|
150
|
+
},
|
|
151
|
+
logprobs: null,
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
created: Date.now(),
|
|
155
|
+
model: this.llm.getModel(),
|
|
156
|
+
object: "chat.completion",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
163
159
|
// Compute the full list of available tools
|
|
164
160
|
let tools;
|
|
165
161
|
const mcpTools = this.mcpServerManager.getOpenAITools();
|
|
@@ -169,7 +165,15 @@ class Agent {
|
|
|
169
165
|
tools = enabledTools;
|
|
170
166
|
}
|
|
171
167
|
logger.debug(`[chatCompletion] tools: ${JSON.stringify(tools)}`);
|
|
172
|
-
|
|
168
|
+
// Log system prompt length
|
|
169
|
+
if (context.length > 0 && context[0].role === "system") {
|
|
170
|
+
const systemPrompt = context[0].content;
|
|
171
|
+
logger.info(`[chatCompletion] System prompt length: ${String(systemPrompt.length)}`);
|
|
172
|
+
}
|
|
173
|
+
const { stop, completion: completionP } = await this.llm.getConversationResponse(context, tools, eventHandler.onAgentMessage.bind(eventHandler), eventHandler.onReasoning.bind(eventHandler));
|
|
174
|
+
this.stopFn = stop;
|
|
175
|
+
const completion = await completionP;
|
|
176
|
+
this.stopFn = undefined;
|
|
173
177
|
logger.debug(`Received chat completion ${JSON.stringify(completion)}`);
|
|
174
178
|
return completion;
|
|
175
179
|
}
|
|
@@ -210,7 +214,10 @@ class Agent {
|
|
|
210
214
|
* handler, informing the IAgentEventHandler of the result, and returns the
|
|
211
215
|
* ChatCompletionToolMessageParam to be used in the conversation.
|
|
212
216
|
*/
|
|
213
|
-
async doToolCall(toolCall) {
|
|
217
|
+
async doToolCall(toolCall, eventHandler) {
|
|
218
|
+
if (this.stopFlag) {
|
|
219
|
+
return { response: exports.USER_STOP_MESSAGE };
|
|
220
|
+
}
|
|
214
221
|
// If the tool is and "agent" (internal) tool, we can just execute it.
|
|
215
222
|
// Otherwise, call the event handler to get permission and invoke the
|
|
216
223
|
// external tool handler.
|
|
@@ -221,7 +228,7 @@ class Agent {
|
|
|
221
228
|
const isAgentTool = !!agentTool;
|
|
222
229
|
if (isAgentTool) {
|
|
223
230
|
// Internal (agent) tool
|
|
224
|
-
if (!(await
|
|
231
|
+
if (!(await eventHandler.onToolCall(toolCall, true))) {
|
|
225
232
|
result = { response: "User denied tool request." };
|
|
226
233
|
}
|
|
227
234
|
else {
|
|
@@ -234,7 +241,7 @@ class Agent {
|
|
|
234
241
|
// tool call data, get approval, and then invoke.
|
|
235
242
|
const args = JSON.parse(toolCall.function.arguments || "{}");
|
|
236
243
|
const tc = this.mcpServerManager.verifyToolCall(toolName, args);
|
|
237
|
-
if (!(await
|
|
244
|
+
if (!(await eventHandler.onToolCall(toolCall, false))) {
|
|
238
245
|
result = { response: "User denied tool request." };
|
|
239
246
|
}
|
|
240
247
|
else {
|
|
@@ -260,27 +267,102 @@ class Agent {
|
|
|
260
267
|
};
|
|
261
268
|
}
|
|
262
269
|
// Final sanity check on the tool call response length.
|
|
263
|
-
if (result.response.length > MAX_TOOL_CALL_RESPONSE_LENGTH) {
|
|
270
|
+
if (result.response.length > toolSettings_1.MAX_TOOL_CALL_RESPONSE_LENGTH) {
|
|
264
271
|
logger.warn("[Agent.doToolCall]: truncating tool call result.response for call:\n" +
|
|
265
272
|
JSON.stringify(toolCall));
|
|
266
273
|
result.response =
|
|
267
|
-
result.response.slice(0, MAX_TOOL_CALL_RESPONSE_LENGTH) +
|
|
274
|
+
result.response.slice(0, toolSettings_1.MAX_TOOL_CALL_RESPONSE_LENGTH) +
|
|
268
275
|
" ..truncated";
|
|
269
276
|
}
|
|
270
277
|
return result;
|
|
271
278
|
}
|
|
272
|
-
processCompletion(completion, images) {
|
|
279
|
+
processCompletion(completion, images, eventHandler) {
|
|
273
280
|
// Add any images into the list, and call the event handler
|
|
274
281
|
const compMessage = completion.choices[0].message;
|
|
275
282
|
if (compMessage.images) {
|
|
276
283
|
for (const image of compMessage.images) {
|
|
277
|
-
|
|
284
|
+
eventHandler.onImage(image);
|
|
278
285
|
images.push(image);
|
|
279
286
|
}
|
|
280
287
|
}
|
|
281
288
|
return completionToAssistantMessageParam(compMessage);
|
|
282
289
|
}
|
|
283
290
|
}
|
|
291
|
+
exports.AgentEx = AgentEx;
|
|
292
|
+
/**
|
|
293
|
+
* Higher-level abstraction over AgentEx, which abstracts out the context
|
|
294
|
+
* transactions. A single agent is associated with an IContextManager and
|
|
295
|
+
* internally creates and commits transactions during each call to
|
|
296
|
+
* `userMessage*`.
|
|
297
|
+
*/
|
|
298
|
+
class Agent {
|
|
299
|
+
constructor(eventHandler, mcpServerManager, llm, contextManager) {
|
|
300
|
+
this.eventHandler = eventHandler;
|
|
301
|
+
this.contextManager = contextManager;
|
|
302
|
+
this.agentEx = new AgentEx(mcpServerManager, llm);
|
|
303
|
+
}
|
|
304
|
+
static initializeWithLLM(eventHandler, llm, contextManager, mcpServerManager) {
|
|
305
|
+
return new Agent(eventHandler, mcpServerManager ?? new mcpServerManager_1.McpServerManager(), llm, contextManager);
|
|
306
|
+
}
|
|
307
|
+
async shutdown() {
|
|
308
|
+
return this.agentEx.shutdown();
|
|
309
|
+
}
|
|
310
|
+
getAgentProfile() {
|
|
311
|
+
return new sdk_2.AgentProfile(this.agentEx.llm.getModel(), this.getSystemPrompt(), this.agentEx.mcpServerManager.getMcpServerSettings());
|
|
312
|
+
}
|
|
313
|
+
getConversation() {
|
|
314
|
+
const llmMessages = this.contextManager.getLLMContext();
|
|
315
|
+
(0, assert_1.strict)(llmMessages[0].role === "system", "first message must have system role");
|
|
316
|
+
return [...llmMessages.slice(1)];
|
|
317
|
+
}
|
|
318
|
+
getMcpServerManager() {
|
|
319
|
+
return this.agentEx.mcpServerManager;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Like `userMessage`, but can be awaited, and accepts the user name.
|
|
323
|
+
*/
|
|
324
|
+
async userMessageEx(msg, imageB64, name) {
|
|
325
|
+
const userMessage = createUserMessage(msg, imageB64, name);
|
|
326
|
+
if (!userMessage) {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
return this.userMessageRaw(userMessage);
|
|
330
|
+
}
|
|
331
|
+
async userMessageRaw(userMessage) {
|
|
332
|
+
return this.userMessagesRaw([userMessage]);
|
|
333
|
+
}
|
|
334
|
+
async userMessagesRaw(userMessages) {
|
|
335
|
+
const tx = await this.contextManager.startTx(userMessages);
|
|
336
|
+
const result = await this.agentEx.userMessagesRaw(tx, this.eventHandler);
|
|
337
|
+
await this.contextManager.commit(tx);
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
userMessage(msg, imageB64) {
|
|
341
|
+
void this.userMessageEx(msg, imageB64);
|
|
342
|
+
}
|
|
343
|
+
getModel() {
|
|
344
|
+
return this.agentEx.llm.getModel();
|
|
345
|
+
}
|
|
346
|
+
setModel(model) {
|
|
347
|
+
logger.debug(`Set model ${model}`);
|
|
348
|
+
this.agentEx.llm.setModel(model);
|
|
349
|
+
}
|
|
350
|
+
getSystemPrompt() {
|
|
351
|
+
return this.contextManager.getAgentPrompt();
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Set the system prompt
|
|
355
|
+
*/
|
|
356
|
+
setSystemPrompt(systemMsg) {
|
|
357
|
+
this.contextManager.setAgentPrompt(systemMsg);
|
|
358
|
+
}
|
|
359
|
+
addAgentToolProvider(toolProvider) {
|
|
360
|
+
return this.agentEx.addAgentToolProvider(toolProvider);
|
|
361
|
+
}
|
|
362
|
+
addAgentTool(tool, handler) {
|
|
363
|
+
this.agentEx.addAgentTool(tool, handler);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
284
366
|
exports.Agent = Agent;
|
|
285
367
|
/**
|
|
286
368
|
* Returns the ChatCompletionMessageParam constructed from (optional) text and
|
|
@@ -323,7 +405,7 @@ function createUserMessage(msg, imageB64, name) {
|
|
|
323
405
|
}
|
|
324
406
|
function createUserMessageEnsure(msg, imageB64, name) {
|
|
325
407
|
const userMsg = createUserMessage(msg, imageB64, name);
|
|
326
|
-
(0, assert_1.strict)(userMsg);
|
|
408
|
+
(0, assert_1.strict)(userMsg, "createUserMessageEnsure");
|
|
327
409
|
return userMsg;
|
|
328
410
|
}
|
|
329
411
|
function completionToAssistantMessageParam(compMessage) {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createAgentWithoutSkills = createAgentWithoutSkills;
|
|
4
4
|
exports.createAgentWithSkills = createAgentWithSkills;
|
|
5
5
|
exports.createAgentFromSkillManager = createAgentFromSkillManager;
|
|
6
|
+
exports.createSpecializedLLM = createSpecializedLLM;
|
|
6
7
|
exports.createLLM = createLLM;
|
|
7
8
|
exports.createNonInteractiveAgent = createNonInteractiveAgent;
|
|
8
9
|
exports.runOneShot = runOneShot;
|
|
@@ -15,7 +16,6 @@ const dummyLLM_1 = require("./dummyLLM");
|
|
|
15
16
|
const assert_1 = require("assert");
|
|
16
17
|
const repeatLLM_1 = require("./repeatLLM");
|
|
17
18
|
const context_1 = require("./context");
|
|
18
|
-
const imageGenLLM_1 = require("./imageGenLLM");
|
|
19
19
|
const logger = (0, sdk_1.getLogger)();
|
|
20
20
|
async function createAgentWithoutSkills(llmUrl, model, eventHandler, platform, contextManager, llmApiKey, sudomcpConfig, authorizedUrl, stream = false) {
|
|
21
21
|
// Init SudoMcpServerManager
|
|
@@ -46,30 +46,36 @@ async function createAgentFromSkillManager(llmUrl, model, eventHandler, platform
|
|
|
46
46
|
logger.debug("[createAgentFromSkillManager] done");
|
|
47
47
|
return agent;
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Interpret the `model` string to create a specialized agent (dummy, repeat,
|
|
51
|
+
* etc) or return undefined if a specialized agent has not been requested.
|
|
52
|
+
*/
|
|
53
|
+
async function createSpecializedLLM(model, platform) {
|
|
50
54
|
let llm;
|
|
51
55
|
if (model && model.startsWith("dummy:")) {
|
|
52
56
|
llm = await dummyLLM_1.DummyLLM.initFromModelUrl(model, platform);
|
|
53
57
|
}
|
|
54
|
-
else if (model
|
|
55
|
-
|
|
58
|
+
else if (model && model.startsWith("repeat")) {
|
|
59
|
+
const prefix = model.startsWith("repeat:") ? model.slice(7) : "";
|
|
60
|
+
llm = new repeatLLM_1.RepeatLLM(prefix);
|
|
61
|
+
}
|
|
62
|
+
return llm;
|
|
63
|
+
}
|
|
64
|
+
async function createLLM(llmUrl, llmApiKey, model, stream = false, platform) {
|
|
65
|
+
let llm = await createSpecializedLLM(model, platform);
|
|
66
|
+
if (llm) {
|
|
67
|
+
return llm;
|
|
68
|
+
}
|
|
69
|
+
// Regular Agent
|
|
70
|
+
if (!llmApiKey) {
|
|
71
|
+
throw new Error("Missing OpenAI API Key");
|
|
56
72
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
llm = new
|
|
73
|
+
logger.debug(`Initializing Agent: ${llmUrl ?? "unknown"} - ${model}`);
|
|
74
|
+
if (stream) {
|
|
75
|
+
llm = new openAILLMStreaming_1.OpenAILLMStreaming(llmApiKey, llmUrl, model);
|
|
60
76
|
}
|
|
61
77
|
else {
|
|
62
|
-
|
|
63
|
-
if (!llmApiKey) {
|
|
64
|
-
throw new Error("Missing OpenAI API Key");
|
|
65
|
-
}
|
|
66
|
-
logger.debug(`Initializing Agent: ${llmUrl ?? "unknown"} - ${model}`);
|
|
67
|
-
if (stream) {
|
|
68
|
-
llm = new openAILLMStreaming_1.OpenAILLMStreaming(llmApiKey, llmUrl, model);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
llm = new openAILLM_1.OpenAILLM(llmApiKey, llmUrl, model);
|
|
72
|
-
}
|
|
78
|
+
llm = new openAILLM_1.OpenAILLM(llmApiKey, llmUrl, model);
|
|
73
79
|
}
|
|
74
80
|
(0, assert_1.strict)(llm);
|
|
75
81
|
return llm;
|
|
@@ -7,10 +7,9 @@ exports.createSummary = createSummary;
|
|
|
7
7
|
const assert_1 = require("assert");
|
|
8
8
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
9
9
|
const agent_1 = require("./agent");
|
|
10
|
-
const nullPlatform_1 = require("./nullPlatform");
|
|
11
|
-
const agentUtils_1 = require("./agentUtils");
|
|
12
10
|
const context_1 = require("./context");
|
|
13
11
|
const nullAgentEventHandler_1 = require("./nullAgentEventHandler");
|
|
12
|
+
const contextWithWorkspace_1 = require("./contextWithWorkspace");
|
|
14
13
|
const logger = (0, sdk_1.getLogger)();
|
|
15
14
|
/**
|
|
16
15
|
* System prompt used to generate a conversation summary.
|
|
@@ -30,12 +29,11 @@ function createCheckpointMessage(summary) {
|
|
|
30
29
|
content: CHECKPOINT_MESSAGE_PREFIX + summary,
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
|
-
|
|
34
|
-
const llm = await (0, agentUtils_1.createLLM)(compressionAgentUrl, compressionAgentApiKey, compressionAgentModel, false /* stream */, nullPlatform_1.NULL_PLATFORM);
|
|
32
|
+
function createCompressionAgent(llm) {
|
|
35
33
|
return agent_1.Agent.initializeWithLLM(nullAgentEventHandler_1.NULL_AGENT_EVENT_HANDLER, llm, new context_1.ContextManager(COMPRESSION_SYSTEM_PROMPT, []));
|
|
36
34
|
}
|
|
37
|
-
async function createSummary(
|
|
38
|
-
const agent =
|
|
35
|
+
async function createSummary(llm, conversation) {
|
|
36
|
+
const agent = createCompressionAgent(llm);
|
|
39
37
|
const agentResp = await agent.userMessageEx(JSON.stringify(conversation));
|
|
40
38
|
if (!agentResp) {
|
|
41
39
|
throw new Error("compression agent returned null");
|
|
@@ -50,12 +48,10 @@ async function createSummary(compressionAgentUrl, compressionAgentModel, compres
|
|
|
50
48
|
* the Agent) is responsible for committing the conversation and triggering
|
|
51
49
|
* compression.
|
|
52
50
|
*/
|
|
53
|
-
class CompressingContextManager extends
|
|
54
|
-
constructor(systemPrompt, messages,
|
|
51
|
+
class CompressingContextManager extends contextWithWorkspace_1.ContextManagerWithWorkspace {
|
|
52
|
+
constructor(systemPrompt, messages, getLLM) {
|
|
55
53
|
super(systemPrompt, messages);
|
|
56
|
-
this.
|
|
57
|
-
this.compressionAgentModel = compressionAgentModel;
|
|
58
|
-
this.compressionAgentApiKey = compressionAgentApiKey;
|
|
54
|
+
this.getLLM = getLLM;
|
|
59
55
|
this.compressingMessages = undefined;
|
|
60
56
|
// Sanity check the conversation form.
|
|
61
57
|
//
|
|
@@ -77,15 +73,15 @@ class CompressingContextManager extends context_1.ContextManagerWithCommit {
|
|
|
77
73
|
}
|
|
78
74
|
}
|
|
79
75
|
async compress() {
|
|
80
|
-
|
|
81
|
-
const numToCompress = this.getCommittedLength();
|
|
76
|
+
const numToCompress = super.numMessages();
|
|
82
77
|
const messagesToCompress = this.leadingMessages(numToCompress);
|
|
83
78
|
(0, assert_1.strict)(messagesToCompress.length === numToCompress);
|
|
84
79
|
this.compressingMessages = numToCompress;
|
|
85
80
|
(0, assert_1.strict)(this.compressingMessages > 1, "<2 messages commited in the context");
|
|
86
81
|
logger.debug(`[CompressingContextManager] start (${String(this.compressingMessages)})`);
|
|
87
82
|
try {
|
|
88
|
-
const
|
|
83
|
+
const llm = await this.getLLM();
|
|
84
|
+
const summary = await createSummary(llm, messagesToCompress);
|
|
89
85
|
logger.debug(`[CompressingContextManager] summary: ${summary}`);
|
|
90
86
|
// Replace the context `messages` and update `lastCommittedMessage`
|
|
91
87
|
// index.
|