@xalia/agent 0.5.7 → 0.6.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/README.md +23 -8
- package/dist/agent/src/agent/agent.js +176 -96
- package/dist/agent/src/agent/agentUtils.js +82 -59
- package/dist/agent/src/agent/compressingContextManager.js +102 -0
- package/dist/agent/src/agent/context.js +189 -0
- package/dist/agent/src/agent/dummyLLM.js +46 -5
- package/dist/agent/src/agent/mcpServerManager.js +23 -24
- package/dist/agent/src/agent/nullAgentEventHandler.js +21 -0
- package/dist/agent/src/agent/nullPlatform.js +14 -0
- package/dist/agent/src/agent/openAILLMStreaming.js +26 -14
- package/dist/agent/src/agent/promptProvider.js +63 -0
- package/dist/agent/src/agent/repeatLLM.js +5 -5
- package/dist/agent/src/agent/sudoMcpServerManager.js +23 -21
- package/dist/agent/src/agent/tokenAuth.js +7 -7
- package/dist/agent/src/agent/tools.js +1 -1
- package/dist/agent/src/chat/client/chatClient.js +733 -0
- package/dist/agent/src/chat/client/connection.js +209 -0
- package/dist/agent/src/chat/client/connection.test.js +188 -0
- package/dist/agent/src/chat/client/constants.js +5 -0
- package/dist/agent/src/chat/client/index.js +15 -0
- package/dist/agent/src/chat/client/interfaces.js +2 -0
- package/dist/agent/src/chat/client/responseHandler.js +105 -0
- package/dist/agent/src/chat/client/sessionClient.js +331 -0
- package/dist/agent/src/chat/client/teamManager.js +2 -0
- package/dist/agent/src/chat/{apiKeyManager.js → data/apiKeyManager.js} +4 -0
- package/dist/agent/src/chat/data/dataModels.js +2 -0
- package/dist/agent/src/chat/data/database.js +749 -0
- package/dist/agent/src/chat/data/dbMcpServerConfigs.js +47 -0
- package/dist/agent/src/chat/protocol/connectionMessages.js +5 -0
- package/dist/agent/src/chat/protocol/constants.js +50 -0
- package/dist/agent/src/chat/protocol/errors.js +22 -0
- package/dist/agent/src/chat/protocol/messages.js +110 -0
- package/dist/agent/src/chat/server/chatContextManager.js +405 -0
- package/dist/agent/src/chat/server/connectionManager.js +352 -0
- package/dist/agent/src/chat/server/connectionManager.test.js +159 -0
- package/dist/agent/src/chat/server/conversation.js +198 -0
- package/dist/agent/src/chat/server/errorUtils.js +23 -0
- package/dist/agent/src/chat/server/openSession.js +869 -0
- package/dist/agent/src/chat/server/server.js +177 -0
- package/dist/agent/src/chat/server/sessionFileManager.js +161 -0
- package/dist/agent/src/chat/server/sessionRegistry.js +700 -0
- package/dist/agent/src/chat/server/sessionRegistry.test.js +97 -0
- package/dist/agent/src/chat/server/test-utils/mockFactories.js +307 -0
- package/dist/agent/src/chat/server/tools.js +243 -0
- package/dist/agent/src/chat/utils/agentSessionMap.js +66 -0
- package/dist/agent/src/chat/utils/approvalManager.js +85 -0
- package/dist/agent/src/{utils → chat/utils}/asyncLock.js +3 -3
- package/dist/agent/src/chat/{asyncQueue.js → utils/asyncQueue.js} +12 -2
- package/dist/agent/src/chat/utils/htmlToText.js +84 -0
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +42 -0
- package/dist/agent/src/chat/utils/search.js +145 -0
- package/dist/agent/src/chat/utils/userResolver.js +46 -0
- package/dist/agent/src/chat/utils/websocket.js +16 -0
- package/dist/agent/src/test/agent.test.js +332 -0
- package/dist/agent/src/test/approvalManager.test.js +58 -0
- package/dist/agent/src/test/chatContextManager.test.js +392 -0
- package/dist/agent/src/test/clientServerConnection.test.js +158 -0
- package/dist/agent/src/test/compressingContextManager.test.js +65 -0
- package/dist/agent/src/test/context.test.js +83 -0
- package/dist/agent/src/test/conversation.test.js +89 -0
- package/dist/agent/src/test/db.test.js +271 -83
- package/dist/agent/src/test/dbMcpServerConfigs.test.js +72 -0
- package/dist/agent/src/test/dbTestTools.js +99 -0
- package/dist/agent/src/test/imageLoad.test.js +8 -7
- package/dist/agent/src/test/mcpServerManager.test.js +23 -20
- package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
- package/dist/agent/src/test/openaiStreaming.test.js +64 -35
- package/dist/agent/src/test/prompt.test.js +5 -4
- package/dist/agent/src/test/promptProvider.test.js +28 -0
- package/dist/agent/src/test/responseHandler.test.js +61 -0
- package/dist/agent/src/test/sudoMcpServerManager.test.js +24 -25
- package/dist/agent/src/test/testTools.js +109 -0
- package/dist/agent/src/test/tools.test.js +31 -0
- package/dist/agent/src/tool/agentChat.js +21 -10
- package/dist/agent/src/tool/agentMain.js +1 -1
- package/dist/agent/src/tool/chatMain.js +241 -58
- package/dist/agent/src/tool/commandPrompt.js +22 -17
- package/dist/agent/src/tool/files.js +20 -16
- package/dist/agent/src/tool/nodePlatform.js +47 -3
- package/dist/agent/src/tool/options.js +4 -4
- package/dist/agent/src/tool/prompt.js +19 -13
- package/eslint.config.mjs +14 -1
- package/package.json +14 -6
- package/scripts/chat_server +8 -0
- package/scripts/setup_chat +7 -2
- package/scripts/shutdown_chat_server +3 -0
- package/scripts/test_chat +135 -17
- package/src/agent/agent.ts +283 -138
- package/src/agent/agentUtils.ts +143 -108
- package/src/agent/compressingContextManager.ts +164 -0
- package/src/agent/context.ts +268 -0
- package/src/agent/dummyLLM.ts +76 -8
- package/src/agent/iAgentEventHandler.ts +54 -0
- package/src/agent/iplatform.ts +1 -0
- package/src/agent/mcpServerManager.ts +35 -31
- package/src/agent/nullAgentEventHandler.ts +20 -0
- package/src/agent/nullPlatform.ts +13 -0
- package/src/agent/openAILLMStreaming.ts +26 -13
- package/src/agent/promptProvider.ts +87 -0
- package/src/agent/repeatLLM.ts +5 -5
- package/src/agent/sudoMcpServerManager.ts +30 -29
- package/src/agent/tokenAuth.ts +7 -7
- package/src/agent/tools.ts +3 -1
- package/src/chat/client/chatClient.ts +900 -0
- package/src/chat/client/connection.test.ts +241 -0
- package/src/chat/client/connection.ts +276 -0
- package/src/chat/client/constants.ts +3 -0
- package/src/chat/client/index.ts +18 -0
- package/src/chat/client/interfaces.ts +34 -0
- package/src/chat/client/responseHandler.ts +131 -0
- package/src/chat/client/sessionClient.ts +443 -0
- package/src/chat/client/teamManager.ts +29 -0
- package/src/chat/{apiKeyManager.ts → data/apiKeyManager.ts} +6 -2
- package/src/chat/data/dataModels.ts +85 -0
- package/src/chat/data/database.ts +982 -0
- package/src/chat/data/dbMcpServerConfigs.ts +59 -0
- package/src/chat/protocol/connectionMessages.ts +49 -0
- package/src/chat/protocol/constants.ts +55 -0
- package/src/chat/protocol/errors.ts +16 -0
- package/src/chat/protocol/messages.ts +682 -0
- package/src/chat/server/README.md +127 -0
- package/src/chat/server/chatContextManager.ts +612 -0
- package/src/chat/server/connectionManager.test.ts +266 -0
- package/src/chat/server/connectionManager.ts +541 -0
- package/src/chat/server/conversation.ts +269 -0
- package/src/chat/server/errorUtils.ts +28 -0
- package/src/chat/server/openSession.ts +1332 -0
- package/src/chat/server/server.ts +177 -0
- package/src/chat/server/sessionFileManager.ts +239 -0
- package/src/chat/server/sessionRegistry.test.ts +138 -0
- package/src/chat/server/sessionRegistry.ts +1064 -0
- package/src/chat/server/test-utils/mockFactories.ts +422 -0
- package/src/chat/server/tools.ts +265 -0
- package/src/chat/utils/agentSessionMap.ts +76 -0
- package/src/chat/utils/approvalManager.ts +111 -0
- package/src/{utils → chat/utils}/asyncLock.ts +3 -3
- package/src/chat/{asyncQueue.ts → utils/asyncQueue.ts} +14 -3
- package/src/chat/utils/htmlToText.ts +61 -0
- package/src/chat/utils/multiAsyncQueue.ts +52 -0
- package/src/chat/utils/search.ts +139 -0
- package/src/chat/utils/userResolver.ts +48 -0
- package/src/chat/utils/websocket.ts +16 -0
- package/src/test/agent.test.ts +487 -0
- package/src/test/approvalManager.test.ts +73 -0
- package/src/test/chatContextManager.test.ts +521 -0
- package/src/test/clientServerConnection.test.ts +207 -0
- package/src/test/compressingContextManager.test.ts +82 -0
- package/src/test/context.test.ts +105 -0
- package/src/test/conversation.test.ts +109 -0
- package/src/test/db.test.ts +358 -89
- package/src/test/dbMcpServerConfigs.test.ts +112 -0
- package/src/test/dbTestTools.ts +153 -0
- package/src/test/imageLoad.test.ts +7 -6
- package/src/test/mcpServerManager.test.ts +21 -16
- package/src/test/multiAsyncQueue.test.ts +125 -0
- package/src/test/openaiStreaming.test.ts +71 -36
- package/src/test/prompt.test.ts +4 -3
- package/src/test/promptProvider.test.ts +33 -0
- package/src/test/responseHandler.test.ts +78 -0
- package/src/test/sudoMcpServerManager.test.ts +32 -30
- package/src/test/testTools.ts +146 -0
- package/src/test/tools.test.ts +39 -0
- package/src/tool/agentChat.ts +26 -12
- package/src/tool/agentMain.ts +1 -1
- package/src/tool/chatMain.ts +292 -100
- package/src/tool/commandPrompt.ts +28 -19
- package/src/tool/files.ts +25 -19
- package/src/tool/nodePlatform.ts +52 -3
- package/src/tool/options.ts +4 -2
- package/src/tool/prompt.ts +22 -15
- package/test_data/dummyllm_script_crash.json +32 -0
- package/test_data/frog.png.b64 +1 -0
- package/vitest.config.ts +39 -0
- package/dist/agent/src/chat/client.js +0 -349
- package/dist/agent/src/chat/conversationManager.js +0 -392
- package/dist/agent/src/chat/db.js +0 -209
- package/dist/agent/src/chat/frontendClient.js +0 -74
- package/dist/agent/src/chat/server.js +0 -158
- package/src/chat/client.ts +0 -455
- package/src/chat/conversationManager.ts +0 -595
- package/src/chat/db.ts +0 -290
- package/src/chat/frontendClient.ts +0 -123
- package/src/chat/messages.ts +0 -235
- package/src/chat/server.ts +0 -177
- /package/dist/agent/src/{chat/messages.js → agent/iAgentEventHandler.js} +0 -0
- /package/{frog.png → test_data/frog.png} +0 -0
package/src/agent/agentUtils.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getLogger } from "@xalia/xmcp/sdk";
|
|
2
|
-
import { Agent, AgentProfile
|
|
2
|
+
import { Agent, AgentProfile } from "./agent";
|
|
3
|
+
import { IAgentEventHandler } from "./iAgentEventHandler";
|
|
3
4
|
import { IPlatform } from "./iplatform";
|
|
4
5
|
import { SkillManager } from "./sudoMcpServerManager";
|
|
5
6
|
import OpenAI from "openai";
|
|
@@ -10,154 +11,159 @@ import { DummyLLM } from "./dummyLLM";
|
|
|
10
11
|
import { ILLM } from "./llm";
|
|
11
12
|
import { strict as assert } from "assert";
|
|
12
13
|
import { RepeatLLM } from "./repeatLLM";
|
|
13
|
-
import {
|
|
14
|
+
import { ContextManager, IContextManager } from "./context";
|
|
14
15
|
|
|
15
16
|
const logger = getLogger();
|
|
16
17
|
|
|
17
18
|
export const DEFAULT_LLM_URL = "http://localhost:5001/v1";
|
|
18
|
-
|
|
19
|
+
// uses openrouter
|
|
20
|
+
export const DEFAULT_LLM_MODEL =
|
|
21
|
+
process.env["DEFAULT_LLM_MODEL"] || "openai/gpt-4o";
|
|
19
22
|
|
|
20
23
|
export const XALIA_APP_HEADER = {
|
|
21
24
|
"HTTP-Referer": "xalia.ai",
|
|
22
25
|
"X-Title": "Xalia",
|
|
23
26
|
};
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
llmUrl: string | undefined,
|
|
30
|
-
model: string | undefined,
|
|
31
|
-
systemPrompt: string,
|
|
32
|
-
onMessage: OnMessageCB,
|
|
33
|
-
onToolCall: OnToolCallCB,
|
|
28
|
+
export async function createAgentWithoutSkills(
|
|
29
|
+
llmUrl: string,
|
|
30
|
+
agentProfile: AgentProfile,
|
|
31
|
+
eventHandler: IAgentEventHandler,
|
|
34
32
|
platform: IPlatform,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
// Regular Agent
|
|
56
|
-
if (!openaiApiKey) {
|
|
57
|
-
throw "Missing OpenAI API Key";
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
logger.debug(`Initializing Agent: ${llmUrl} - ${model}`);
|
|
61
|
-
if (stream) {
|
|
62
|
-
llm = new OpenAILLMStreaming(openaiApiKey, llmUrl, model);
|
|
63
|
-
} else {
|
|
64
|
-
llm = new OpenAILLM(openaiApiKey, llmUrl, model);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
33
|
+
contextManager: IContextManager,
|
|
34
|
+
llmApiKey: string | undefined,
|
|
35
|
+
sudomcpConfig: SudoMcpConfiguration,
|
|
36
|
+
authorizedUrl: string | undefined,
|
|
37
|
+
stream: boolean = false
|
|
38
|
+
): Promise<[Agent, SkillManager]> {
|
|
39
|
+
// Init SudoMcpServerManager
|
|
40
|
+
logger.debug("[createAgentWithSkills] creating SudoMcpServerManager.");
|
|
41
|
+
const sudoMcpServerManager = await SkillManager.initialize(
|
|
42
|
+
(url: string, authResultP: Promise<boolean>, displayName: string) => {
|
|
43
|
+
platform.openUrl(url, authResultP, displayName);
|
|
44
|
+
},
|
|
45
|
+
sudomcpConfig.backend_url,
|
|
46
|
+
sudomcpConfig.api_key,
|
|
47
|
+
authorizedUrl
|
|
48
|
+
);
|
|
49
|
+
logger.debug(
|
|
50
|
+
"[createAgentWithoutSkills] restore mcp settings:" +
|
|
51
|
+
JSON.stringify(agentProfile.mcp_settings)
|
|
52
|
+
);
|
|
67
53
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
54
|
+
// Create agent using the event handler
|
|
55
|
+
const agent = await createAgentFromSkillManager(
|
|
56
|
+
llmUrl,
|
|
57
|
+
agentProfile,
|
|
58
|
+
eventHandler,
|
|
59
|
+
platform,
|
|
60
|
+
contextManager,
|
|
61
|
+
llmApiKey,
|
|
62
|
+
sudoMcpServerManager,
|
|
63
|
+
stream
|
|
75
64
|
);
|
|
65
|
+
|
|
66
|
+
return [agent, sudoMcpServerManager];
|
|
76
67
|
}
|
|
77
68
|
|
|
78
69
|
/**
|
|
79
|
-
*
|
|
70
|
+
* Create and initialize an Agent given an AgentProfile using the
|
|
71
|
+
* IAgentEventHandler interface. This is the preferred way to create
|
|
72
|
+
* agents.
|
|
80
73
|
*/
|
|
81
74
|
export async function createAgentWithSkills(
|
|
82
75
|
llmUrl: string,
|
|
83
76
|
agentProfile: AgentProfile,
|
|
84
|
-
|
|
85
|
-
onToolCall: OnToolCallCB,
|
|
77
|
+
eventHandler: IAgentEventHandler,
|
|
86
78
|
platform: IPlatform,
|
|
79
|
+
contextManager: IContextManager,
|
|
87
80
|
llmApiKey: string | undefined,
|
|
88
81
|
sudomcpConfig: SudoMcpConfiguration,
|
|
89
82
|
authorizedUrl: string | undefined,
|
|
90
|
-
conversation: OpenAI.ChatCompletionMessageParam[] | undefined,
|
|
91
83
|
stream: boolean = false
|
|
92
84
|
): Promise<[Agent, SkillManager]> {
|
|
93
|
-
|
|
94
|
-
logger.debug("[createAgentAndSudoMcpServerManager] creating agent ...");
|
|
95
|
-
const agent = await createAgent(
|
|
85
|
+
const [agent, sudoMcpServerManager] = await createAgentWithoutSkills(
|
|
96
86
|
llmUrl,
|
|
97
|
-
agentProfile
|
|
98
|
-
|
|
99
|
-
onMessage,
|
|
100
|
-
onToolCall,
|
|
87
|
+
agentProfile,
|
|
88
|
+
eventHandler,
|
|
101
89
|
platform,
|
|
90
|
+
contextManager,
|
|
102
91
|
llmApiKey,
|
|
92
|
+
sudomcpConfig,
|
|
93
|
+
authorizedUrl,
|
|
103
94
|
stream
|
|
104
95
|
);
|
|
105
|
-
if (conversation) {
|
|
106
|
-
agent.setConversation(conversation);
|
|
107
|
-
}
|
|
108
96
|
|
|
109
|
-
|
|
110
|
-
logger.debug("[createAgentWithSkills] creating SudoMcpServerManager.");
|
|
111
|
-
const sudoMcpServerManager = await SkillManager.initialize(
|
|
112
|
-
agent.getMcpServerManager(),
|
|
113
|
-
platform.openUrl,
|
|
114
|
-
sudomcpConfig.backend_url,
|
|
115
|
-
sudomcpConfig.api_key,
|
|
116
|
-
authorizedUrl
|
|
117
|
-
);
|
|
118
|
-
logger.debug(
|
|
119
|
-
"[createAgentWithSkills] restore mcp settings:" +
|
|
120
|
-
JSON.stringify(agentProfile.mcp_settings)
|
|
121
|
-
);
|
|
97
|
+
logger.debug("[createAgentWithSkills] restoring skills");
|
|
122
98
|
await sudoMcpServerManager.restoreMcpSettings(agentProfile.mcp_settings);
|
|
123
99
|
|
|
124
|
-
logger.debug("[createAgentWithSkills] done");
|
|
125
100
|
return [agent, sudoMcpServerManager];
|
|
126
101
|
}
|
|
127
102
|
|
|
128
103
|
export async function createAgentFromSkillManager(
|
|
129
104
|
llmUrl: string,
|
|
130
105
|
agentProfile: AgentProfile,
|
|
131
|
-
|
|
132
|
-
onToolCall: OnToolCallCB,
|
|
106
|
+
eventHandler: IAgentEventHandler,
|
|
133
107
|
platform: IPlatform,
|
|
108
|
+
contextManager: IContextManager,
|
|
134
109
|
llmApiKey: string | undefined,
|
|
135
110
|
skillManager: SkillManager,
|
|
136
|
-
conversation: OpenAI.ChatCompletionMessageParam[] | undefined,
|
|
137
111
|
stream: boolean = false
|
|
138
112
|
): Promise<Agent> {
|
|
139
113
|
// Create agent
|
|
140
|
-
logger.debug("[
|
|
141
|
-
const
|
|
142
|
-
const agent = await createAgent(
|
|
114
|
+
logger.debug("[createAgentFromSkillManager] creating agent ...");
|
|
115
|
+
const llm = await createLLM(
|
|
143
116
|
llmUrl,
|
|
144
|
-
agentProfile.model,
|
|
145
|
-
agentProfile.system_prompt,
|
|
146
|
-
onMessage,
|
|
147
|
-
onToolCall,
|
|
148
|
-
platform,
|
|
149
117
|
llmApiKey,
|
|
118
|
+
agentProfile.model,
|
|
150
119
|
stream,
|
|
151
|
-
|
|
120
|
+
platform
|
|
121
|
+
);
|
|
122
|
+
contextManager.setAgentPrompt(agentProfile.system_prompt);
|
|
123
|
+
const agent = Agent.initializeWithLLM(
|
|
124
|
+
eventHandler,
|
|
125
|
+
llm,
|
|
126
|
+
contextManager,
|
|
127
|
+
skillManager
|
|
152
128
|
);
|
|
153
|
-
if (conversation) {
|
|
154
|
-
agent.setConversation(conversation);
|
|
155
|
-
}
|
|
156
129
|
|
|
157
|
-
logger.debug("[
|
|
130
|
+
logger.debug("[createAgentFromSkillManager] done");
|
|
158
131
|
return agent;
|
|
159
132
|
}
|
|
160
133
|
|
|
134
|
+
export async function createLLM(
|
|
135
|
+
llmUrl: string | undefined,
|
|
136
|
+
llmApiKey: string | undefined,
|
|
137
|
+
model: string | undefined,
|
|
138
|
+
stream: boolean = false,
|
|
139
|
+
platform: IPlatform
|
|
140
|
+
): Promise<ILLM> {
|
|
141
|
+
let llm: ILLM | undefined;
|
|
142
|
+
|
|
143
|
+
if (model && model.startsWith("dummy:")) {
|
|
144
|
+
llm = await DummyLLM.initFromModelUrl(model, platform);
|
|
145
|
+
} else if (model === "repeat") {
|
|
146
|
+
llm = new RepeatLLM();
|
|
147
|
+
} else {
|
|
148
|
+
// Regular Agent
|
|
149
|
+
if (!llmApiKey) {
|
|
150
|
+
throw new Error("Missing OpenAI API Key");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
logger.debug(
|
|
154
|
+
`Initializing Agent: ${llmUrl ?? "unknown"} - ${model ?? "unknown"}`
|
|
155
|
+
);
|
|
156
|
+
if (stream) {
|
|
157
|
+
llm = new OpenAILLMStreaming(llmApiKey, llmUrl, model);
|
|
158
|
+
} else {
|
|
159
|
+
llm = new OpenAILLM(llmApiKey, llmUrl, model);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
assert(llm);
|
|
164
|
+
return llm;
|
|
165
|
+
}
|
|
166
|
+
|
|
161
167
|
/**
|
|
162
168
|
* An "non-interactive" agent is one which is not intended to be used
|
|
163
169
|
* interactively (settings cannot be dyanmically adjusted, intermediate
|
|
@@ -174,25 +180,33 @@ export async function createNonInteractiveAgent(
|
|
|
174
180
|
approveToolsUpTo: number
|
|
175
181
|
): Promise<Agent> {
|
|
176
182
|
let remainingToolCalls = approveToolsUpTo;
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
183
|
+
const eventHandler: IAgentEventHandler = {
|
|
184
|
+
onCompletion: () => {},
|
|
185
|
+
onAgentMessage: async () => {},
|
|
186
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
187
|
+
onToolCall: async () => {
|
|
188
|
+
if (remainingToolCalls !== 0) {
|
|
189
|
+
--remainingToolCalls;
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
193
|
+
},
|
|
194
|
+
onToolCallResult: () => {},
|
|
184
195
|
};
|
|
185
196
|
|
|
197
|
+
const contextManager = new ContextManager(
|
|
198
|
+
agentProfile.system_prompt,
|
|
199
|
+
conversation || []
|
|
200
|
+
);
|
|
186
201
|
const [agent, _] = await createAgentWithSkills(
|
|
187
202
|
url,
|
|
188
203
|
agentProfile,
|
|
189
|
-
|
|
190
|
-
onToolCall,
|
|
204
|
+
eventHandler,
|
|
191
205
|
platform,
|
|
206
|
+
contextManager,
|
|
192
207
|
openaiApiKey,
|
|
193
208
|
sudomcpConfig,
|
|
194
|
-
undefined
|
|
195
|
-
conversation
|
|
209
|
+
undefined
|
|
196
210
|
);
|
|
197
211
|
|
|
198
212
|
return agent;
|
|
@@ -236,11 +250,32 @@ export async function runOneShot(
|
|
|
236
250
|
logger.debug("[runOneShot]: shutdown done");
|
|
237
251
|
|
|
238
252
|
if (!response) {
|
|
239
|
-
throw "No message returned from agent";
|
|
253
|
+
throw new Error("No message returned from agent");
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Handle different content types
|
|
257
|
+
let responseText = "";
|
|
258
|
+
if (typeof response.content === "string") {
|
|
259
|
+
responseText = response.content;
|
|
260
|
+
} else if (response.content === null || response.content === undefined) {
|
|
261
|
+
responseText = "";
|
|
262
|
+
} else if (Array.isArray(response.content)) {
|
|
263
|
+
// Handle array of content parts
|
|
264
|
+
responseText = response.content
|
|
265
|
+
.map((part) => {
|
|
266
|
+
if ("text" in part) {
|
|
267
|
+
return part.text;
|
|
268
|
+
}
|
|
269
|
+
return "";
|
|
270
|
+
})
|
|
271
|
+
.join("");
|
|
272
|
+
} else {
|
|
273
|
+
// Fallback for other types
|
|
274
|
+
responseText = String(response.content);
|
|
240
275
|
}
|
|
241
276
|
|
|
242
277
|
return {
|
|
243
|
-
response:
|
|
278
|
+
response: responseText,
|
|
244
279
|
conversation: agent.getConversation(),
|
|
245
280
|
};
|
|
246
281
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { strict as assert } from "assert";
|
|
2
|
+
import { getLogger } from "@xalia/xmcp/sdk";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Agent,
|
|
6
|
+
ChatCompletionUserMessageParam,
|
|
7
|
+
ChatCompletionMessageParam,
|
|
8
|
+
} from "./agent";
|
|
9
|
+
import { NULL_PLATFORM } from "./nullPlatform";
|
|
10
|
+
import { createLLM } from "./agentUtils";
|
|
11
|
+
import { ContextManager, ContextManagerWithCommit } from "./context";
|
|
12
|
+
import { NULL_AGENT_EVENT_HANDLER } from "./nullAgentEventHandler";
|
|
13
|
+
|
|
14
|
+
const logger = getLogger();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* System prompt used to generate a conversation summary.
|
|
18
|
+
*/
|
|
19
|
+
const COMPRESSION_SYSTEM_PROMPT =
|
|
20
|
+
// eslint-disable-next-line max-len
|
|
21
|
+
"You are a context summarizer, creating MINIMAL conversation digests which can be used to CONTINUE the conversation at a later date. TOKEN EFFICIENCY is a HIGH PRIORITY. Summaries will only be seen by LLMs and should USE ANY AND ALL ABBREVIATIONS to keep them as CONCISE as possible, while PRESERVING IMPORTANT DETAILS of the CONVERSATION.";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Text prepended to a summary to create the checkpoint message
|
|
25
|
+
*/
|
|
26
|
+
const CHECKPOINT_MESSAGE_PREFIX =
|
|
27
|
+
// eslint-disable-next-line max-len
|
|
28
|
+
"We are continuing an earlier conversation. The remainder of this message is a summary of the conversation so far: ";
|
|
29
|
+
|
|
30
|
+
export function createCheckpointMessage(
|
|
31
|
+
summary: string
|
|
32
|
+
): ChatCompletionUserMessageParam {
|
|
33
|
+
return {
|
|
34
|
+
role: "user",
|
|
35
|
+
content: CHECKPOINT_MESSAGE_PREFIX + summary,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function createCompressionAgent(
|
|
40
|
+
compressionAgentUrl: string,
|
|
41
|
+
compressionAgentModel: string,
|
|
42
|
+
compressionAgentApiKey: string
|
|
43
|
+
): Promise<Agent> {
|
|
44
|
+
const llm = await createLLM(
|
|
45
|
+
compressionAgentUrl,
|
|
46
|
+
compressionAgentApiKey,
|
|
47
|
+
compressionAgentModel,
|
|
48
|
+
false /* stream */,
|
|
49
|
+
NULL_PLATFORM
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
return Agent.initializeWithLLM(
|
|
53
|
+
NULL_AGENT_EVENT_HANDLER,
|
|
54
|
+
llm,
|
|
55
|
+
new ContextManager(COMPRESSION_SYSTEM_PROMPT, [])
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function createSummary(
|
|
60
|
+
compressionAgentUrl: string,
|
|
61
|
+
compressionAgentModel: string,
|
|
62
|
+
compressionAgentApiKey: string,
|
|
63
|
+
conversation: ChatCompletionMessageParam[]
|
|
64
|
+
): Promise<string> {
|
|
65
|
+
const agent = await createCompressionAgent(
|
|
66
|
+
compressionAgentUrl,
|
|
67
|
+
compressionAgentModel,
|
|
68
|
+
compressionAgentApiKey
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const resp = await agent.userMessageEx(JSON.stringify(conversation));
|
|
72
|
+
if (!resp) {
|
|
73
|
+
throw new Error("compression agent returned null");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
assert(resp.role === "assistant");
|
|
77
|
+
assert(
|
|
78
|
+
typeof resp.content === "string",
|
|
79
|
+
"expected string content from compression agent"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return resp.content;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Can perform compression on the committed part of the context. Caller (not
|
|
87
|
+
* the Agent) is responsible for committing the conversation and triggering
|
|
88
|
+
* compression.
|
|
89
|
+
*/
|
|
90
|
+
export class CompressingContextManager extends ContextManagerWithCommit {
|
|
91
|
+
compressionAgentUrl: string;
|
|
92
|
+
compressionAgentModel: string;
|
|
93
|
+
compressionAgentApiKey: string;
|
|
94
|
+
compressingMessages: number | undefined;
|
|
95
|
+
|
|
96
|
+
constructor(
|
|
97
|
+
systemPrompt: string,
|
|
98
|
+
messages: ChatCompletionMessageParam[],
|
|
99
|
+
compressionAgentUrl: string,
|
|
100
|
+
compressionAgentModel: string,
|
|
101
|
+
compressionAgentApiKey: string
|
|
102
|
+
) {
|
|
103
|
+
super(systemPrompt, messages);
|
|
104
|
+
this.compressionAgentUrl = compressionAgentUrl;
|
|
105
|
+
this.compressionAgentModel = compressionAgentModel;
|
|
106
|
+
this.compressionAgentApiKey = compressionAgentApiKey;
|
|
107
|
+
this.compressingMessages = undefined;
|
|
108
|
+
|
|
109
|
+
// Sanity check the conversation form.
|
|
110
|
+
//
|
|
111
|
+
// Ordinarily, the committed context should end with an "assistant"
|
|
112
|
+
// message (i.e. user messages, an agent loop terminating in a final agent
|
|
113
|
+
// response). However, if a conversation has been compressed, we may have
|
|
114
|
+
// only the summary, which is a "user" message. In this case, this should
|
|
115
|
+
// be the only message.
|
|
116
|
+
|
|
117
|
+
const numMessages = this.numMessages();
|
|
118
|
+
const lastMessage = this.lastMessage();
|
|
119
|
+
if (lastMessage) {
|
|
120
|
+
const finalRole = lastMessage.role;
|
|
121
|
+
if (finalRole === "user") {
|
|
122
|
+
assert(numMessages === 1);
|
|
123
|
+
} else {
|
|
124
|
+
assert(finalRole === "assistant", `unexpected final role ${finalRole}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async compress(): Promise<string> {
|
|
130
|
+
// Only select messages for compression if they have been committed.
|
|
131
|
+
const numToCompress = this.getCommittedLength();
|
|
132
|
+
const messagesToCompress = this.leadingMessages(numToCompress);
|
|
133
|
+
assert(messagesToCompress.length === numToCompress);
|
|
134
|
+
this.compressingMessages = numToCompress;
|
|
135
|
+
assert(this.compressingMessages > 1, "<2 messages commited in the context");
|
|
136
|
+
|
|
137
|
+
logger.debug(
|
|
138
|
+
`[CompressingContextManager] start (${String(this.compressingMessages)})`
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const summary = await createSummary(
|
|
143
|
+
this.compressionAgentUrl,
|
|
144
|
+
this.compressionAgentModel,
|
|
145
|
+
this.compressionAgentApiKey,
|
|
146
|
+
messagesToCompress
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
logger.debug(`[CompressingContextManager] summary: ${summary}`);
|
|
150
|
+
|
|
151
|
+
// Replace the context `messages` and update `lastCommittedMessage`
|
|
152
|
+
// index.
|
|
153
|
+
|
|
154
|
+
const checkpointMessage = createCheckpointMessage(summary);
|
|
155
|
+
assert(typeof checkpointMessage.content === "string");
|
|
156
|
+
this.replaceLeadingMessages(numToCompress, checkpointMessage);
|
|
157
|
+
|
|
158
|
+
return summary;
|
|
159
|
+
} finally {
|
|
160
|
+
this.compressingMessages = undefined;
|
|
161
|
+
logger.debug(`[CompressingContextManager] compression done`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|