@flink-app/flink 0.14.3 → 2.0.0-alpha.100
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/CHANGELOG.md +1051 -0
- package/SCHEMA_EXTRACTION_ANALYSIS.md +494 -0
- package/SIMPLE_AST_FEASIBILITY.md +570 -0
- package/bin/flink.ts +13 -2
- package/cli/build.ts +24 -44
- package/cli/clean.ts +13 -25
- package/cli/cli-utils.ts +190 -17
- package/cli/dev.ts +252 -0
- package/cli/loadEnvFiles.ts +116 -0
- package/cli/run.ts +45 -62
- package/dist/bin/flink.js +61 -2
- package/dist/cli/build.js +20 -25
- package/dist/cli/clean.js +12 -10
- package/dist/cli/cli-utils.d.ts +34 -3
- package/dist/cli/cli-utils.js +193 -12
- package/dist/cli/dev.d.ts +2 -0
- package/dist/cli/dev.js +279 -0
- package/dist/cli/loadEnvFiles.d.ts +30 -0
- package/dist/cli/loadEnvFiles.js +113 -0
- package/dist/cli/run.js +47 -46
- package/dist/src/DependencyTracker.d.ts +44 -0
- package/dist/src/DependencyTracker.js +239 -0
- package/dist/src/FlinkApp.d.ts +163 -10
- package/dist/src/FlinkApp.js +847 -184
- package/dist/src/FlinkContext.d.ts +41 -0
- package/dist/src/FlinkErrors.d.ts +19 -6
- package/dist/src/FlinkErrors.js +36 -42
- package/dist/src/FlinkHttpHandler.d.ts +219 -26
- package/dist/src/FlinkHttpHandler.js +37 -1
- package/dist/src/FlinkJob.d.ts +10 -0
- package/dist/src/FlinkLog.d.ts +82 -18
- package/dist/src/FlinkLog.js +165 -13
- package/dist/src/FlinkLogFactory.d.ts +288 -0
- package/dist/src/FlinkLogFactory.js +619 -0
- package/dist/src/FlinkRepo.d.ts +10 -2
- package/dist/src/FlinkRepo.js +11 -1
- package/dist/src/FlinkRequestContext.d.ts +63 -0
- package/dist/src/FlinkRequestContext.js +74 -0
- package/dist/src/FlinkResponse.d.ts +6 -0
- package/dist/src/FlinkService.d.ts +38 -0
- package/dist/src/FlinkService.js +46 -0
- package/dist/src/LeaderElection.d.ts +45 -0
- package/dist/src/LeaderElection.js +269 -0
- package/dist/src/SchemaCache.d.ts +84 -0
- package/dist/src/SchemaCache.js +289 -0
- package/dist/src/TypeScriptCompiler.d.ts +161 -51
- package/dist/src/TypeScriptCompiler.js +1253 -617
- package/dist/src/TypeScriptUtils.js +4 -0
- package/dist/src/ai/AgentRunner.d.ts +39 -0
- package/dist/src/ai/AgentRunner.js +760 -0
- package/dist/src/ai/ConversationAgent.d.ts +279 -0
- package/dist/src/ai/ConversationAgent.js +404 -0
- package/dist/src/ai/ConversationFlinkAgent.d.ts +278 -0
- package/dist/src/ai/ConversationFlinkAgent.js +404 -0
- package/dist/src/ai/FlinkAgent.d.ts +690 -0
- package/dist/src/ai/FlinkAgent.js +729 -0
- package/dist/src/ai/FlinkTool.d.ts +135 -0
- package/dist/src/ai/FlinkTool.js +2 -0
- package/dist/src/ai/InMemoryConversationAgent.d.ts +121 -0
- package/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/dist/src/ai/LLMAdapter.d.ts +148 -0
- package/dist/src/ai/LLMAdapter.js +2 -0
- package/dist/src/ai/PersistentFlinkAgent.d.ts +278 -0
- package/dist/src/ai/PersistentFlinkAgent.js +403 -0
- package/dist/src/ai/SubAgentExecutor.d.ts +38 -0
- package/dist/src/ai/SubAgentExecutor.js +223 -0
- package/dist/src/ai/ToolExecutor.d.ts +64 -0
- package/dist/src/ai/ToolExecutor.js +497 -0
- package/dist/src/ai/agentInstructions.d.ts +68 -0
- package/dist/src/ai/agentInstructions.js +286 -0
- package/dist/src/ai/index.d.ts +8 -0
- package/dist/src/ai/index.js +26 -0
- package/dist/src/ai/instructionFileLoader.d.ts +44 -0
- package/dist/src/ai/instructionFileLoader.js +179 -0
- package/dist/src/auth/FlinkAuthPlugin.d.ts +1 -1
- package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
- package/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +17 -0
- package/dist/src/loadPluginSchemas.d.ts +45 -0
- package/dist/src/loadPluginSchemas.js +143 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.d.ts +40 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.js +75 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.d.ts +321 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.js +925 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.d.ts +1 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.js +233 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.d.ts +57 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.js +177 -0
- package/dist/src/schema-extraction/index.d.ts +2 -0
- package/dist/src/schema-extraction/index.js +20 -0
- package/dist/src/schema-extraction/types.d.ts +31 -0
- package/dist/src/schema-extraction/types.js +2 -0
- package/dist/src/utils/loadFlinkConfig.d.ts +53 -0
- package/dist/src/utils/loadFlinkConfig.js +77 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +52 -0
- package/dist/src/workers/SchemaGeneratorWorker.d.ts +1 -0
- package/dist/src/workers/SchemaGeneratorWorker.js +49 -0
- package/dist/src/workers/WorkerPool.d.ts +60 -0
- package/dist/src/workers/WorkerPool.js +306 -0
- package/examples/logging-hierarchical-example.ts +125 -0
- package/package.json +29 -4
- package/readme.md +499 -0
- package/spec/AgentDescendantDetection.spec.ts +335 -0
- package/spec/AgentDuplicateDetection.spec.ts +112 -0
- package/spec/AgentObserver.spec.ts +266 -0
- package/spec/AgentRunner.spec.ts +1062 -0
- package/spec/AsyncLocalStorageContext.spec.ts +223 -0
- package/spec/ConversationHooks.spec.ts +257 -0
- package/spec/FlinkAgent.spec.ts +681 -0
- package/spec/FlinkApp.htmlResponse.spec.ts +260 -0
- package/spec/FlinkApp.onError.invocation.spec.ts +151 -0
- package/spec/FlinkApp.onError.spec.ts +1 -2
- package/spec/FlinkApp.query.spec.ts +107 -0
- package/spec/FlinkApp.routeOrdering.spec.ts +61 -0
- package/spec/FlinkApp.undefinedResponse.spec.ts +123 -0
- package/spec/FlinkApp.validationMode.spec.ts +155 -0
- package/spec/FlinkJob.spec.ts +171 -0
- package/spec/FlinkLogFactory.spec.ts +337 -0
- package/spec/FlinkRepo.spec.ts +1 -1
- package/spec/LeaderElection.spec.ts +174 -0
- package/spec/StreamingIntegration.spec.ts +139 -0
- package/spec/ToolExecutor.spec.ts +465 -0
- package/spec/TypeScriptCompiler.spec.ts +1 -1
- package/spec/TypeScriptSourceParser.spec.ts +1215 -0
- package/spec/TypeScriptTokenizer.spec.ts +366 -0
- package/spec/ai/ContextCompaction.spec.ts +405 -0
- package/spec/ai/ConversationAgent.spec.ts +520 -0
- package/spec/ai/InMemoryConversationAgent.spec.ts +144 -0
- package/spec/ai/agentInstructions.spec.ts +358 -0
- package/spec/fixtures/agent-instructions/TestAgent.ts +24 -0
- package/spec/fixtures/agent-instructions/simple.md +3 -0
- package/spec/fixtures/agent-instructions/template.md +18 -0
- package/spec/fixtures/agent-instructions/yaml-format.yaml +9 -0
- package/spec/mock-project/dist/.tsbuildinfo +1 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +56 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +75 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
- package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
- package/spec/mock-project/dist/src/FlinkApp.js +1000 -0
- package/spec/mock-project/dist/src/FlinkContext.js +2 -0
- package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
- package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
- package/spec/mock-project/dist/src/FlinkJob.js +2 -0
- package/spec/mock-project/dist/src/FlinkLog.js +119 -0
- package/spec/mock-project/dist/src/FlinkLogFactory.js +617 -0
- package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
- package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
- package/spec/mock-project/dist/src/FlinkRequestContext.js +74 -0
- package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
- package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
- package/spec/mock-project/dist/src/ai/AgentRunner.js +632 -0
- package/spec/mock-project/dist/src/ai/ConversationAgent.js +402 -0
- package/spec/mock-project/dist/src/ai/ConversationFlinkAgent.js +422 -0
- package/spec/mock-project/dist/src/ai/FlinkAgent.js +699 -0
- package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
- package/spec/mock-project/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
- package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +223 -0
- package/spec/mock-project/dist/src/ai/ToolExecutor.js +412 -0
- package/spec/mock-project/dist/src/ai/agentInstructions.js +246 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCar.js +26 -52
- package/spec/mock-project/dist/src/handlers/GetCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCar2.js +32 -54
- package/spec/mock-project/dist/src/handlers/GetCar2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +26 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +28 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +29 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +26 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +28 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +27 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +29 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +16 -49
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +25 -50
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchCar.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js +44 -70
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogin.js +25 -51
- package/spec/mock-project/dist/src/handlers/PostLogin.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogout.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostLogout.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PutCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PutCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/spec/mock-project/dist/src/index.js +52 -76
- package/spec/mock-project/dist/src/index.js.map +1 -0
- package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
- package/spec/mock-project/dist/src/repos/CarRepo.js +12 -24
- package/spec/mock-project/dist/src/repos/CarRepo.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/Car.js +3 -1
- package/spec/mock-project/dist/src/schemas/Car.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js +3 -1
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js +3 -1
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js.map +1 -0
- package/spec/mock-project/dist/src/utils.js +290 -0
- package/spec/mock-project/tsconfig.json +6 -1
- package/spec/schema-generation-nested-objects.spec.ts +97 -0
- package/spec/testHelpers.ts +49 -0
- package/spec/utils.caseConversion.spec.ts +78 -0
- package/spec/utils.spec.ts +13 -13
- package/src/DependencyTracker.ts +166 -0
- package/src/FlinkApp.ts +919 -155
- package/src/FlinkContext.ts +43 -0
- package/src/FlinkErrors.ts +32 -12
- package/src/FlinkHttpHandler.ts +246 -28
- package/src/FlinkJob.ts +11 -0
- package/src/FlinkLog.ts +119 -12
- package/src/FlinkLogFactory.ts +699 -0
- package/src/FlinkRepo.ts +10 -3
- package/src/FlinkRequestContext.ts +95 -0
- package/src/FlinkResponse.ts +6 -0
- package/src/FlinkService.ts +49 -0
- package/src/LeaderElection.ts +203 -0
- package/src/SchemaCache.ts +232 -0
- package/src/TypeScriptCompiler.ts +1347 -610
- package/src/TypeScriptUtils.ts +5 -0
- package/src/ai/AgentRunner.ts +646 -0
- package/src/ai/ConversationAgent.ts +413 -0
- package/src/ai/FlinkAgent.ts +1069 -0
- package/src/ai/FlinkTool.ts +165 -0
- package/src/ai/InMemoryConversationAgent.ts +149 -0
- package/src/ai/LLMAdapter.ts +126 -0
- package/src/ai/ToolExecutor.ts +485 -0
- package/src/ai/agentInstructions.ts +245 -0
- package/src/ai/index.ts +8 -0
- package/src/ai/instructionFileLoader.ts +156 -0
- package/src/auth/FlinkAuthPlugin.ts +2 -1
- package/src/handlers/StreamWriterFactory.ts +84 -0
- package/src/index.ts +14 -0
- package/src/loadPluginSchemas.ts +141 -0
- package/src/schema-extraction/TypeScriptSourceParser.ts +1058 -0
- package/src/schema-extraction/TypeScriptTokenizer.ts +205 -0
- package/src/schema-extraction/index.ts +2 -0
- package/src/schema-extraction/types.ts +34 -0
- package/src/utils/loadFlinkConfig.ts +89 -0
- package/src/utils.ts +52 -0
- package/tsconfig.json +6 -1
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConversationAgent,
|
|
3
|
+
ConversationData,
|
|
4
|
+
StorageMessage,
|
|
5
|
+
FlinkContext,
|
|
6
|
+
} from "../../src";
|
|
7
|
+
|
|
8
|
+
// Mock agent for testing
|
|
9
|
+
class MockConversationAgent extends ConversationAgent<FlinkContext> {
|
|
10
|
+
id = "mock-agent";
|
|
11
|
+
description = "Mock agent for testing";
|
|
12
|
+
instructions() { return "You are a test agent"; }
|
|
13
|
+
tools = [];
|
|
14
|
+
|
|
15
|
+
// Spy methods
|
|
16
|
+
loadConversationSpy = jasmine.createSpy("loadConversation");
|
|
17
|
+
saveConversationSpy = jasmine.createSpy("saveConversation");
|
|
18
|
+
|
|
19
|
+
protected async loadConversation(
|
|
20
|
+
conversationId: string
|
|
21
|
+
): Promise<ConversationData | null> {
|
|
22
|
+
return this.loadConversationSpy(conversationId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected async saveConversation(
|
|
26
|
+
conversationId: string,
|
|
27
|
+
data: ConversationData
|
|
28
|
+
): Promise<void> {
|
|
29
|
+
return this.saveConversationSpy(conversationId, data);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Expose protected methods for testing
|
|
33
|
+
public testBeforeRun(input: any, context: any) {
|
|
34
|
+
return (this as any).beforeRun(input, context);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public testAfterRun(result: any, context: any) {
|
|
38
|
+
return (this as any).afterRun(result, context);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public testConvertToMessages(llmMessages: any[]): StorageMessage[] {
|
|
42
|
+
return (this as any).convertToMessages(llmMessages);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
describe("ConversationAgent", () => {
|
|
47
|
+
let agent: MockConversationAgent;
|
|
48
|
+
let mockContext: FlinkContext;
|
|
49
|
+
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
mockContext = {} as FlinkContext;
|
|
52
|
+
agent = new MockConversationAgent();
|
|
53
|
+
(agent as any).ctx = mockContext;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("beforeRun", () => {
|
|
57
|
+
it("should load conversation when conversationId is provided", async () => {
|
|
58
|
+
const conversationId = "conv-123";
|
|
59
|
+
const conversationData: ConversationData = {
|
|
60
|
+
messages: [
|
|
61
|
+
{ role: "user", content: "Hello" },
|
|
62
|
+
{ role: "assistant", content: "Hi there" },
|
|
63
|
+
],
|
|
64
|
+
providerMetadata: {
|
|
65
|
+
openai: { responseId: "resp-123", instructionsHash: "hash-456" },
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
agent.loadConversationSpy.and.returnValue(
|
|
70
|
+
Promise.resolve(conversationData)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const input: any = { conversationId };
|
|
74
|
+
const context: any = {};
|
|
75
|
+
|
|
76
|
+
await agent.testBeforeRun(input, context);
|
|
77
|
+
|
|
78
|
+
expect(agent.loadConversationSpy).toHaveBeenCalledWith(
|
|
79
|
+
conversationId
|
|
80
|
+
);
|
|
81
|
+
expect(input.history).toBeDefined();
|
|
82
|
+
expect(input.history.length).toBe(2);
|
|
83
|
+
expect(input.providerMetadata).toEqual(
|
|
84
|
+
conversationData.providerMetadata
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should not populate history if conversation not found", async () => {
|
|
89
|
+
const conversationId = "conv-nonexistent";
|
|
90
|
+
|
|
91
|
+
agent.loadConversationSpy.and.returnValue(Promise.resolve(null));
|
|
92
|
+
|
|
93
|
+
const input: any = { conversationId };
|
|
94
|
+
const context: any = {};
|
|
95
|
+
|
|
96
|
+
await agent.testBeforeRun(input, context);
|
|
97
|
+
|
|
98
|
+
expect(agent.loadConversationSpy).toHaveBeenCalledWith(
|
|
99
|
+
conversationId
|
|
100
|
+
);
|
|
101
|
+
expect(input.history).toBeUndefined();
|
|
102
|
+
expect(input.providerMetadata).toBeUndefined();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should not call loadConversation if conversationId not provided", async () => {
|
|
106
|
+
const input: any = {};
|
|
107
|
+
const context: any = {};
|
|
108
|
+
|
|
109
|
+
await agent.testBeforeRun(input, context);
|
|
110
|
+
|
|
111
|
+
expect(agent.loadConversationSpy).not.toHaveBeenCalled();
|
|
112
|
+
expect(input.history).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("should propagate load errors", async () => {
|
|
116
|
+
const conversationId = "conv-error";
|
|
117
|
+
const error = new Error("Database connection failed");
|
|
118
|
+
|
|
119
|
+
agent.loadConversationSpy.and.returnValue(Promise.reject(error));
|
|
120
|
+
|
|
121
|
+
const input: any = { conversationId };
|
|
122
|
+
const context: any = {};
|
|
123
|
+
|
|
124
|
+
await expectAsync(
|
|
125
|
+
agent.testBeforeRun(input, context)
|
|
126
|
+
).toBeRejectedWith(error);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("provider metadata", () => {
|
|
131
|
+
it("should always preserve provider metadata", async () => {
|
|
132
|
+
const conversationData: ConversationData = {
|
|
133
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
134
|
+
providerMetadata: { openai: { responseId: "resp-123" } },
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
agent.loadConversationSpy.and.returnValue(
|
|
138
|
+
Promise.resolve(conversationData)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const input: any = { conversationId: "conv-123" };
|
|
142
|
+
await agent.testBeforeRun(input, {});
|
|
143
|
+
|
|
144
|
+
// Metadata is always preserved (no strategy filtering)
|
|
145
|
+
expect(input.providerMetadata).toEqual({
|
|
146
|
+
openai: { responseId: "resp-123" },
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("afterRun", () => {
|
|
152
|
+
it("should save conversation after successful execution", async () => {
|
|
153
|
+
const conversationId = "conv-123";
|
|
154
|
+
const llmMessages = [
|
|
155
|
+
{
|
|
156
|
+
role: "user",
|
|
157
|
+
content: [{ type: "text", text: "What cars do you have?" }],
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
role: "assistant",
|
|
161
|
+
content: [{ type: "text", text: "We have sedans and SUVs" }],
|
|
162
|
+
},
|
|
163
|
+
];
|
|
164
|
+
const providerMetadata = {
|
|
165
|
+
openai: { responseId: "resp-456", instructionsHash: "hash-789" },
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
agent.saveConversationSpy.and.returnValue(Promise.resolve());
|
|
169
|
+
|
|
170
|
+
const result: any = {
|
|
171
|
+
message: "Response",
|
|
172
|
+
toolCalls: [],
|
|
173
|
+
stepsUsed: 1,
|
|
174
|
+
stoppedEarly: false,
|
|
175
|
+
providerMetadata,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const context: any = {
|
|
179
|
+
messages: llmMessages,
|
|
180
|
+
conversationId,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
await agent.testAfterRun(result, context);
|
|
184
|
+
|
|
185
|
+
expect(agent.saveConversationSpy).toHaveBeenCalled();
|
|
186
|
+
|
|
187
|
+
const [savedConvId, savedData] =
|
|
188
|
+
agent.saveConversationSpy.calls.argsFor(0);
|
|
189
|
+
expect(savedConvId).toBe(conversationId);
|
|
190
|
+
expect(savedData.messages).toEqual([
|
|
191
|
+
{ role: "user", content: "What cars do you have?" },
|
|
192
|
+
{ role: "assistant", content: "We have sedans and SUVs" },
|
|
193
|
+
]);
|
|
194
|
+
expect(savedData.providerMetadata).toEqual(providerMetadata);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should skip save when conversationId not provided", async () => {
|
|
198
|
+
const result: any = {
|
|
199
|
+
message: "Response",
|
|
200
|
+
toolCalls: [],
|
|
201
|
+
stepsUsed: 1,
|
|
202
|
+
stoppedEarly: false,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const context: any = {
|
|
206
|
+
messages: [],
|
|
207
|
+
input: {},
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
await agent.testAfterRun(result, context);
|
|
211
|
+
|
|
212
|
+
expect(agent.saveConversationSpy).not.toHaveBeenCalled();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should not throw on save errors (log only)", async () => {
|
|
216
|
+
const conversationId = "conv-123";
|
|
217
|
+
const error = new Error("Database write failed");
|
|
218
|
+
|
|
219
|
+
agent.saveConversationSpy.and.returnValue(Promise.reject(error));
|
|
220
|
+
|
|
221
|
+
const result: any = {
|
|
222
|
+
message: "Response",
|
|
223
|
+
toolCalls: [],
|
|
224
|
+
stepsUsed: 1,
|
|
225
|
+
stoppedEarly: false,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const context: any = {
|
|
229
|
+
messages: [
|
|
230
|
+
{
|
|
231
|
+
role: "user",
|
|
232
|
+
content: [{ type: "text", text: "Hello" }],
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
conversationId,
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Should not throw - just log error
|
|
239
|
+
await expectAsync(agent.testAfterRun(result, context)).toBeResolved();
|
|
240
|
+
|
|
241
|
+
expect(agent.saveConversationSpy).toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe("convertToAgentMessages (loading from storage)", () => {
|
|
246
|
+
it("should convert user messages with toolResults to tool Messages", async () => {
|
|
247
|
+
const conversationData: ConversationData = {
|
|
248
|
+
messages: [
|
|
249
|
+
{ role: "user", content: "What is the weather?" },
|
|
250
|
+
{ role: "assistant", content: "Let me check" },
|
|
251
|
+
{
|
|
252
|
+
role: "user",
|
|
253
|
+
toolResults: [
|
|
254
|
+
{ id: "call_1", result: { temp: "22C", condition: "sunny" } },
|
|
255
|
+
],
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
agent.loadConversationSpy.and.returnValue(Promise.resolve(conversationData));
|
|
261
|
+
|
|
262
|
+
const input: any = { conversationId: "conv-123" };
|
|
263
|
+
await agent.testBeforeRun(input, {});
|
|
264
|
+
|
|
265
|
+
expect(input.history).toBeDefined();
|
|
266
|
+
expect(input.history.length).toBe(3);
|
|
267
|
+
|
|
268
|
+
// First message: user
|
|
269
|
+
expect(input.history[0]).toEqual({
|
|
270
|
+
role: "user",
|
|
271
|
+
content: "What is the weather?",
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Second message: assistant
|
|
275
|
+
expect(input.history[1]).toEqual({
|
|
276
|
+
role: "assistant",
|
|
277
|
+
content: "Let me check",
|
|
278
|
+
toolCalls: undefined,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Third message: tool result (converted from user with toolResults)
|
|
282
|
+
expect(input.history[2]).toEqual({
|
|
283
|
+
role: "tool",
|
|
284
|
+
toolCallId: "call_1",
|
|
285
|
+
toolName: "",
|
|
286
|
+
result: JSON.stringify({ temp: "22C", condition: "sunny" }),
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it("should convert multiple tool results to multiple tool Messages", async () => {
|
|
291
|
+
const conversationData: ConversationData = {
|
|
292
|
+
messages: [
|
|
293
|
+
{
|
|
294
|
+
role: "user",
|
|
295
|
+
toolResults: [
|
|
296
|
+
{ id: "call_1", result: { location: "Stockholm" } },
|
|
297
|
+
{ id: "call_2", result: { temp: "22C" } },
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
agent.loadConversationSpy.and.returnValue(Promise.resolve(conversationData));
|
|
304
|
+
|
|
305
|
+
const input: any = { conversationId: "conv-123" };
|
|
306
|
+
await agent.testBeforeRun(input, {});
|
|
307
|
+
|
|
308
|
+
expect(input.history).toBeDefined();
|
|
309
|
+
expect(input.history.length).toBe(2);
|
|
310
|
+
|
|
311
|
+
// Each tool result becomes a separate Message
|
|
312
|
+
expect(input.history[0]).toEqual({
|
|
313
|
+
role: "tool",
|
|
314
|
+
toolCallId: "call_1",
|
|
315
|
+
toolName: "",
|
|
316
|
+
result: JSON.stringify({ location: "Stockholm" }),
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
expect(input.history[1]).toEqual({
|
|
320
|
+
role: "tool",
|
|
321
|
+
toolCallId: "call_2",
|
|
322
|
+
toolName: "",
|
|
323
|
+
result: JSON.stringify({ temp: "22C" }),
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("should not double-encode string tool results on reload", async () => {
|
|
328
|
+
// Real production flow: ToolExecutor.formatResultForAI returns a JSON string,
|
|
329
|
+
// which is what AgentRunner stores on tool_result content blocks. On the
|
|
330
|
+
// round-trip back from storage, that string must NOT be JSON.stringified
|
|
331
|
+
// again — otherwise the model sees escaped JSON literals on every reload.
|
|
332
|
+
const alreadyStringified = JSON.stringify({ temp: "22C", condition: "sunny" });
|
|
333
|
+
|
|
334
|
+
const conversationData: ConversationData = {
|
|
335
|
+
messages: [
|
|
336
|
+
{
|
|
337
|
+
role: "user",
|
|
338
|
+
toolResults: [{ id: "call_1", result: alreadyStringified }],
|
|
339
|
+
},
|
|
340
|
+
],
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
agent.loadConversationSpy.and.returnValue(Promise.resolve(conversationData));
|
|
344
|
+
|
|
345
|
+
const input: any = { conversationId: "conv-123" };
|
|
346
|
+
await agent.testBeforeRun(input, {});
|
|
347
|
+
|
|
348
|
+
expect(input.history[0]).toEqual({
|
|
349
|
+
role: "tool",
|
|
350
|
+
toolCallId: "call_1",
|
|
351
|
+
toolName: "",
|
|
352
|
+
result: alreadyStringified, // not JSON.stringify(alreadyStringified)
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("should preserve assistant messages with toolCalls", async () => {
|
|
357
|
+
const conversationData: ConversationData = {
|
|
358
|
+
messages: [
|
|
359
|
+
{
|
|
360
|
+
role: "assistant",
|
|
361
|
+
content: "Let me check",
|
|
362
|
+
toolCalls: [
|
|
363
|
+
{ id: "call_1", name: "get-weather", input: { city: "Stockholm" } },
|
|
364
|
+
],
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
agent.loadConversationSpy.and.returnValue(Promise.resolve(conversationData));
|
|
370
|
+
|
|
371
|
+
const input: any = { conversationId: "conv-123" };
|
|
372
|
+
await agent.testBeforeRun(input, {});
|
|
373
|
+
|
|
374
|
+
expect(input.history).toBeDefined();
|
|
375
|
+
expect(input.history.length).toBe(1);
|
|
376
|
+
expect(input.history[0]).toEqual({
|
|
377
|
+
role: "assistant",
|
|
378
|
+
content: "Let me check",
|
|
379
|
+
toolCalls: [
|
|
380
|
+
{ id: "call_1", name: "get-weather", input: { city: "Stockholm" } },
|
|
381
|
+
],
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe("message conversion", () => {
|
|
387
|
+
it("should convert text content blocks to StorageMessage format", () => {
|
|
388
|
+
const llmMessages = [
|
|
389
|
+
{
|
|
390
|
+
role: "user",
|
|
391
|
+
content: [{ type: "text", text: "Hello world" }],
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
role: "assistant",
|
|
395
|
+
content: [{ type: "text", text: "Hi there!" }],
|
|
396
|
+
},
|
|
397
|
+
];
|
|
398
|
+
|
|
399
|
+
const result = agent.testConvertToMessages(llmMessages);
|
|
400
|
+
|
|
401
|
+
expect(result).toEqual([
|
|
402
|
+
{ role: "user", content: "Hello world" },
|
|
403
|
+
{ role: "assistant", content: "Hi there!" },
|
|
404
|
+
]);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("should convert tool calls from content blocks", () => {
|
|
408
|
+
const llmMessages = [
|
|
409
|
+
{
|
|
410
|
+
role: "assistant",
|
|
411
|
+
content: [
|
|
412
|
+
{
|
|
413
|
+
type: "tool_use",
|
|
414
|
+
id: "tool-123",
|
|
415
|
+
name: "search-cars",
|
|
416
|
+
input: { query: "SUV" },
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
},
|
|
420
|
+
];
|
|
421
|
+
|
|
422
|
+
const result = agent.testConvertToMessages(llmMessages);
|
|
423
|
+
|
|
424
|
+
expect(result).toEqual([
|
|
425
|
+
{
|
|
426
|
+
role: "assistant",
|
|
427
|
+
toolCalls: [
|
|
428
|
+
{
|
|
429
|
+
id: "tool-123",
|
|
430
|
+
name: "search-cars",
|
|
431
|
+
input: { query: "SUV" },
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
]);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("should convert tool results from content blocks", () => {
|
|
439
|
+
const llmMessages = [
|
|
440
|
+
{
|
|
441
|
+
role: "user",
|
|
442
|
+
content: [
|
|
443
|
+
{
|
|
444
|
+
type: "tool_result",
|
|
445
|
+
tool_use_id: "tool-123",
|
|
446
|
+
content: { cars: ["Model X", "Model Y"] },
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
},
|
|
450
|
+
];
|
|
451
|
+
|
|
452
|
+
const result = agent.testConvertToMessages(llmMessages);
|
|
453
|
+
|
|
454
|
+
expect(result).toEqual([
|
|
455
|
+
{
|
|
456
|
+
role: "user",
|
|
457
|
+
toolResults: [
|
|
458
|
+
{
|
|
459
|
+
id: "tool-123",
|
|
460
|
+
result: { cars: ["Model X", "Model Y"] },
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
},
|
|
464
|
+
]);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it("should handle string content (legacy format)", () => {
|
|
468
|
+
const llmMessages = [
|
|
469
|
+
{
|
|
470
|
+
role: "user",
|
|
471
|
+
content: "Hello",
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
role: "assistant",
|
|
475
|
+
content: "Hi there",
|
|
476
|
+
},
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
const result = agent.testConvertToMessages(llmMessages);
|
|
480
|
+
|
|
481
|
+
expect(result).toEqual([
|
|
482
|
+
{ role: "user", content: "Hello" },
|
|
483
|
+
{ role: "assistant", content: "Hi there" },
|
|
484
|
+
]);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("should handle mixed content blocks (text + tool calls)", () => {
|
|
488
|
+
const llmMessages = [
|
|
489
|
+
{
|
|
490
|
+
role: "assistant",
|
|
491
|
+
content: [
|
|
492
|
+
{ type: "text", text: "Let me search for that" },
|
|
493
|
+
{
|
|
494
|
+
type: "tool_use",
|
|
495
|
+
id: "tool-123",
|
|
496
|
+
name: "search-cars",
|
|
497
|
+
input: { query: "sedan" },
|
|
498
|
+
},
|
|
499
|
+
],
|
|
500
|
+
},
|
|
501
|
+
];
|
|
502
|
+
|
|
503
|
+
const result = agent.testConvertToMessages(llmMessages);
|
|
504
|
+
|
|
505
|
+
expect(result).toEqual([
|
|
506
|
+
{
|
|
507
|
+
role: "assistant",
|
|
508
|
+
content: "Let me search for that",
|
|
509
|
+
toolCalls: [
|
|
510
|
+
{
|
|
511
|
+
id: "tool-123",
|
|
512
|
+
name: "search-cars",
|
|
513
|
+
input: { query: "sedan" },
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
},
|
|
517
|
+
]);
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { InMemoryConversationAgent, FlinkContext } from "../../src";
|
|
2
|
+
|
|
3
|
+
class TestAgent extends InMemoryConversationAgent<FlinkContext> {
|
|
4
|
+
id = "test-agent";
|
|
5
|
+
description = "Test agent";
|
|
6
|
+
instructions() { return "You are a test agent"; }
|
|
7
|
+
tools = [];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe("InMemoryConversationAgent", () => {
|
|
11
|
+
let agent: TestAgent;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
InMemoryConversationAgent.clearAll();
|
|
15
|
+
agent = new TestAgent();
|
|
16
|
+
(agent as any).ctx = {} as FlinkContext;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("conversation storage", () => {
|
|
20
|
+
it("should save and load conversations", async () => {
|
|
21
|
+
const conversationId = "test-conv-1";
|
|
22
|
+
const testData = {
|
|
23
|
+
messages: [
|
|
24
|
+
{ role: "user" as const, content: "Hello" },
|
|
25
|
+
{ role: "assistant" as const, content: "Hi there" },
|
|
26
|
+
],
|
|
27
|
+
providerMetadata: { openai: { responseId: "resp-123" } },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Save
|
|
31
|
+
await (agent as any).saveConversation(conversationId, testData, {}, {});
|
|
32
|
+
|
|
33
|
+
// Load
|
|
34
|
+
const loaded = await (agent as any).loadConversation(conversationId);
|
|
35
|
+
|
|
36
|
+
expect(loaded).toEqual(testData);
|
|
37
|
+
expect(loaded?.messages.length).toBe(2);
|
|
38
|
+
expect(loaded?.providerMetadata?.openai?.responseId).toBe("resp-123");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should return null for non-existent conversations", async () => {
|
|
42
|
+
const loaded = await (agent as any).loadConversation("non-existent");
|
|
43
|
+
expect(loaded).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should update existing conversations", async () => {
|
|
47
|
+
const conversationId = "test-conv-2";
|
|
48
|
+
|
|
49
|
+
// First save
|
|
50
|
+
await (agent as any).saveConversation(
|
|
51
|
+
conversationId,
|
|
52
|
+
{ messages: [{ role: "user", content: "First" }] },
|
|
53
|
+
{},
|
|
54
|
+
{}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Update
|
|
58
|
+
await (agent as any).saveConversation(
|
|
59
|
+
conversationId,
|
|
60
|
+
{
|
|
61
|
+
messages: [
|
|
62
|
+
{ role: "user", content: "First" },
|
|
63
|
+
{ role: "assistant", content: "Second" },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{},
|
|
67
|
+
{}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const loaded = await (agent as any).loadConversation(conversationId);
|
|
71
|
+
expect(loaded?.messages.length).toBe(2);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("static utilities", () => {
|
|
76
|
+
it("should track conversation count", async () => {
|
|
77
|
+
expect(InMemoryConversationAgent.getConversationCount()).toBe(0);
|
|
78
|
+
|
|
79
|
+
await (agent as any).saveConversation("conv-1", { messages: [] }, {}, {});
|
|
80
|
+
expect(InMemoryConversationAgent.getConversationCount()).toBe(1);
|
|
81
|
+
|
|
82
|
+
await (agent as any).saveConversation("conv-2", { messages: [] }, {}, {});
|
|
83
|
+
expect(InMemoryConversationAgent.getConversationCount()).toBe(2);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should clear all conversations", async () => {
|
|
87
|
+
await (agent as any).saveConversation("conv-1", { messages: [] }, {}, {});
|
|
88
|
+
await (agent as any).saveConversation("conv-2", { messages: [] }, {}, {});
|
|
89
|
+
|
|
90
|
+
expect(InMemoryConversationAgent.getConversationCount()).toBe(2);
|
|
91
|
+
|
|
92
|
+
InMemoryConversationAgent.clearAll();
|
|
93
|
+
|
|
94
|
+
expect(InMemoryConversationAgent.getConversationCount()).toBe(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should get specific conversation", async () => {
|
|
98
|
+
const testData = { messages: [{ role: "user" as const, content: "Test" }] };
|
|
99
|
+
await (agent as any).saveConversation("conv-1", testData, {}, {});
|
|
100
|
+
|
|
101
|
+
const retrieved = InMemoryConversationAgent.getConversation("conv-1");
|
|
102
|
+
expect(retrieved).toEqual(testData);
|
|
103
|
+
|
|
104
|
+
const missing = InMemoryConversationAgent.getConversation("missing");
|
|
105
|
+
expect(missing).toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should get all conversation IDs", async () => {
|
|
109
|
+
await (agent as any).saveConversation("conv-1", { messages: [] }, {}, {});
|
|
110
|
+
await (agent as any).saveConversation("conv-2", { messages: [] }, {}, {});
|
|
111
|
+
await (agent as any).saveConversation("conv-3", { messages: [] }, {}, {});
|
|
112
|
+
|
|
113
|
+
const ids = InMemoryConversationAgent.getAllConversationIds();
|
|
114
|
+
expect(ids.length).toBe(3);
|
|
115
|
+
expect(ids).toContain("conv-1");
|
|
116
|
+
expect(ids).toContain("conv-2");
|
|
117
|
+
expect(ids).toContain("conv-3");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("isolation", () => {
|
|
122
|
+
it("should isolate conversations by ID", async () => {
|
|
123
|
+
await (agent as any).saveConversation(
|
|
124
|
+
"conv-a",
|
|
125
|
+
{ messages: [{ role: "user" as const, content: "A" }] },
|
|
126
|
+
{},
|
|
127
|
+
{}
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
await (agent as any).saveConversation(
|
|
131
|
+
"conv-b",
|
|
132
|
+
{ messages: [{ role: "user" as const, content: "B" }] },
|
|
133
|
+
{},
|
|
134
|
+
{}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const convA = await (agent as any).loadConversation("conv-a");
|
|
138
|
+
const convB = await (agent as any).loadConversation("conv-b");
|
|
139
|
+
|
|
140
|
+
expect(convA?.messages[0].content).toBe("A");
|
|
141
|
+
expect(convB?.messages[0].content).toBe("B");
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|