@xalia/agent 0.5.8 → 0.6.1
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 +173 -96
- package/dist/agent/src/agent/agentUtils.js +82 -53
- 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/iAgentEventHandler.js +2 -0
- package/dist/agent/src/agent/mcpServerManager.js +22 -23
- 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 +12 -7
- 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 +11 -9
- 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/{websocket.js → utils/websocket.js} +2 -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 +262 -90
- 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 +21 -18
- package/dist/agent/src/test/multiAsyncQueue.test.js +101 -0
- package/dist/agent/src/test/openaiStreaming.test.js +12 -11
- 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 +14 -12
- 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 +235 -58
- package/dist/agent/src/tool/commandPrompt.js +15 -9
- 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 +270 -135
- package/src/agent/agentUtils.ts +136 -95
- 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 +32 -30
- package/src/agent/nullAgentEventHandler.ts +20 -0
- package/src/agent/nullPlatform.ts +13 -0
- package/src/agent/openAILLMStreaming.ts +12 -6
- package/src/agent/promptProvider.ts +87 -0
- package/src/agent/repeatLLM.ts +5 -5
- package/src/agent/sudoMcpServerManager.ts +13 -11
- 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/{websocket.ts → utils/websocket.ts} +2 -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 +351 -103
- 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 +19 -14
- package/src/test/multiAsyncQueue.test.ts +125 -0
- package/src/test/openaiStreaming.test.ts +11 -10
- 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 +22 -15
- 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 +283 -100
- package/src/tool/commandPrompt.ts +25 -9
- 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 -310
- package/dist/agent/src/chat/conversationManager.js +0 -502
- package/dist/agent/src/chat/db.js +0 -218
- package/dist/agent/src/chat/messages.js +0 -29
- package/dist/agent/src/chat/server.js +0 -158
- package/src/chat/client.ts +0 -445
- package/src/chat/conversationManager.ts +0 -730
- package/src/chat/db.ts +0 -304
- package/src/chat/messages.ts +0 -266
- package/src/chat/server.ts +0 -177
- /package/{frog.png → test_data/frog.png} +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, vi } from "vitest";
|
|
2
|
+
import { Server } from "ws";
|
|
3
|
+
import { Connection } from "../chat/client/connection";
|
|
4
|
+
import { runServer } from "../chat/server/server";
|
|
5
|
+
import { Database } from "../chat/data/database";
|
|
6
|
+
import { getLogger } from "@xalia/xmcp/sdk";
|
|
7
|
+
import { SUPABASE_LOCAL_KEY, SUPABASE_LOCAL_URL } from "../chat/data/database";
|
|
8
|
+
import {
|
|
9
|
+
ClientControlGetSessionList,
|
|
10
|
+
ServerControlSessionList,
|
|
11
|
+
} from "../chat/protocol/messages";
|
|
12
|
+
import { userSessionDataCreate } from "../chat/server/sessionRegistry";
|
|
13
|
+
|
|
14
|
+
// Mock uuid to have predictable client message IDs
|
|
15
|
+
vi.mock("uuid", () => ({
|
|
16
|
+
v4: () => "mock-uuid-123",
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const logger = getLogger();
|
|
20
|
+
|
|
21
|
+
describe("Client-Server WebSocket Integration", () => {
|
|
22
|
+
let server: Server;
|
|
23
|
+
let db: Database;
|
|
24
|
+
const serverPort = 5105; // Different port to avoid conflicts
|
|
25
|
+
// Test API key data
|
|
26
|
+
const testApiKey = "integration_test";
|
|
27
|
+
const testUserUuid = "test_user_0";
|
|
28
|
+
const testUserName = "default";
|
|
29
|
+
const testScopes: string[] = [];
|
|
30
|
+
// Mock environment variables for server
|
|
31
|
+
const mockEnv = {
|
|
32
|
+
SUPABASE_URL: SUPABASE_LOCAL_URL,
|
|
33
|
+
SUPABASE_KEY: SUPABASE_LOCAL_KEY,
|
|
34
|
+
LLM_URL: "http://localhost:8080", // not used in this test
|
|
35
|
+
XMCP_URL: "http://localhost:8081", // not used in this test
|
|
36
|
+
};
|
|
37
|
+
beforeAll(async () => {
|
|
38
|
+
// Set up environment variables
|
|
39
|
+
Object.assign(process.env, mockEnv);
|
|
40
|
+
// Initialize database connection
|
|
41
|
+
db = new Database(mockEnv.SUPABASE_URL, mockEnv.SUPABASE_KEY);
|
|
42
|
+
// Try to insert test data, but don't fail if there are database issues
|
|
43
|
+
try {
|
|
44
|
+
await cleanupTestData();
|
|
45
|
+
await insertTestApiKey();
|
|
46
|
+
logger.info("[Test] Test data setup completed successfully");
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.warn(
|
|
49
|
+
"[Test] Test data setup failed - will test with existing data:",
|
|
50
|
+
error
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
// Start the real server using runServer
|
|
54
|
+
server = await runServer(
|
|
55
|
+
serverPort,
|
|
56
|
+
mockEnv.SUPABASE_URL,
|
|
57
|
+
mockEnv.SUPABASE_KEY,
|
|
58
|
+
mockEnv.LLM_URL,
|
|
59
|
+
mockEnv.XMCP_URL
|
|
60
|
+
);
|
|
61
|
+
// Wait for server to be ready
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
63
|
+
});
|
|
64
|
+
async function cleanupTestData() {
|
|
65
|
+
try {
|
|
66
|
+
// Delete test API key first (foreign key constraint)
|
|
67
|
+
await db
|
|
68
|
+
.getClientForTesting()
|
|
69
|
+
.from("api_keys")
|
|
70
|
+
.delete()
|
|
71
|
+
.eq("api_key", testApiKey);
|
|
72
|
+
// Delete test user
|
|
73
|
+
await db
|
|
74
|
+
.getClientForTesting()
|
|
75
|
+
.from("users")
|
|
76
|
+
.delete()
|
|
77
|
+
.eq("uuid", testUserUuid);
|
|
78
|
+
logger.info("[Test] Cleaned up existing test data");
|
|
79
|
+
} catch (error) {
|
|
80
|
+
logger.warn(
|
|
81
|
+
"[Test] Error during cleanup (expected if no data exists):",
|
|
82
|
+
error
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function insertTestApiKey() {
|
|
87
|
+
try {
|
|
88
|
+
// Use Database class methods which include timezone handling
|
|
89
|
+
await db.createUser(
|
|
90
|
+
testUserUuid,
|
|
91
|
+
"test@example.com",
|
|
92
|
+
testUserName,
|
|
93
|
+
"UTC"
|
|
94
|
+
);
|
|
95
|
+
logger.info("[Test] User created successfully");
|
|
96
|
+
await db.addApiKey(
|
|
97
|
+
testUserUuid,
|
|
98
|
+
testApiKey,
|
|
99
|
+
testUserName,
|
|
100
|
+
testScopes,
|
|
101
|
+
true // is_default
|
|
102
|
+
);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
logger.error("[Test] Database method insert failed:", error);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
afterAll(async () => {
|
|
109
|
+
// Clean up test data
|
|
110
|
+
await cleanupTestData();
|
|
111
|
+
// Close server
|
|
112
|
+
server.close();
|
|
113
|
+
});
|
|
114
|
+
describe("Connection Setup and List Sessions", () => {
|
|
115
|
+
it("should establish connection with valid API key", async () => {
|
|
116
|
+
// Inject test agent profiles and sessions into the database
|
|
117
|
+
const agentProfile1 = await db.createAgentProfile(
|
|
118
|
+
testUserUuid,
|
|
119
|
+
undefined,
|
|
120
|
+
"Test agent profile 1",
|
|
121
|
+
{
|
|
122
|
+
model: undefined,
|
|
123
|
+
system_prompt: "You are a test agent",
|
|
124
|
+
mcp_settings: {},
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
const agentProfile2 = await db.createAgentProfile(
|
|
128
|
+
testUserUuid,
|
|
129
|
+
undefined,
|
|
130
|
+
"Test agent profile 2",
|
|
131
|
+
{
|
|
132
|
+
model: undefined,
|
|
133
|
+
system_prompt: "You are a test agent",
|
|
134
|
+
mcp_settings: {},
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
if (!agentProfile1 || !agentProfile2) {
|
|
138
|
+
throw new Error("Failed to create agent profiles");
|
|
139
|
+
}
|
|
140
|
+
const agentProfile1Id = agentProfile1.uuid;
|
|
141
|
+
const agentProfile2Id = agentProfile2.uuid;
|
|
142
|
+
const session1CreateData = userSessionDataCreate(
|
|
143
|
+
testUserUuid,
|
|
144
|
+
"Test Session 1",
|
|
145
|
+
agentProfile1Id
|
|
146
|
+
);
|
|
147
|
+
const session1Id = session1CreateData.session_uuid;
|
|
148
|
+
await db.sessionCreate(session1CreateData);
|
|
149
|
+
const session2CreateData = userSessionDataCreate(
|
|
150
|
+
testUserUuid,
|
|
151
|
+
"Test Session 2",
|
|
152
|
+
agentProfile2Id
|
|
153
|
+
);
|
|
154
|
+
const session2Id = session2CreateData.session_uuid;
|
|
155
|
+
await db.sessionCreate(session2CreateData);
|
|
156
|
+
// Log session creation for debugging
|
|
157
|
+
logger.info(`[Test] Created sessions: ${session1Id}, ${session2Id}`);
|
|
158
|
+
const client = new Connection({
|
|
159
|
+
url: `ws://localhost:${String(serverPort)}`,
|
|
160
|
+
token: testApiKey,
|
|
161
|
+
});
|
|
162
|
+
// Wait for session_list response
|
|
163
|
+
const responsePromise = new Promise<ServerControlSessionList>(
|
|
164
|
+
(resolve) => {
|
|
165
|
+
client.on("control_session_list", (msg) => {
|
|
166
|
+
resolve(msg as ServerControlSessionList);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
await client.connect();
|
|
171
|
+
expect(client.getState()).toBe("ready");
|
|
172
|
+
client.send({
|
|
173
|
+
type: "control_get_session_list",
|
|
174
|
+
client_message_id: "test-msg-id",
|
|
175
|
+
} as ClientControlGetSessionList);
|
|
176
|
+
const response = await responsePromise;
|
|
177
|
+
expect(response.type).toBe("control_session_list");
|
|
178
|
+
expect(response.user_sessions).toBeDefined();
|
|
179
|
+
expect(response.user_sessions.length).toBe(2);
|
|
180
|
+
// Verify session details
|
|
181
|
+
const sessionIds = response.user_sessions.map((s) => s.session_uuid);
|
|
182
|
+
expect(sessionIds).toContain(session1Id);
|
|
183
|
+
expect(sessionIds).toContain(session2Id);
|
|
184
|
+
const session1Data = response.user_sessions.find(
|
|
185
|
+
(s) => s.session_uuid === session1Id
|
|
186
|
+
);
|
|
187
|
+
expect(session1Data?.title).toBe("Test Session 1");
|
|
188
|
+
const session2Data = response.user_sessions.find(
|
|
189
|
+
(s) => s.session_uuid === session2Id
|
|
190
|
+
);
|
|
191
|
+
expect(session2Data?.title).toBe("Test Session 2");
|
|
192
|
+
client.close();
|
|
193
|
+
}, 15000);
|
|
194
|
+
});
|
|
195
|
+
describe("Authentication Error Handling", () => {
|
|
196
|
+
it("should handle invalid API key gracefully", async () => {
|
|
197
|
+
const invalidApiKey = "invalid-test-key";
|
|
198
|
+
const client = new Connection({
|
|
199
|
+
url: `ws://localhost:${String(serverPort)}`,
|
|
200
|
+
token: invalidApiKey,
|
|
201
|
+
});
|
|
202
|
+
await expect(client.connect()).rejects.toThrow();
|
|
203
|
+
expect(client.getState()).toBe("closed");
|
|
204
|
+
client.close();
|
|
205
|
+
}, 4000);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { expect } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CompressingContextManager,
|
|
5
|
+
createCheckpointMessage,
|
|
6
|
+
} from "../agent/compressingContextManager";
|
|
7
|
+
import { ChatCompletionMessageParam } from "../agent/agent";
|
|
8
|
+
|
|
9
|
+
const MESSAGES: ChatCompletionMessageParam[] = [
|
|
10
|
+
{ role: "user", content: "msg200" },
|
|
11
|
+
{ role: "assistant", content: "msg300" },
|
|
12
|
+
{ role: "user", content: "msg400" },
|
|
13
|
+
{
|
|
14
|
+
role: "assistant",
|
|
15
|
+
content: "msg500",
|
|
16
|
+
tool_calls: [
|
|
17
|
+
{
|
|
18
|
+
id: "tool_call_0",
|
|
19
|
+
type: "function",
|
|
20
|
+
function: { name: "tool1", arguments: "" },
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
role: "tool",
|
|
26
|
+
content: "msg501",
|
|
27
|
+
tool_call_id: "tool_call_0",
|
|
28
|
+
},
|
|
29
|
+
{ role: "assistant", content: "msg502" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
describe("Compression context", () => {
|
|
33
|
+
it("context compression", async function () {
|
|
34
|
+
const ccm = new CompressingContextManager(
|
|
35
|
+
"sys_prompt",
|
|
36
|
+
[],
|
|
37
|
+
"", // llmUrl
|
|
38
|
+
"repeat", // llmModel
|
|
39
|
+
"" // llmApiKey
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Add first 2 messages
|
|
43
|
+
ccm.addMessages(MESSAGES.slice(0, 2));
|
|
44
|
+
ccm.commit();
|
|
45
|
+
|
|
46
|
+
// Start the compression
|
|
47
|
+
const compressionP = ccm.compress();
|
|
48
|
+
|
|
49
|
+
// Add more messages and update system prompt
|
|
50
|
+
ccm.addMessages(MESSAGES.slice(2));
|
|
51
|
+
ccm.setAgentPrompt("New system prompt");
|
|
52
|
+
|
|
53
|
+
// Wait for compression to complete, then get the new context
|
|
54
|
+
const summary = await compressionP;
|
|
55
|
+
const context = ccm.getLLMContext();
|
|
56
|
+
|
|
57
|
+
// Expect
|
|
58
|
+
// - callback happened
|
|
59
|
+
// - new context is as expected
|
|
60
|
+
|
|
61
|
+
const expectSummary = "Message number 0";
|
|
62
|
+
const expectContext: ChatCompletionMessageParam[] = [
|
|
63
|
+
{
|
|
64
|
+
role: "system",
|
|
65
|
+
content: "New system prompt",
|
|
66
|
+
},
|
|
67
|
+
createCheckpointMessage(expectSummary),
|
|
68
|
+
MESSAGES[2], // 400
|
|
69
|
+
MESSAGES[3], // 500
|
|
70
|
+
MESSAGES[4], // 501
|
|
71
|
+
MESSAGES[5], // 502
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
expect(summary).eql(expectSummary);
|
|
75
|
+
expect(context).eql(expectContext);
|
|
76
|
+
|
|
77
|
+
// Pending messages shoudl still be intact
|
|
78
|
+
|
|
79
|
+
const pending = ccm.getPending();
|
|
80
|
+
expect(pending).eql(MESSAGES.slice(2));
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { expect } from "vitest";
|
|
2
|
+
import { strict as assert } from "assert";
|
|
3
|
+
|
|
4
|
+
import { ContextManagerWithWorkspace } from "../agent/context";
|
|
5
|
+
import { ChatCompletionMessageParam, createUserMessage } from "../agent/agent";
|
|
6
|
+
import { SystemPromptProvider } from "../agent/promptProvider";
|
|
7
|
+
|
|
8
|
+
describe("ContextManagerWithWorkspace", () => {
|
|
9
|
+
const msgs: ChatCompletionMessageParam[] = [
|
|
10
|
+
{ role: "user", content: "message A" },
|
|
11
|
+
{ role: "assistant", content: "message B" },
|
|
12
|
+
{ role: "user", content: "message C" },
|
|
13
|
+
{ role: "assistant", content: "message D" },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
it("should correctly manage messages", function () {
|
|
17
|
+
const cm = new ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
|
|
18
|
+
|
|
19
|
+
// set workspace and add new messages
|
|
20
|
+
cm.setWorkspace({ role: "user", content: "workspace1" });
|
|
21
|
+
cm.addMessages(msgs.slice(2));
|
|
22
|
+
|
|
23
|
+
// context with workspace
|
|
24
|
+
const ctx1 = cm.getLLMContext();
|
|
25
|
+
expect(ctx1).eql([
|
|
26
|
+
{ role: "system", content: "sys_msg" },
|
|
27
|
+
...msgs,
|
|
28
|
+
{ role: "user", content: "workspace1" },
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
// context with new message and workspace
|
|
32
|
+
const lastMsg: ChatCompletionMessageParam = {
|
|
33
|
+
role: "user",
|
|
34
|
+
content: "hello",
|
|
35
|
+
};
|
|
36
|
+
cm.addMessage(lastMsg);
|
|
37
|
+
const ctx2 = cm.getLLMContext();
|
|
38
|
+
expect(ctx2).eql([
|
|
39
|
+
{ role: "system", content: "sys_msg" },
|
|
40
|
+
...msgs,
|
|
41
|
+
lastMsg,
|
|
42
|
+
{ role: "user", content: "workspace1" },
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
// context without workspace
|
|
46
|
+
cm.setWorkspace(undefined);
|
|
47
|
+
const ctx3 = cm.getLLMContext();
|
|
48
|
+
expect(ctx3).eql([
|
|
49
|
+
{ role: "system", content: "sys_msg" },
|
|
50
|
+
...msgs,
|
|
51
|
+
lastMsg,
|
|
52
|
+
]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("correctly includes all system prompt components", function () {
|
|
56
|
+
const cm = new ContextManagerWithWorkspace(
|
|
57
|
+
"agent_prompt",
|
|
58
|
+
msgs.slice(0, 2)
|
|
59
|
+
);
|
|
60
|
+
SystemPromptProvider.setGlobalPrompt("global");
|
|
61
|
+
cm.setPromptFragment("frag1", "FRAG1");
|
|
62
|
+
|
|
63
|
+
// Get the agentPrompt and llmContext, then undo the global prompt change
|
|
64
|
+
// (to ensure other tests are affected, even if this one fails).
|
|
65
|
+
|
|
66
|
+
const agentPrompt = cm.getAgentPrompt();
|
|
67
|
+
const llmContext = cm.getLLMContext();
|
|
68
|
+
SystemPromptProvider.setGlobalPrompt(undefined);
|
|
69
|
+
|
|
70
|
+
expect(agentPrompt).eql("agent_prompt");
|
|
71
|
+
expect(llmContext[0].content).eql("global\nagent_prompt\nFRAG1");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("handles rich workspace messages", function () {
|
|
75
|
+
const cm = new ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
|
|
76
|
+
|
|
77
|
+
expect(cm.getLLMContext()).eql([
|
|
78
|
+
{ role: "system", content: "sys_msg" },
|
|
79
|
+
...msgs.slice(0, 2),
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
// set workspace and add new messages
|
|
83
|
+
const wsMsg = createUserMessage(
|
|
84
|
+
"text",
|
|
85
|
+
"data:image/png;base64;adsfadsfadf",
|
|
86
|
+
"user_name"
|
|
87
|
+
);
|
|
88
|
+
assert(wsMsg);
|
|
89
|
+
assert(wsMsg.content);
|
|
90
|
+
assert(typeof wsMsg.content !== "string");
|
|
91
|
+
cm.setWorkspace(wsMsg);
|
|
92
|
+
|
|
93
|
+
expect(cm.getLLMContext()).eql([
|
|
94
|
+
{ role: "system", content: "sys_msg" },
|
|
95
|
+
...msgs.slice(0, 2),
|
|
96
|
+
wsMsg,
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
cm.addMessages(msgs.slice(2));
|
|
100
|
+
|
|
101
|
+
// context with workspace
|
|
102
|
+
const ctx1 = cm.getLLMContext();
|
|
103
|
+
expect(ctx1).eql([{ role: "system", content: "sys_msg" }, ...msgs, wsMsg]);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
ChatCompletionAssistantMessageParam,
|
|
4
|
+
ChatCompletionMessageParam,
|
|
5
|
+
ChatCompletionUserMessageParam,
|
|
6
|
+
} from "../agent/agent";
|
|
7
|
+
import {
|
|
8
|
+
MESSAGE_INDEX_FULL_INCREMENT,
|
|
9
|
+
MESSAGE_INDEX_SUB_INCREMENT,
|
|
10
|
+
sessionMessagesToLLMConversation,
|
|
11
|
+
sessionMessagesToNextIndex,
|
|
12
|
+
} from "../chat/server/conversation";
|
|
13
|
+
import { SessionMessage } from "../chat/data/dataModels";
|
|
14
|
+
|
|
15
|
+
// Fake conversation values.
|
|
16
|
+
const AGENT: ChatCompletionAssistantMessageParam[] = [
|
|
17
|
+
{ role: "assistant", content: "Message 0" },
|
|
18
|
+
{ role: "assistant", content: "Message 1" },
|
|
19
|
+
{ role: "assistant", content: "Message 2" },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const USER: ChatCompletionUserMessageParam[] = [
|
|
23
|
+
{ role: "user", content: "Message 0", name: "userA" },
|
|
24
|
+
{ role: "user", content: "Message 1", name: "userA" },
|
|
25
|
+
{ role: "user", content: "Message 2", name: "userA" },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
describe("Conversation tools", () => {
|
|
29
|
+
it("sessionMessagesToLLMConversation handles empty case", () => {
|
|
30
|
+
const nextMessageIdx = sessionMessagesToNextIndex([]);
|
|
31
|
+
const { firstIndex, conversation } = sessionMessagesToLLMConversation([]);
|
|
32
|
+
|
|
33
|
+
expect(nextMessageIdx).eql(0);
|
|
34
|
+
expect(firstIndex).eql(-1);
|
|
35
|
+
expect(conversation).eql([]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("sessionMessagesToLLMConversation extracts LLM messages", () => {
|
|
39
|
+
const messages: SessionMessage[] = [
|
|
40
|
+
{
|
|
41
|
+
message_idx: 0,
|
|
42
|
+
sender_uuid: "userA",
|
|
43
|
+
content: USER[0],
|
|
44
|
+
is_for_llm: true,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
message_idx: 100,
|
|
48
|
+
content: {} as unknown as ChatCompletionMessageParam,
|
|
49
|
+
is_for_llm: false,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
message_idx: 101,
|
|
53
|
+
content: AGENT[0],
|
|
54
|
+
is_for_llm: true,
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
const expectConversation = [USER[0], AGENT[0]];
|
|
58
|
+
const expectNextIdx = 2 * MESSAGE_INDEX_FULL_INCREMENT;
|
|
59
|
+
const expectFirstIdx = 0;
|
|
60
|
+
|
|
61
|
+
const nextMessageIdx = sessionMessagesToNextIndex(messages);
|
|
62
|
+
const nextMessageIdx100 = sessionMessagesToNextIndex(messages.slice(0, 2));
|
|
63
|
+
const { firstIndex, conversation } =
|
|
64
|
+
sessionMessagesToLLMConversation(messages);
|
|
65
|
+
expect(conversation).eql(expectConversation);
|
|
66
|
+
expect(nextMessageIdx).eql(expectNextIdx);
|
|
67
|
+
expect(nextMessageIdx100).eql(expectNextIdx);
|
|
68
|
+
expect(firstIndex).eql(expectFirstIdx);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("sessionMessagesToLLMConversation skips leading invalid", () => {
|
|
72
|
+
const messages: SessionMessage[] = [
|
|
73
|
+
{
|
|
74
|
+
message_idx: 3 * MESSAGE_INDEX_FULL_INCREMENT,
|
|
75
|
+
content: AGENT[0],
|
|
76
|
+
is_for_llm: true,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
// Should be identified as the first message, since we can't start
|
|
80
|
+
// with an agent message
|
|
81
|
+
message_idx: 4 * MESSAGE_INDEX_FULL_INCREMENT,
|
|
82
|
+
sender_uuid: "userA",
|
|
83
|
+
content: USER[0],
|
|
84
|
+
is_for_llm: true,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
message_idx: 5 * MESSAGE_INDEX_FULL_INCREMENT,
|
|
88
|
+
content: {} as unknown as ChatCompletionMessageParam,
|
|
89
|
+
is_for_llm: false,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
message_idx:
|
|
93
|
+
5 * MESSAGE_INDEX_FULL_INCREMENT + 3 * MESSAGE_INDEX_SUB_INCREMENT,
|
|
94
|
+
content: AGENT[1],
|
|
95
|
+
is_for_llm: true,
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
const expectConversation = [USER[0], AGENT[1]];
|
|
99
|
+
const expectNextIdx = 6 * MESSAGE_INDEX_FULL_INCREMENT;
|
|
100
|
+
const expectFirstIdx = 4 * MESSAGE_INDEX_FULL_INCREMENT;
|
|
101
|
+
|
|
102
|
+
const nextMessageIdx = sessionMessagesToNextIndex(messages);
|
|
103
|
+
const { firstIndex, conversation } =
|
|
104
|
+
sessionMessagesToLLMConversation(messages);
|
|
105
|
+
expect(conversation).eql(expectConversation);
|
|
106
|
+
expect(nextMessageIdx).eql(expectNextIdx);
|
|
107
|
+
expect(firstIndex).eql(expectFirstIdx);
|
|
108
|
+
});
|
|
109
|
+
});
|