@xalia/agent 0.6.10 → 0.6.11
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/package.json +5 -2
- package/.env.development +0 -6
- package/.env.test +0 -7
- package/.prettierrc.json +0 -11
- package/context_system.md +0 -498
- package/eslint.config.mjs +0 -38
- package/scripts/chat_server +0 -8
- package/scripts/git_message +0 -31
- package/scripts/git_wip +0 -21
- package/scripts/pr_message +0 -18
- package/scripts/pr_review +0 -16
- package/scripts/setup_chat +0 -90
- package/scripts/shutdown_chat_server +0 -42
- package/scripts/start_chat_server +0 -24
- package/scripts/sudomcp_import +0 -23
- package/scripts/test_chat +0 -327
- package/src/agent/agent.ts +0 -699
- package/src/agent/agentUtils.ts +0 -286
- package/src/agent/compressingContextManager.ts +0 -129
- package/src/agent/context.ts +0 -265
- package/src/agent/contextWithWorkspace.ts +0 -162
- package/src/agent/documentSummarizer.ts +0 -157
- package/src/agent/dummyLLM.ts +0 -130
- package/src/agent/iAgentEventHandler.ts +0 -64
- package/src/agent/imageGenLLM.ts +0 -101
- package/src/agent/imageGenerator.ts +0 -45
- package/src/agent/iplatform.ts +0 -18
- package/src/agent/llm.ts +0 -74
- package/src/agent/mcpServerManager.ts +0 -541
- package/src/agent/nullAgentEventHandler.ts +0 -26
- package/src/agent/nullPlatform.ts +0 -13
- package/src/agent/openAI.ts +0 -123
- package/src/agent/openAILLM.ts +0 -99
- package/src/agent/openAILLMStreaming.ts +0 -648
- package/src/agent/promptProvider.ts +0 -87
- package/src/agent/repeatLLM.ts +0 -62
- package/src/agent/sudoMcpServerManager.ts +0 -361
- package/src/agent/test_data/harrypotter.txt +0 -6065
- package/src/agent/tokenAuth.ts +0 -50
- package/src/agent/tokenCounter.test.ts +0 -243
- package/src/agent/tokenCounter.ts +0 -483
- package/src/agent/toolSettings.ts +0 -24
- package/src/agent/tools/calculatorTool.ts +0 -50
- package/src/agent/tools/contentExtractors/htmlToText.ts +0 -61
- package/src/agent/tools/contentExtractors/pdfToText.ts +0 -60
- package/src/agent/tools/datetimeTool.ts +0 -41
- package/src/agent/tools/fileManager/fileManagerTool.ts +0 -199
- package/src/agent/tools/fileManager/index.ts +0 -50
- package/src/agent/tools/fileManager/memoryFileManager.ts +0 -120
- package/src/agent/tools/fileManager/mimeTypes.ts +0 -60
- package/src/agent/tools/fileManager/prompt.ts +0 -38
- package/src/agent/tools/fileManager/types.ts +0 -189
- package/src/agent/tools/index.ts +0 -49
- package/src/agent/tools/openUrlTool.ts +0 -62
- package/src/agent/tools/renderTool.ts +0 -92
- package/src/agent/tools/utils.ts +0 -74
- package/src/agent/tools/webSearch.ts +0 -138
- package/src/agent/tools/webSearchTool.ts +0 -44
- package/src/chat/client/chatClient.ts +0 -967
- package/src/chat/client/connection.test.ts +0 -241
- package/src/chat/client/connection.ts +0 -286
- package/src/chat/client/constants.ts +0 -1
- package/src/chat/client/index.ts +0 -21
- package/src/chat/client/interfaces.ts +0 -34
- package/src/chat/client/sessionClient.ts +0 -574
- package/src/chat/client/sessionFiles.ts +0 -142
- package/src/chat/client/teamManager.ts +0 -29
- package/src/chat/constants.ts +0 -6
- package/src/chat/data/apiKeyManager.ts +0 -76
- package/src/chat/data/dataModels.ts +0 -107
- package/src/chat/data/database.ts +0 -997
- package/src/chat/data/dbMcpServerConfigs.ts +0 -59
- package/src/chat/data/dbSessionFiles.ts +0 -107
- package/src/chat/data/dbSessionMessages.ts +0 -102
- package/src/chat/protocol/connectionMessages.ts +0 -49
- package/src/chat/protocol/constants.ts +0 -55
- package/src/chat/protocol/errors.ts +0 -16
- package/src/chat/protocol/messages.ts +0 -899
- package/src/chat/server/README.md +0 -127
- package/src/chat/server/chatContextManager.ts +0 -660
- package/src/chat/server/connectionManager.test.ts +0 -246
- package/src/chat/server/connectionManager.ts +0 -506
- package/src/chat/server/conversation.ts +0 -319
- package/src/chat/server/errorUtils.ts +0 -28
- package/src/chat/server/imageGeneratorTools.ts +0 -179
- package/src/chat/server/openAIRouterLLM.ts +0 -168
- package/src/chat/server/openSession.ts +0 -1945
- package/src/chat/server/openSessionMessageSender.ts +0 -4
- package/src/chat/server/promptRefiner.ts +0 -106
- package/src/chat/server/server.ts +0 -178
- package/src/chat/server/sessionFileManager.ts +0 -151
- package/src/chat/server/sessionRegistry.test.ts +0 -137
- package/src/chat/server/sessionRegistry.ts +0 -1553
- package/src/chat/server/test-utils/mockFactories.ts +0 -422
- package/src/chat/server/titleGenerator.test.ts +0 -103
- package/src/chat/server/titleGenerator.ts +0 -143
- package/src/chat/server/tools.ts +0 -170
- package/src/chat/utils/agentSessionMap.ts +0 -76
- package/src/chat/utils/approvalManager.ts +0 -189
- package/src/chat/utils/asyncLock.ts +0 -43
- package/src/chat/utils/asyncQueue.ts +0 -62
- package/src/chat/utils/multiAsyncQueue.ts +0 -66
- package/src/chat/utils/responseAwaiter.ts +0 -181
- package/src/chat/utils/userResolver.ts +0 -48
- package/src/chat/utils/websocket.ts +0 -16
- package/src/index.ts +0 -0
- package/src/test/agent.test.ts +0 -584
- package/src/test/approvalManager.test.ts +0 -141
- package/src/test/chatContextManager.test.ts +0 -552
- package/src/test/clientServerConnection.test.ts +0 -205
- package/src/test/compressingContextManager.test.ts +0 -77
- package/src/test/context.test.ts +0 -150
- package/src/test/contextTestTools.ts +0 -95
- package/src/test/conversation.test.ts +0 -109
- package/src/test/db.test.ts +0 -363
- package/src/test/dbMcpServerConfigs.test.ts +0 -112
- package/src/test/dbSessionFiles.test.ts +0 -258
- package/src/test/dbSessionMessages.test.ts +0 -85
- package/src/test/dbTestTools.ts +0 -157
- package/src/test/imageLoad.test.ts +0 -15
- package/src/test/mcpServerManager.test.ts +0 -114
- package/src/test/multiAsyncQueue.test.ts +0 -183
- package/src/test/openaiStreaming.test.ts +0 -177
- package/src/test/prompt.test.ts +0 -27
- package/src/test/promptProvider.test.ts +0 -33
- package/src/test/responseAwaiter.test.ts +0 -103
- package/src/test/sudoMcpServerManager.test.ts +0 -63
- package/src/test/testTools.ts +0 -176
- package/src/test/tools.test.ts +0 -64
- package/src/tool/agentChat.ts +0 -203
- package/src/tool/agentMain.ts +0 -180
- package/src/tool/chatMain.ts +0 -621
- package/src/tool/commandPrompt.ts +0 -264
- package/src/tool/files.ts +0 -82
- package/src/tool/main.ts +0 -25
- package/src/tool/nodePlatform.ts +0 -73
- package/src/tool/options.ts +0 -144
- package/src/tool/prompt.ts +0 -101
- package/test_data/background_test_profile.json +0 -6
- package/test_data/background_test_script.json +0 -11
- package/test_data/dummyllm_script_crash.json +0 -32
- package/test_data/dummyllm_script_image_gen.json +0 -19
- package/test_data/dummyllm_script_image_gen_fe.json +0 -29
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +0 -37
- package/test_data/dummyllm_script_render_tool.json +0 -29
- package/test_data/dummyllm_script_simplecalc.json +0 -28
- package/test_data/dummyllm_script_test_auto_approve.json +0 -81
- package/test_data/dummyllm_script_test_simplecalc_addition.json +0 -29
- package/test_data/frog.png +0 -0
- package/test_data/frog.png.b64 +0 -1
- package/test_data/git_message_profile.json +0 -4
- package/test_data/git_wip_system.txt +0 -5
- package/test_data/image_gen_test_profile.json +0 -5
- package/test_data/pr_message_profile.json +0 -4
- package/test_data/pr_review_profile.json +0 -4
- package/test_data/prompt_simplecalc.txt +0 -1
- package/test_data/simplecalc_profile.json +0 -4
- package/test_data/sudomcp_import_profile.json +0 -4
- package/test_data/test_script_profile.json +0 -8
- package/tsconfig.json +0 -13
- package/vitest.config.ts +0 -39
|
@@ -1,205 +0,0 @@
|
|
|
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 { userSessionCreateData } 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
|
-
XMCP_URL: "http://localhost:8081", // not used in this test
|
|
35
|
-
};
|
|
36
|
-
beforeAll(async () => {
|
|
37
|
-
// Set up environment variables
|
|
38
|
-
Object.assign(process.env, mockEnv);
|
|
39
|
-
// Initialize database connection
|
|
40
|
-
db = new Database(mockEnv.SUPABASE_URL, mockEnv.SUPABASE_KEY);
|
|
41
|
-
// Try to insert test data, but don't fail if there are database issues
|
|
42
|
-
try {
|
|
43
|
-
await cleanupTestData();
|
|
44
|
-
await insertTestApiKey();
|
|
45
|
-
logger.info("[Test] Test data setup completed successfully");
|
|
46
|
-
} catch (error) {
|
|
47
|
-
logger.warn(
|
|
48
|
-
"[Test] Test data setup failed - will test with existing data:",
|
|
49
|
-
error
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
// Start the real server using runServer
|
|
53
|
-
server = await runServer(
|
|
54
|
-
serverPort,
|
|
55
|
-
mockEnv.SUPABASE_URL,
|
|
56
|
-
mockEnv.SUPABASE_KEY,
|
|
57
|
-
mockEnv.XMCP_URL
|
|
58
|
-
);
|
|
59
|
-
// Wait for server to be ready
|
|
60
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
61
|
-
});
|
|
62
|
-
async function cleanupTestData() {
|
|
63
|
-
try {
|
|
64
|
-
// Delete test API key first (foreign key constraint)
|
|
65
|
-
await db
|
|
66
|
-
.getClientForTesting()
|
|
67
|
-
.from("api_keys")
|
|
68
|
-
.delete()
|
|
69
|
-
.eq("api_key", testApiKey);
|
|
70
|
-
// Delete test user
|
|
71
|
-
await db
|
|
72
|
-
.getClientForTesting()
|
|
73
|
-
.from("users")
|
|
74
|
-
.delete()
|
|
75
|
-
.eq("uuid", testUserUuid);
|
|
76
|
-
logger.info("[Test] Cleaned up existing test data");
|
|
77
|
-
} catch (error) {
|
|
78
|
-
logger.warn(
|
|
79
|
-
"[Test] Error during cleanup (expected if no data exists):",
|
|
80
|
-
error
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
async function insertTestApiKey() {
|
|
85
|
-
try {
|
|
86
|
-
// Use Database class methods which include timezone handling
|
|
87
|
-
await db.createUser(
|
|
88
|
-
testUserUuid,
|
|
89
|
-
"test@example.com",
|
|
90
|
-
testUserName,
|
|
91
|
-
"UTC"
|
|
92
|
-
);
|
|
93
|
-
logger.info("[Test] User created successfully");
|
|
94
|
-
await db.addApiKey(
|
|
95
|
-
testUserUuid,
|
|
96
|
-
testApiKey,
|
|
97
|
-
testUserName,
|
|
98
|
-
testScopes,
|
|
99
|
-
true // is_default
|
|
100
|
-
);
|
|
101
|
-
} catch (error) {
|
|
102
|
-
logger.error("[Test] Database method insert failed:", error);
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
afterAll(async () => {
|
|
107
|
-
// Clean up test data
|
|
108
|
-
await cleanupTestData();
|
|
109
|
-
// Close server
|
|
110
|
-
server.close();
|
|
111
|
-
});
|
|
112
|
-
describe("Connection Setup and List Sessions", () => {
|
|
113
|
-
it("should establish connection with valid API key", async () => {
|
|
114
|
-
// Inject test agent profiles and sessions into the database
|
|
115
|
-
const agentProfile1 = await db.createAgentProfile(
|
|
116
|
-
testUserUuid,
|
|
117
|
-
undefined,
|
|
118
|
-
"Test agent profile 1",
|
|
119
|
-
{
|
|
120
|
-
model: undefined,
|
|
121
|
-
system_prompt: "You are a test agent",
|
|
122
|
-
mcp_settings: {},
|
|
123
|
-
}
|
|
124
|
-
);
|
|
125
|
-
const agentProfile2 = await db.createAgentProfile(
|
|
126
|
-
testUserUuid,
|
|
127
|
-
undefined,
|
|
128
|
-
"Test agent profile 2",
|
|
129
|
-
{
|
|
130
|
-
model: undefined,
|
|
131
|
-
system_prompt: "You are a test agent",
|
|
132
|
-
mcp_settings: {},
|
|
133
|
-
}
|
|
134
|
-
);
|
|
135
|
-
if (!agentProfile1 || !agentProfile2) {
|
|
136
|
-
throw new Error("Failed to create agent profiles");
|
|
137
|
-
}
|
|
138
|
-
const agentProfile1Id = agentProfile1.uuid;
|
|
139
|
-
const agentProfile2Id = agentProfile2.uuid;
|
|
140
|
-
const session1CreateData = userSessionCreateData(
|
|
141
|
-
testUserUuid,
|
|
142
|
-
"Test Session 1",
|
|
143
|
-
agentProfile1Id
|
|
144
|
-
);
|
|
145
|
-
const session1Id = session1CreateData.session_uuid;
|
|
146
|
-
await db.sessionCreate(session1CreateData);
|
|
147
|
-
const session2CreateData = userSessionCreateData(
|
|
148
|
-
testUserUuid,
|
|
149
|
-
"Test Session 2",
|
|
150
|
-
agentProfile2Id
|
|
151
|
-
);
|
|
152
|
-
const session2Id = session2CreateData.session_uuid;
|
|
153
|
-
await db.sessionCreate(session2CreateData);
|
|
154
|
-
// Log session creation for debugging
|
|
155
|
-
logger.info(`[Test] Created sessions: ${session1Id}, ${session2Id}`);
|
|
156
|
-
const client = new Connection({
|
|
157
|
-
url: `ws://localhost:${String(serverPort)}`,
|
|
158
|
-
token: testApiKey,
|
|
159
|
-
});
|
|
160
|
-
// Wait for session_list response
|
|
161
|
-
const responsePromise = new Promise<ServerControlSessionList>(
|
|
162
|
-
(resolve) => {
|
|
163
|
-
client.on("control_session_list", (msg) => {
|
|
164
|
-
resolve(msg as ServerControlSessionList);
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
);
|
|
168
|
-
await client.connect();
|
|
169
|
-
expect(client.getState()).toBe("ready");
|
|
170
|
-
client.send({
|
|
171
|
-
type: "control_get_session_list",
|
|
172
|
-
client_message_id: "test-msg-id",
|
|
173
|
-
} as ClientControlGetSessionList);
|
|
174
|
-
const response = await responsePromise;
|
|
175
|
-
expect(response.type).toBe("control_session_list");
|
|
176
|
-
expect(response.user_sessions).toBeDefined();
|
|
177
|
-
expect(response.user_sessions.length).toBe(2);
|
|
178
|
-
// Verify session details
|
|
179
|
-
const sessionIds = response.user_sessions.map((s) => s.session_uuid);
|
|
180
|
-
expect(sessionIds).toContain(session1Id);
|
|
181
|
-
expect(sessionIds).toContain(session2Id);
|
|
182
|
-
const session1Data = response.user_sessions.find(
|
|
183
|
-
(s) => s.session_uuid === session1Id
|
|
184
|
-
);
|
|
185
|
-
expect(session1Data?.title).toBe("Test Session 1");
|
|
186
|
-
const session2Data = response.user_sessions.find(
|
|
187
|
-
(s) => s.session_uuid === session2Id
|
|
188
|
-
);
|
|
189
|
-
expect(session2Data?.title).toBe("Test Session 2");
|
|
190
|
-
client.close();
|
|
191
|
-
}, 15000);
|
|
192
|
-
});
|
|
193
|
-
describe("Authentication Error Handling", () => {
|
|
194
|
-
it("should handle invalid API key gracefully", async () => {
|
|
195
|
-
const invalidApiKey = "invalid-test-key";
|
|
196
|
-
const client = new Connection({
|
|
197
|
-
url: `ws://localhost:${String(serverPort)}`,
|
|
198
|
-
token: invalidApiKey,
|
|
199
|
-
});
|
|
200
|
-
await expect(client.connect()).rejects.toThrow();
|
|
201
|
-
expect(client.getState()).toBe("closed");
|
|
202
|
-
client.close();
|
|
203
|
-
}, 4000);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { expect } from "vitest";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
CompressingContextManager,
|
|
5
|
-
createCheckpointMessage,
|
|
6
|
-
} from "../agent/compressingContextManager";
|
|
7
|
-
import { ILLM, MessageParam, UserMessageParam } from "../agent/llm";
|
|
8
|
-
import { MESSAGES, basicContextTest } from "./contextTestTools";
|
|
9
|
-
import { createLLM } from "../agent/agentUtils";
|
|
10
|
-
import { IPlatform } from "../agent/iplatform";
|
|
11
|
-
|
|
12
|
-
describe("Compression context", () => {
|
|
13
|
-
it("context compression (basic)", async function () {
|
|
14
|
-
const ccm = new CompressingContextManager(
|
|
15
|
-
"sys_prompt",
|
|
16
|
-
[],
|
|
17
|
-
() => undefined as unknown as Promise<ILLM> // getLLM
|
|
18
|
-
);
|
|
19
|
-
await basicContextTest(ccm);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("context compression", async function () {
|
|
23
|
-
const llm = await createLLM(
|
|
24
|
-
"",
|
|
25
|
-
"",
|
|
26
|
-
"repeat",
|
|
27
|
-
false,
|
|
28
|
-
undefined as unknown as IPlatform
|
|
29
|
-
);
|
|
30
|
-
const ccm = new CompressingContextManager("sys_prompt", [], () =>
|
|
31
|
-
Promise.resolve(llm)
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
// Add first 2 messages
|
|
35
|
-
let tx = await ccm.startTx([MESSAGES[0] as UserMessageParam]);
|
|
36
|
-
tx.addMessage(MESSAGES[1]);
|
|
37
|
-
await ccm.commit(tx);
|
|
38
|
-
|
|
39
|
-
// Start the compression
|
|
40
|
-
const compressionP = ccm.compress();
|
|
41
|
-
|
|
42
|
-
// Add more messages and update system prompt
|
|
43
|
-
tx = await ccm.startTx([MESSAGES[2] as UserMessageParam]);
|
|
44
|
-
tx.addMessages(MESSAGES.slice(3));
|
|
45
|
-
await ccm.commit(tx);
|
|
46
|
-
ccm.setAgentPrompt("New system prompt");
|
|
47
|
-
|
|
48
|
-
// Wait for compression to complete, then get the new context
|
|
49
|
-
const summary = await compressionP;
|
|
50
|
-
const context = ccm.getLLMContext();
|
|
51
|
-
|
|
52
|
-
// Expect
|
|
53
|
-
// - callback happened
|
|
54
|
-
// - new context is as expected
|
|
55
|
-
|
|
56
|
-
const expectSummary = "Message number 0";
|
|
57
|
-
const expectContext: MessageParam[] = [
|
|
58
|
-
{
|
|
59
|
-
role: "system",
|
|
60
|
-
content: "New system prompt",
|
|
61
|
-
},
|
|
62
|
-
createCheckpointMessage(expectSummary),
|
|
63
|
-
MESSAGES[2], // 400
|
|
64
|
-
MESSAGES[3], // 500
|
|
65
|
-
MESSAGES[4], // 501
|
|
66
|
-
MESSAGES[5], // 502
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
expect(summary).eql(expectSummary);
|
|
70
|
-
expect(context).eql(expectContext);
|
|
71
|
-
|
|
72
|
-
// // Pending messages should still be intact
|
|
73
|
-
|
|
74
|
-
// const pending = ccm.getPending();
|
|
75
|
-
// expect(pending).eql(MESSAGES.slice(2));
|
|
76
|
-
});
|
|
77
|
-
});
|
package/src/test/context.test.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { expect } from "vitest";
|
|
2
|
-
import { strict as assert } from "assert";
|
|
3
|
-
|
|
4
|
-
import { ContextManagerWithWorkspace } from "../agent/contextWithWorkspace";
|
|
5
|
-
import { createUserMessage } from "../agent/agent";
|
|
6
|
-
import { MessageParam, UserMessageParam } from "../agent/llm";
|
|
7
|
-
import { SystemPromptProvider } from "../agent/promptProvider";
|
|
8
|
-
import { ContextManager } from "../agent/context";
|
|
9
|
-
import { basicContextTest } from "./contextTestTools";
|
|
10
|
-
|
|
11
|
-
describe("ContextManagerWithWorkspace", () => {
|
|
12
|
-
const msgs: MessageParam[] = [
|
|
13
|
-
{ role: "user", content: "message A" },
|
|
14
|
-
{ role: "assistant", content: "message B" },
|
|
15
|
-
{ role: "user", content: "message C" },
|
|
16
|
-
{ role: "assistant", content: "message D" },
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
it("context (basic)", async function () {
|
|
20
|
-
const cm = new ContextManager("sys_prompt", []);
|
|
21
|
-
await basicContextTest(cm);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("context with workspace (basic)", async function () {
|
|
25
|
-
const cm = new ContextManagerWithWorkspace("sys_prompt", []);
|
|
26
|
-
await basicContextTest(cm);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("should correctly manage messages", async function () {
|
|
30
|
-
const cm = new ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
|
|
31
|
-
|
|
32
|
-
// set workspace and add new user and assistant messages
|
|
33
|
-
cm.setWorkspace({ role: "user", content: "workspace1" });
|
|
34
|
-
|
|
35
|
-
const ctxTx1 = await cm.startTx([msgs[2] as UserMessageParam]);
|
|
36
|
-
ctxTx1.addMessage(msgs[3]);
|
|
37
|
-
|
|
38
|
-
// context writer. workspace should be before first assistant message
|
|
39
|
-
expect(ctxTx1.getLLMContext()).eql([
|
|
40
|
-
{ role: "system", content: "sys_msg" },
|
|
41
|
-
...msgs.slice(0, 3),
|
|
42
|
-
{ role: "user", content: "workspace1" },
|
|
43
|
-
...msgs.slice(3),
|
|
44
|
-
]);
|
|
45
|
-
|
|
46
|
-
// original cm unchanged
|
|
47
|
-
expect(cm.getLLMContext()).eql([
|
|
48
|
-
{ role: "system", content: "sys_msg" },
|
|
49
|
-
...msgs.slice(0, 2),
|
|
50
|
-
{ role: "user", content: "workspace1" },
|
|
51
|
-
]);
|
|
52
|
-
|
|
53
|
-
// Commit
|
|
54
|
-
await cm.commit(ctxTx1);
|
|
55
|
-
|
|
56
|
-
// write new messages
|
|
57
|
-
const lastMsg: MessageParam = {
|
|
58
|
-
role: "user",
|
|
59
|
-
content: "hello",
|
|
60
|
-
};
|
|
61
|
-
const ctxTx2 = await cm.startTx([lastMsg]);
|
|
62
|
-
expect(ctxTx2.getLLMContext()).eql([
|
|
63
|
-
{ role: "system", content: "sys_msg" },
|
|
64
|
-
...msgs,
|
|
65
|
-
lastMsg,
|
|
66
|
-
{ role: "user", content: "workspace1" },
|
|
67
|
-
]);
|
|
68
|
-
expect(cm.getLLMContext()).eql([
|
|
69
|
-
{ role: "system", content: "sys_msg" },
|
|
70
|
-
...msgs,
|
|
71
|
-
{ role: "user", content: "workspace1" },
|
|
72
|
-
]);
|
|
73
|
-
await cm.commit(ctxTx2);
|
|
74
|
-
|
|
75
|
-
// context without workspace
|
|
76
|
-
cm.setWorkspace(undefined);
|
|
77
|
-
const ctx3 = cm.getLLMContext();
|
|
78
|
-
expect(ctx3).eql([
|
|
79
|
-
{ role: "system", content: "sys_msg" },
|
|
80
|
-
...msgs,
|
|
81
|
-
lastMsg,
|
|
82
|
-
]);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("correctly includes all system prompt components", function () {
|
|
86
|
-
const cm = new ContextManagerWithWorkspace(
|
|
87
|
-
"agent_prompt",
|
|
88
|
-
msgs.slice(0, 2)
|
|
89
|
-
);
|
|
90
|
-
SystemPromptProvider.setGlobalPrompt("global");
|
|
91
|
-
cm.setPromptFragment("frag1", "FRAG1");
|
|
92
|
-
|
|
93
|
-
// Get the agentPrompt and llmContext, then undo the global prompt change
|
|
94
|
-
// (to ensure other tests are affected, even if this one fails).
|
|
95
|
-
|
|
96
|
-
const agentPrompt = cm.getAgentPrompt();
|
|
97
|
-
const llmContext = cm.getLLMContext();
|
|
98
|
-
SystemPromptProvider.setGlobalPrompt(undefined);
|
|
99
|
-
|
|
100
|
-
expect(agentPrompt).eql("agent_prompt");
|
|
101
|
-
expect(llmContext[0].content).eql("global\nagent_prompt\nFRAG1");
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("handles rich workspace messages", async function () {
|
|
105
|
-
const cm = new ContextManagerWithWorkspace("sys_msg", msgs.slice(0, 2));
|
|
106
|
-
|
|
107
|
-
expect(cm.getLLMContext()).eql([
|
|
108
|
-
{ role: "system", content: "sys_msg" },
|
|
109
|
-
...msgs.slice(0, 2),
|
|
110
|
-
]);
|
|
111
|
-
|
|
112
|
-
// set workspace and add new messages
|
|
113
|
-
const wsMsg = createUserMessage(
|
|
114
|
-
"text",
|
|
115
|
-
"data:image/png;base64;adsfadsfadf",
|
|
116
|
-
"user_name"
|
|
117
|
-
);
|
|
118
|
-
assert(wsMsg);
|
|
119
|
-
assert(wsMsg.content);
|
|
120
|
-
assert(typeof wsMsg.content !== "string");
|
|
121
|
-
cm.setWorkspace(wsMsg);
|
|
122
|
-
|
|
123
|
-
expect(cm.getLLMContext()).eql([
|
|
124
|
-
{ role: "system", content: "sys_msg" },
|
|
125
|
-
...msgs.slice(0, 2),
|
|
126
|
-
wsMsg,
|
|
127
|
-
]);
|
|
128
|
-
|
|
129
|
-
const ctxTx = await cm.startTx([msgs[2] as UserMessageParam]);
|
|
130
|
-
expect(ctxTx.getLLMContext()).eql([
|
|
131
|
-
{ role: "system", content: "sys_msg" },
|
|
132
|
-
...msgs.slice(0, 3),
|
|
133
|
-
wsMsg,
|
|
134
|
-
]);
|
|
135
|
-
ctxTx.addMessage(msgs[3]);
|
|
136
|
-
expect(ctxTx.getLLMContext()).eql([
|
|
137
|
-
{ role: "system", content: "sys_msg" },
|
|
138
|
-
...msgs.slice(0, 3),
|
|
139
|
-
wsMsg,
|
|
140
|
-
msgs[3],
|
|
141
|
-
]);
|
|
142
|
-
expect(ctxTx.newMessages()).eql(msgs.slice(2, 4));
|
|
143
|
-
|
|
144
|
-
await cm.commit(ctxTx);
|
|
145
|
-
|
|
146
|
-
// context with workspace
|
|
147
|
-
const ctx1 = cm.getLLMContext();
|
|
148
|
-
expect(ctx1).eql([{ role: "system", content: "sys_msg" }, ...msgs, wsMsg]);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { expect } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { IContextManager } from "../agent/context";
|
|
4
|
-
import { MessageParam, UserMessageParam } from "../agent/llm";
|
|
5
|
-
|
|
6
|
-
export const MESSAGES: MessageParam[] = [
|
|
7
|
-
{ role: "user", content: "msg200" },
|
|
8
|
-
{ role: "assistant", content: "msg300" },
|
|
9
|
-
{ role: "user", content: "msg400" },
|
|
10
|
-
{
|
|
11
|
-
role: "assistant",
|
|
12
|
-
content: "msg500",
|
|
13
|
-
tool_calls: [
|
|
14
|
-
{
|
|
15
|
-
id: "tool_call_0",
|
|
16
|
-
type: "function",
|
|
17
|
-
function: { name: "tool1", arguments: "" },
|
|
18
|
-
},
|
|
19
|
-
],
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
role: "tool",
|
|
23
|
-
content: "msg501",
|
|
24
|
-
tool_call_id: "tool_call_0",
|
|
25
|
-
},
|
|
26
|
-
{ role: "assistant", content: "msg502" },
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
export async function basicContextTest(cm: IContextManager) {
|
|
30
|
-
expect(cm.getAgentPrompt()).eql("sys_prompt");
|
|
31
|
-
|
|
32
|
-
// Add first 2 messages
|
|
33
|
-
let tx = await cm.startTx([MESSAGES[0] as UserMessageParam]);
|
|
34
|
-
tx.addMessage(MESSAGES[1]);
|
|
35
|
-
expect(tx.getLLMContext()).eql([
|
|
36
|
-
{
|
|
37
|
-
role: "system",
|
|
38
|
-
content: "sys_prompt",
|
|
39
|
-
},
|
|
40
|
-
MESSAGES[0],
|
|
41
|
-
MESSAGES[1],
|
|
42
|
-
]);
|
|
43
|
-
await cm.commit(tx);
|
|
44
|
-
|
|
45
|
-
// Add more messages
|
|
46
|
-
tx = await cm.startTx([MESSAGES[2] as UserMessageParam]);
|
|
47
|
-
expect(tx.getLLMContext()).eql([
|
|
48
|
-
{
|
|
49
|
-
role: "system",
|
|
50
|
-
content: "sys_prompt",
|
|
51
|
-
},
|
|
52
|
-
MESSAGES[0],
|
|
53
|
-
MESSAGES[1],
|
|
54
|
-
MESSAGES[2],
|
|
55
|
-
]);
|
|
56
|
-
tx.addMessages(MESSAGES.slice(3));
|
|
57
|
-
expect(tx.getLLMContext()).eql([
|
|
58
|
-
{
|
|
59
|
-
role: "system",
|
|
60
|
-
content: "sys_prompt",
|
|
61
|
-
},
|
|
62
|
-
MESSAGES[0],
|
|
63
|
-
MESSAGES[1],
|
|
64
|
-
MESSAGES[2],
|
|
65
|
-
MESSAGES[3],
|
|
66
|
-
MESSAGES[4],
|
|
67
|
-
MESSAGES[5],
|
|
68
|
-
]);
|
|
69
|
-
await cm.commit(tx);
|
|
70
|
-
|
|
71
|
-
// Update system prompt
|
|
72
|
-
cm.setAgentPrompt("New system prompt");
|
|
73
|
-
|
|
74
|
-
// Wait for compression to complete, then get the new context
|
|
75
|
-
const context = cm.getLLMContext();
|
|
76
|
-
|
|
77
|
-
// Expect
|
|
78
|
-
// - callback happened
|
|
79
|
-
// - new context is as expected
|
|
80
|
-
|
|
81
|
-
const expectContext: MessageParam[] = [
|
|
82
|
-
{
|
|
83
|
-
role: "system",
|
|
84
|
-
content: "New system prompt",
|
|
85
|
-
},
|
|
86
|
-
MESSAGES[0], // 400
|
|
87
|
-
MESSAGES[1], // 500
|
|
88
|
-
MESSAGES[2], // 400
|
|
89
|
-
MESSAGES[3], // 500
|
|
90
|
-
MESSAGES[4], // 501
|
|
91
|
-
MESSAGES[5], // 502
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
expect(context).eql(expectContext);
|
|
95
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
AssistantMessageParam,
|
|
4
|
-
MessageParam,
|
|
5
|
-
UserMessageParam,
|
|
6
|
-
} from "../agent/llm";
|
|
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: AssistantMessageParam[] = [
|
|
17
|
-
{ role: "assistant", content: "Message 0" },
|
|
18
|
-
{ role: "assistant", content: "Message 1" },
|
|
19
|
-
{ role: "assistant", content: "Message 2" },
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
const USER: UserMessageParam[] = [
|
|
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 MessageParam,
|
|
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 MessageParam,
|
|
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
|
-
});
|