@tambo-ai/react 0.70.0 → 0.71.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/v1/hooks/use-tambo-v1-messages.d.ts +58 -0
- package/dist/v1/hooks/use-tambo-v1-messages.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-messages.js +54 -0
- package/dist/v1/hooks/use-tambo-v1-messages.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.js +137 -0
- package/dist/v1/hooks/use-tambo-v1-messages.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.d.ts +96 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.js +227 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.js +827 -0
- package/dist/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts +61 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.js +56 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.js +98 -0
- package/dist/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.d.ts +37 -0
- package/dist/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.js +49 -0
- package/dist/v1/hooks/use-tambo-v1-thread.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.js +83 -0
- package/dist/v1/hooks/use-tambo-v1-thread.test.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.d.ts +107 -0
- package/dist/v1/hooks/use-tambo-v1.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.js +87 -0
- package/dist/v1/hooks/use-tambo-v1.js.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.test.d.ts +2 -0
- package/dist/v1/hooks/use-tambo-v1.test.d.ts.map +1 -0
- package/dist/v1/hooks/use-tambo-v1.test.js +150 -0
- package/dist/v1/hooks/use-tambo-v1.test.js.map +1 -0
- package/dist/v1/index.d.ts +54 -16
- package/dist/v1/index.d.ts.map +1 -1
- package/dist/v1/index.js +85 -26
- package/dist/v1/index.js.map +1 -1
- package/dist/v1/providers/tambo-v1-provider.d.ts +91 -0
- package/dist/v1/providers/tambo-v1-provider.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.js +110 -0
- package/dist/v1/providers/tambo-v1-provider.js.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.test.d.ts +2 -0
- package/dist/v1/providers/tambo-v1-provider.test.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-provider.test.js +123 -0
- package/dist/v1/providers/tambo-v1-provider.test.js.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.d.ts +136 -0
- package/dist/v1/providers/tambo-v1-stream-context.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.js +230 -0
- package/dist/v1/providers/tambo-v1-stream-context.js.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.d.ts +2 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.d.ts.map +1 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.js +85 -0
- package/dist/v1/providers/tambo-v1-stream-context.test.js.map +1 -0
- package/dist/v1/types/component.d.ts +5 -2
- package/dist/v1/types/component.d.ts.map +1 -1
- package/dist/v1/types/component.js +5 -2
- package/dist/v1/types/component.js.map +1 -1
- package/dist/v1/types/event.d.ts +21 -12
- package/dist/v1/types/event.d.ts.map +1 -1
- package/dist/v1/types/event.js +46 -1
- package/dist/v1/types/event.js.map +1 -1
- package/dist/v1/types/event.test.d.ts +2 -0
- package/dist/v1/types/event.test.d.ts.map +1 -0
- package/dist/v1/types/event.test.js +70 -0
- package/dist/v1/types/event.test.js.map +1 -0
- package/dist/v1/types/message.d.ts +4 -8
- package/dist/v1/types/message.d.ts.map +1 -1
- package/dist/v1/types/message.js +1 -1
- package/dist/v1/types/message.js.map +1 -1
- package/dist/v1/types/thread.d.ts +1 -3
- package/dist/v1/types/thread.d.ts.map +1 -1
- package/dist/v1/types/thread.js +1 -1
- package/dist/v1/types/thread.js.map +1 -1
- package/dist/v1/utils/event-accumulator.d.ts +100 -0
- package/dist/v1/utils/event-accumulator.d.ts.map +1 -0
- package/dist/v1/utils/event-accumulator.js +715 -0
- package/dist/v1/utils/event-accumulator.js.map +1 -0
- package/dist/v1/utils/event-accumulator.test.d.ts +2 -0
- package/dist/v1/utils/event-accumulator.test.d.ts.map +1 -0
- package/dist/v1/utils/event-accumulator.test.js +1010 -0
- package/dist/v1/utils/event-accumulator.test.js.map +1 -0
- package/dist/v1/utils/json-patch.d.ts +18 -0
- package/dist/v1/utils/json-patch.d.ts.map +1 -0
- package/dist/v1/utils/json-patch.js +35 -0
- package/dist/v1/utils/json-patch.js.map +1 -0
- package/dist/v1/utils/json-patch.test.d.ts +2 -0
- package/dist/v1/utils/json-patch.test.d.ts.map +1 -0
- package/dist/v1/utils/json-patch.test.js +28 -0
- package/dist/v1/utils/json-patch.test.js.map +1 -0
- package/dist/v1/utils/registry-conversion.d.ts +53 -0
- package/dist/v1/utils/registry-conversion.d.ts.map +1 -0
- package/dist/v1/utils/registry-conversion.js +114 -0
- package/dist/v1/utils/registry-conversion.js.map +1 -0
- package/dist/v1/utils/registry-conversion.test.d.ts +2 -0
- package/dist/v1/utils/registry-conversion.test.d.ts.map +1 -0
- package/dist/v1/utils/registry-conversion.test.js +179 -0
- package/dist/v1/utils/registry-conversion.test.js.map +1 -0
- package/dist/v1/utils/stream-handler.d.ts +45 -0
- package/dist/v1/utils/stream-handler.d.ts.map +1 -0
- package/dist/v1/utils/stream-handler.js +47 -0
- package/dist/v1/utils/stream-handler.js.map +1 -0
- package/dist/v1/utils/stream-handler.test.d.ts +2 -0
- package/dist/v1/utils/stream-handler.test.d.ts.map +1 -0
- package/dist/v1/utils/stream-handler.test.js +74 -0
- package/dist/v1/utils/stream-handler.test.js.map +1 -0
- package/dist/v1/utils/tool-call-tracker.d.ts +41 -0
- package/dist/v1/utils/tool-call-tracker.d.ts.map +1 -0
- package/dist/v1/utils/tool-call-tracker.js +90 -0
- package/dist/v1/utils/tool-call-tracker.js.map +1 -0
- package/dist/v1/utils/tool-executor.d.ts +33 -0
- package/dist/v1/utils/tool-executor.d.ts.map +1 -0
- package/dist/v1/utils/tool-executor.js +103 -0
- package/dist/v1/utils/tool-executor.js.map +1 -0
- package/dist/v1/utils/tool-executor.test.d.ts +2 -0
- package/dist/v1/utils/tool-executor.test.d.ts.map +1 -0
- package/dist/v1/utils/tool-executor.test.js +222 -0
- package/dist/v1/utils/tool-executor.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-messages.d.ts +58 -0
- package/esm/v1/hooks/use-tambo-v1-messages.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-messages.js +51 -0
- package/esm/v1/hooks/use-tambo-v1-messages.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.js +132 -0
- package/esm/v1/hooks/use-tambo-v1-messages.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.d.ts +96 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.js +223 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.js +822 -0
- package/esm/v1/hooks/use-tambo-v1-send-message.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts +61 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.js +53 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.js +93 -0
- package/esm/v1/hooks/use-tambo-v1-thread-list.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.d.ts +37 -0
- package/esm/v1/hooks/use-tambo-v1-thread.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.js +46 -0
- package/esm/v1/hooks/use-tambo-v1-thread.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.js +78 -0
- package/esm/v1/hooks/use-tambo-v1-thread.test.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.d.ts +107 -0
- package/esm/v1/hooks/use-tambo-v1.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.js +84 -0
- package/esm/v1/hooks/use-tambo-v1.js.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.test.d.ts +2 -0
- package/esm/v1/hooks/use-tambo-v1.test.d.ts.map +1 -0
- package/esm/v1/hooks/use-tambo-v1.test.js +145 -0
- package/esm/v1/hooks/use-tambo-v1.test.js.map +1 -0
- package/esm/v1/index.d.ts +54 -16
- package/esm/v1/index.d.ts.map +1 -1
- package/esm/v1/index.js +64 -27
- package/esm/v1/index.js.map +1 -1
- package/esm/v1/providers/tambo-v1-provider.d.ts +91 -0
- package/esm/v1/providers/tambo-v1-provider.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.js +74 -0
- package/esm/v1/providers/tambo-v1-provider.js.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.test.d.ts +2 -0
- package/esm/v1/providers/tambo-v1-provider.test.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-provider.test.js +118 -0
- package/esm/v1/providers/tambo-v1-provider.test.js.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.d.ts +136 -0
- package/esm/v1/providers/tambo-v1-stream-context.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.js +191 -0
- package/esm/v1/providers/tambo-v1-stream-context.js.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.d.ts +2 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.d.ts.map +1 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.js +80 -0
- package/esm/v1/providers/tambo-v1-stream-context.test.js.map +1 -0
- package/esm/v1/types/component.d.ts +5 -2
- package/esm/v1/types/component.d.ts.map +1 -1
- package/esm/v1/types/component.js +5 -2
- package/esm/v1/types/component.js.map +1 -1
- package/esm/v1/types/event.d.ts +21 -12
- package/esm/v1/types/event.d.ts.map +1 -1
- package/esm/v1/types/event.js +44 -2
- package/esm/v1/types/event.js.map +1 -1
- package/esm/v1/types/event.test.d.ts +2 -0
- package/esm/v1/types/event.test.d.ts.map +1 -0
- package/esm/v1/types/event.test.js +68 -0
- package/esm/v1/types/event.test.js.map +1 -0
- package/esm/v1/types/message.d.ts +4 -8
- package/esm/v1/types/message.d.ts.map +1 -1
- package/esm/v1/types/message.js +1 -1
- package/esm/v1/types/message.js.map +1 -1
- package/esm/v1/types/thread.d.ts +1 -3
- package/esm/v1/types/thread.d.ts.map +1 -1
- package/esm/v1/types/thread.js +1 -1
- package/esm/v1/types/thread.js.map +1 -1
- package/esm/v1/utils/event-accumulator.d.ts +100 -0
- package/esm/v1/utils/event-accumulator.d.ts.map +1 -0
- package/esm/v1/utils/event-accumulator.js +708 -0
- package/esm/v1/utils/event-accumulator.js.map +1 -0
- package/esm/v1/utils/event-accumulator.test.d.ts +2 -0
- package/esm/v1/utils/event-accumulator.test.d.ts.map +1 -0
- package/esm/v1/utils/event-accumulator.test.js +1008 -0
- package/esm/v1/utils/event-accumulator.test.js.map +1 -0
- package/esm/v1/utils/json-patch.d.ts +18 -0
- package/esm/v1/utils/json-patch.d.ts.map +1 -0
- package/esm/v1/utils/json-patch.js +32 -0
- package/esm/v1/utils/json-patch.js.map +1 -0
- package/esm/v1/utils/json-patch.test.d.ts +2 -0
- package/esm/v1/utils/json-patch.test.d.ts.map +1 -0
- package/esm/v1/utils/json-patch.test.js +26 -0
- package/esm/v1/utils/json-patch.test.js.map +1 -0
- package/esm/v1/utils/registry-conversion.d.ts +53 -0
- package/esm/v1/utils/registry-conversion.d.ts.map +1 -0
- package/esm/v1/utils/registry-conversion.js +108 -0
- package/esm/v1/utils/registry-conversion.js.map +1 -0
- package/esm/v1/utils/registry-conversion.test.d.ts +2 -0
- package/esm/v1/utils/registry-conversion.test.d.ts.map +1 -0
- package/esm/v1/utils/registry-conversion.test.js +177 -0
- package/esm/v1/utils/registry-conversion.test.js.map +1 -0
- package/esm/v1/utils/stream-handler.d.ts +45 -0
- package/esm/v1/utils/stream-handler.d.ts.map +1 -0
- package/esm/v1/utils/stream-handler.js +44 -0
- package/esm/v1/utils/stream-handler.js.map +1 -0
- package/esm/v1/utils/stream-handler.test.d.ts +2 -0
- package/esm/v1/utils/stream-handler.test.d.ts.map +1 -0
- package/esm/v1/utils/stream-handler.test.js +72 -0
- package/esm/v1/utils/stream-handler.test.js.map +1 -0
- package/esm/v1/utils/tool-call-tracker.d.ts +41 -0
- package/esm/v1/utils/tool-call-tracker.d.ts.map +1 -0
- package/esm/v1/utils/tool-call-tracker.js +86 -0
- package/esm/v1/utils/tool-call-tracker.js.map +1 -0
- package/esm/v1/utils/tool-executor.d.ts +33 -0
- package/esm/v1/utils/tool-executor.d.ts.map +1 -0
- package/esm/v1/utils/tool-executor.js +99 -0
- package/esm/v1/utils/tool-executor.js.map +1 -0
- package/esm/v1/utils/tool-executor.test.d.ts +2 -0
- package/esm/v1/utils/tool-executor.test.d.ts.map +1 -0
- package/esm/v1/utils/tool-executor.test.js +220 -0
- package/esm/v1/utils/tool-executor.test.js.map +1 -0
- package/package.json +7 -6
- package/dist/v1/types/tool.d.ts +0 -52
- package/dist/v1/types/tool.d.ts.map +0 -1
- package/dist/v1/types/tool.js +0 -11
- package/dist/v1/types/tool.js.map +0 -1
- package/esm/v1/types/tool.d.ts +0 -52
- package/esm/v1/types/tool.d.ts.map +0 -1
- package/esm/v1/types/tool.js +0 -10
- package/esm/v1/types/tool.js.map +0 -1
|
@@ -0,0 +1,1010 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@ag-ui/core");
|
|
4
|
+
const event_accumulator_1 = require("./event-accumulator");
|
|
5
|
+
/**
|
|
6
|
+
* Helper to extract a ToolUseContent from a message content array.
|
|
7
|
+
* @param content - Content array from a message
|
|
8
|
+
* @param index - Index of the content item
|
|
9
|
+
* @returns The content as ToolUseContent
|
|
10
|
+
*/
|
|
11
|
+
function asToolUseContent(content, index) {
|
|
12
|
+
return content[index];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Helper to extract a ComponentContent from a message content array.
|
|
16
|
+
* @param content - Content array from a message
|
|
17
|
+
* @param index - Index of the content item
|
|
18
|
+
* @returns The content as ComponentContent
|
|
19
|
+
*/
|
|
20
|
+
function asComponentContent(content, index) {
|
|
21
|
+
return content[index];
|
|
22
|
+
}
|
|
23
|
+
// Helper to create a base thread state for testing
|
|
24
|
+
function createTestThreadState(threadId) {
|
|
25
|
+
return {
|
|
26
|
+
...(0, event_accumulator_1.createInitialThreadState)(threadId),
|
|
27
|
+
thread: {
|
|
28
|
+
...(0, event_accumulator_1.createInitialThreadState)(threadId).thread,
|
|
29
|
+
// Use fixed timestamps for snapshot stability
|
|
30
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
31
|
+
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Helper to create stream state with a thread
|
|
36
|
+
function createTestStreamState(threadId) {
|
|
37
|
+
return {
|
|
38
|
+
threadMap: {
|
|
39
|
+
[threadId]: createTestThreadState(threadId),
|
|
40
|
+
},
|
|
41
|
+
currentThreadId: threadId,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
describe("createInitialThreadState", () => {
|
|
45
|
+
it("creates thread state with correct structure", () => {
|
|
46
|
+
const state = (0, event_accumulator_1.createInitialThreadState)("thread_123");
|
|
47
|
+
expect(state.thread.id).toBe("thread_123");
|
|
48
|
+
expect(state.thread.messages).toEqual([]);
|
|
49
|
+
expect(state.thread.status).toBe("idle");
|
|
50
|
+
expect(state.streaming.status).toBe("idle");
|
|
51
|
+
expect(state.accumulatingToolArgs).toBeInstanceOf(Map);
|
|
52
|
+
expect(state.accumulatingToolArgs.size).toBe(0);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("createInitialState", () => {
|
|
56
|
+
it("creates empty stream state", () => {
|
|
57
|
+
const state = (0, event_accumulator_1.createInitialState)();
|
|
58
|
+
expect(state.threadMap).toEqual({});
|
|
59
|
+
expect(state.currentThreadId).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe("streamReducer", () => {
|
|
63
|
+
// Mock console.warn for tests that check logging
|
|
64
|
+
let consoleWarnSpy;
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => { });
|
|
67
|
+
});
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
consoleWarnSpy.mockRestore();
|
|
70
|
+
});
|
|
71
|
+
describe("unknown thread handling", () => {
|
|
72
|
+
it("auto-initializes unknown thread when receiving events", () => {
|
|
73
|
+
const state = (0, event_accumulator_1.createInitialState)();
|
|
74
|
+
const event = {
|
|
75
|
+
type: core_1.EventType.RUN_STARTED,
|
|
76
|
+
runId: "run_1",
|
|
77
|
+
threadId: "unknown_thread",
|
|
78
|
+
};
|
|
79
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
80
|
+
type: "EVENT",
|
|
81
|
+
event,
|
|
82
|
+
threadId: "unknown_thread",
|
|
83
|
+
});
|
|
84
|
+
// Should auto-initialize the thread rather than dropping the event
|
|
85
|
+
expect(result.threadMap.unknown_thread).toBeDefined();
|
|
86
|
+
expect(result.threadMap.unknown_thread.thread.id).toBe("unknown_thread");
|
|
87
|
+
expect(result.threadMap.unknown_thread.streaming.status).toBe("streaming");
|
|
88
|
+
expect(result.threadMap.unknown_thread.streaming.runId).toBe("run_1");
|
|
89
|
+
// No warning should be logged for auto-initialization
|
|
90
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe("unsupported event types", () => {
|
|
94
|
+
it("logs warning for unsupported AG-UI events", () => {
|
|
95
|
+
const state = createTestStreamState("thread_1");
|
|
96
|
+
const event = {
|
|
97
|
+
type: core_1.EventType.STATE_SNAPSHOT,
|
|
98
|
+
snapshot: {},
|
|
99
|
+
};
|
|
100
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
101
|
+
type: "EVENT",
|
|
102
|
+
event,
|
|
103
|
+
threadId: "thread_1",
|
|
104
|
+
});
|
|
105
|
+
expect(result).toBe(state);
|
|
106
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("unsupported event type"));
|
|
107
|
+
});
|
|
108
|
+
it("throws for completely unknown event types (fail-fast)", () => {
|
|
109
|
+
const state = createTestStreamState("thread_1");
|
|
110
|
+
// Create an event with an unknown type (not in EventType enum)
|
|
111
|
+
// This tests fail-fast behavior when SDK returns unexpected event types
|
|
112
|
+
const event = {
|
|
113
|
+
type: "TOTALLY_UNKNOWN_EVENT_TYPE",
|
|
114
|
+
};
|
|
115
|
+
expect(() => (0, event_accumulator_1.streamReducer)(state, {
|
|
116
|
+
type: "EVENT",
|
|
117
|
+
event: event,
|
|
118
|
+
threadId: "thread_1",
|
|
119
|
+
})).toThrow("Unreachable case");
|
|
120
|
+
});
|
|
121
|
+
it("logs warning for unknown custom event names", () => {
|
|
122
|
+
const state = createTestStreamState("thread_1");
|
|
123
|
+
const event = {
|
|
124
|
+
type: core_1.EventType.CUSTOM,
|
|
125
|
+
name: "unknown.custom.event",
|
|
126
|
+
value: {},
|
|
127
|
+
};
|
|
128
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
129
|
+
type: "EVENT",
|
|
130
|
+
event,
|
|
131
|
+
threadId: "thread_1",
|
|
132
|
+
});
|
|
133
|
+
expect(result.threadMap.thread_1).toBe(state.threadMap.thread_1);
|
|
134
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("Unknown custom event name"));
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
describe("RUN_STARTED event", () => {
|
|
138
|
+
it("updates thread status to streaming", () => {
|
|
139
|
+
const state = createTestStreamState("thread_1");
|
|
140
|
+
const event = {
|
|
141
|
+
type: core_1.EventType.RUN_STARTED,
|
|
142
|
+
runId: "run_123",
|
|
143
|
+
threadId: "thread_1",
|
|
144
|
+
timestamp: 1704067200000,
|
|
145
|
+
};
|
|
146
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
147
|
+
type: "EVENT",
|
|
148
|
+
event,
|
|
149
|
+
threadId: "thread_1",
|
|
150
|
+
});
|
|
151
|
+
expect(result.threadMap.thread_1.thread.status).toBe("streaming");
|
|
152
|
+
expect(result.threadMap.thread_1.streaming.status).toBe("streaming");
|
|
153
|
+
expect(result.threadMap.thread_1.streaming.runId).toBe("run_123");
|
|
154
|
+
});
|
|
155
|
+
it("uses provided timestamp for startTime", () => {
|
|
156
|
+
const state = createTestStreamState("thread_1");
|
|
157
|
+
const event = {
|
|
158
|
+
type: core_1.EventType.RUN_STARTED,
|
|
159
|
+
runId: "run_123",
|
|
160
|
+
threadId: "thread_1",
|
|
161
|
+
timestamp: 1704067200000,
|
|
162
|
+
};
|
|
163
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
164
|
+
type: "EVENT",
|
|
165
|
+
event,
|
|
166
|
+
threadId: "thread_1",
|
|
167
|
+
});
|
|
168
|
+
expect(result.threadMap.thread_1.streaming.startTime).toBe(1704067200000);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe("RUN_FINISHED event", () => {
|
|
172
|
+
it("updates thread status to complete", () => {
|
|
173
|
+
const state = createTestStreamState("thread_1");
|
|
174
|
+
state.threadMap.thread_1.thread.status = "streaming";
|
|
175
|
+
state.threadMap.thread_1.streaming.status = "streaming";
|
|
176
|
+
const event = {
|
|
177
|
+
type: core_1.EventType.RUN_FINISHED,
|
|
178
|
+
runId: "run_123",
|
|
179
|
+
threadId: "thread_1",
|
|
180
|
+
};
|
|
181
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
182
|
+
type: "EVENT",
|
|
183
|
+
event,
|
|
184
|
+
threadId: "thread_1",
|
|
185
|
+
});
|
|
186
|
+
expect(result.threadMap.thread_1.thread.status).toBe("complete");
|
|
187
|
+
expect(result.threadMap.thread_1.streaming.status).toBe("complete");
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
describe("RUN_ERROR event", () => {
|
|
191
|
+
it("updates thread status to error with details", () => {
|
|
192
|
+
const state = createTestStreamState("thread_1");
|
|
193
|
+
const event = {
|
|
194
|
+
type: core_1.EventType.RUN_ERROR,
|
|
195
|
+
message: "Something went wrong",
|
|
196
|
+
code: "ERR_001",
|
|
197
|
+
};
|
|
198
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
199
|
+
type: "EVENT",
|
|
200
|
+
event,
|
|
201
|
+
threadId: "thread_1",
|
|
202
|
+
});
|
|
203
|
+
expect(result.threadMap.thread_1.thread.status).toBe("error");
|
|
204
|
+
expect(result.threadMap.thread_1.streaming.status).toBe("error");
|
|
205
|
+
expect(result.threadMap.thread_1.streaming.error).toEqual({
|
|
206
|
+
message: "Something went wrong",
|
|
207
|
+
code: "ERR_001",
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
describe("TEXT_MESSAGE_START event", () => {
|
|
212
|
+
it("creates new message in thread", () => {
|
|
213
|
+
const state = createTestStreamState("thread_1");
|
|
214
|
+
const event = {
|
|
215
|
+
type: core_1.EventType.TEXT_MESSAGE_START,
|
|
216
|
+
messageId: "msg_1",
|
|
217
|
+
role: "assistant",
|
|
218
|
+
};
|
|
219
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
220
|
+
type: "EVENT",
|
|
221
|
+
event,
|
|
222
|
+
threadId: "thread_1",
|
|
223
|
+
});
|
|
224
|
+
const messages = result.threadMap.thread_1.thread.messages;
|
|
225
|
+
expect(messages).toHaveLength(1);
|
|
226
|
+
expect(messages[0].id).toBe("msg_1");
|
|
227
|
+
expect(messages[0].role).toBe("assistant");
|
|
228
|
+
expect(messages[0].content).toEqual([]);
|
|
229
|
+
});
|
|
230
|
+
it("creates user message when role is user", () => {
|
|
231
|
+
const state = createTestStreamState("thread_1");
|
|
232
|
+
const event = {
|
|
233
|
+
type: core_1.EventType.TEXT_MESSAGE_START,
|
|
234
|
+
messageId: "msg_1",
|
|
235
|
+
role: "user",
|
|
236
|
+
};
|
|
237
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
238
|
+
type: "EVENT",
|
|
239
|
+
event,
|
|
240
|
+
threadId: "thread_1",
|
|
241
|
+
});
|
|
242
|
+
const messages = result.threadMap.thread_1.thread.messages;
|
|
243
|
+
expect(messages).toHaveLength(1);
|
|
244
|
+
expect(messages[0].role).toBe("user");
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
describe("TEXT_MESSAGE_CONTENT event", () => {
|
|
248
|
+
it("appends text content to message", () => {
|
|
249
|
+
const state = createTestStreamState("thread_1");
|
|
250
|
+
// Add a message first
|
|
251
|
+
state.threadMap.thread_1.thread.messages = [
|
|
252
|
+
{
|
|
253
|
+
id: "msg_1",
|
|
254
|
+
role: "assistant",
|
|
255
|
+
content: [],
|
|
256
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
const event = {
|
|
260
|
+
type: core_1.EventType.TEXT_MESSAGE_CONTENT,
|
|
261
|
+
messageId: "msg_1",
|
|
262
|
+
delta: "Hello ",
|
|
263
|
+
};
|
|
264
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
265
|
+
type: "EVENT",
|
|
266
|
+
event,
|
|
267
|
+
threadId: "thread_1",
|
|
268
|
+
});
|
|
269
|
+
const content = result.threadMap.thread_1.thread.messages[0].content;
|
|
270
|
+
expect(content).toHaveLength(1);
|
|
271
|
+
expect(content[0]).toEqual({ type: "text", text: "Hello " });
|
|
272
|
+
});
|
|
273
|
+
it("creates new text block after non-text content", () => {
|
|
274
|
+
const state = createTestStreamState("thread_1");
|
|
275
|
+
state.threadMap.thread_1.thread.messages = [
|
|
276
|
+
{
|
|
277
|
+
id: "msg_1",
|
|
278
|
+
role: "assistant",
|
|
279
|
+
content: [
|
|
280
|
+
{ type: "tool_use", id: "tool_1", name: "test", input: {} },
|
|
281
|
+
],
|
|
282
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
283
|
+
},
|
|
284
|
+
];
|
|
285
|
+
const event = {
|
|
286
|
+
type: core_1.EventType.TEXT_MESSAGE_CONTENT,
|
|
287
|
+
messageId: "msg_1",
|
|
288
|
+
delta: "After tool call",
|
|
289
|
+
};
|
|
290
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
291
|
+
type: "EVENT",
|
|
292
|
+
event,
|
|
293
|
+
threadId: "thread_1",
|
|
294
|
+
});
|
|
295
|
+
const content = result.threadMap.thread_1.thread.messages[0].content;
|
|
296
|
+
expect(content).toHaveLength(2);
|
|
297
|
+
expect(content[0]).toEqual({
|
|
298
|
+
type: "tool_use",
|
|
299
|
+
id: "tool_1",
|
|
300
|
+
name: "test",
|
|
301
|
+
input: {},
|
|
302
|
+
});
|
|
303
|
+
expect(content[1]).toEqual({ type: "text", text: "After tool call" });
|
|
304
|
+
});
|
|
305
|
+
it("accumulates text content deltas", () => {
|
|
306
|
+
const state = createTestStreamState("thread_1");
|
|
307
|
+
state.threadMap.thread_1.thread.messages = [
|
|
308
|
+
{
|
|
309
|
+
id: "msg_1",
|
|
310
|
+
role: "assistant",
|
|
311
|
+
content: [{ type: "text", text: "Hello " }],
|
|
312
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
313
|
+
},
|
|
314
|
+
];
|
|
315
|
+
const event = {
|
|
316
|
+
type: core_1.EventType.TEXT_MESSAGE_CONTENT,
|
|
317
|
+
messageId: "msg_1",
|
|
318
|
+
delta: "world!",
|
|
319
|
+
};
|
|
320
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
321
|
+
type: "EVENT",
|
|
322
|
+
event,
|
|
323
|
+
threadId: "thread_1",
|
|
324
|
+
});
|
|
325
|
+
const content = result.threadMap.thread_1.thread.messages[0].content;
|
|
326
|
+
expect(content).toHaveLength(1);
|
|
327
|
+
expect(content[0]).toEqual({ type: "text", text: "Hello world!" });
|
|
328
|
+
});
|
|
329
|
+
it("throws when message not found", () => {
|
|
330
|
+
const state = createTestStreamState("thread_1");
|
|
331
|
+
state.threadMap.thread_1.thread.messages = [
|
|
332
|
+
{
|
|
333
|
+
id: "msg_1",
|
|
334
|
+
role: "assistant",
|
|
335
|
+
content: [],
|
|
336
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
337
|
+
},
|
|
338
|
+
];
|
|
339
|
+
const event = {
|
|
340
|
+
type: core_1.EventType.TEXT_MESSAGE_CONTENT,
|
|
341
|
+
messageId: "unknown_msg",
|
|
342
|
+
delta: "Hello",
|
|
343
|
+
};
|
|
344
|
+
expect(() => {
|
|
345
|
+
(0, event_accumulator_1.streamReducer)(state, {
|
|
346
|
+
type: "EVENT",
|
|
347
|
+
event,
|
|
348
|
+
threadId: "thread_1",
|
|
349
|
+
});
|
|
350
|
+
}).toThrow("Message unknown_msg not found");
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
describe("TEXT_MESSAGE_END event", () => {
|
|
354
|
+
it("throws when messageId doesn't match active message", () => {
|
|
355
|
+
const state = createTestStreamState("thread_1");
|
|
356
|
+
// Set up an active message in streaming state
|
|
357
|
+
state.threadMap.thread_1.streaming.messageId = "msg_1";
|
|
358
|
+
state.threadMap.thread_1.thread.messages = [
|
|
359
|
+
{
|
|
360
|
+
id: "msg_1",
|
|
361
|
+
role: "assistant",
|
|
362
|
+
content: [{ type: "text", text: "Hello" }],
|
|
363
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
364
|
+
},
|
|
365
|
+
];
|
|
366
|
+
const event = {
|
|
367
|
+
type: core_1.EventType.TEXT_MESSAGE_END,
|
|
368
|
+
messageId: "wrong_msg_id",
|
|
369
|
+
};
|
|
370
|
+
expect(() => {
|
|
371
|
+
(0, event_accumulator_1.streamReducer)(state, {
|
|
372
|
+
type: "EVENT",
|
|
373
|
+
event,
|
|
374
|
+
threadId: "thread_1",
|
|
375
|
+
});
|
|
376
|
+
}).toThrow("TEXT_MESSAGE_END messageId mismatch");
|
|
377
|
+
});
|
|
378
|
+
it("clears active messageId on success", () => {
|
|
379
|
+
const state = createTestStreamState("thread_1");
|
|
380
|
+
state.threadMap.thread_1.streaming.messageId = "msg_1";
|
|
381
|
+
state.threadMap.thread_1.thread.messages = [
|
|
382
|
+
{
|
|
383
|
+
id: "msg_1",
|
|
384
|
+
role: "assistant",
|
|
385
|
+
content: [{ type: "text", text: "Hello" }],
|
|
386
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
387
|
+
},
|
|
388
|
+
];
|
|
389
|
+
const event = {
|
|
390
|
+
type: core_1.EventType.TEXT_MESSAGE_END,
|
|
391
|
+
messageId: "msg_1",
|
|
392
|
+
};
|
|
393
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
394
|
+
type: "EVENT",
|
|
395
|
+
event,
|
|
396
|
+
threadId: "thread_1",
|
|
397
|
+
});
|
|
398
|
+
expect(result.threadMap.thread_1.streaming.messageId).toBeUndefined();
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
describe("TOOL_CALL_START event", () => {
|
|
402
|
+
it("adds tool_use content block to message", () => {
|
|
403
|
+
const state = createTestStreamState("thread_1");
|
|
404
|
+
state.threadMap.thread_1.thread.messages = [
|
|
405
|
+
{
|
|
406
|
+
id: "msg_1",
|
|
407
|
+
role: "assistant",
|
|
408
|
+
content: [],
|
|
409
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
410
|
+
},
|
|
411
|
+
];
|
|
412
|
+
const event = {
|
|
413
|
+
type: core_1.EventType.TOOL_CALL_START,
|
|
414
|
+
toolCallId: "tool_1",
|
|
415
|
+
toolCallName: "get_weather",
|
|
416
|
+
parentMessageId: "msg_1",
|
|
417
|
+
};
|
|
418
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
419
|
+
type: "EVENT",
|
|
420
|
+
event,
|
|
421
|
+
threadId: "thread_1",
|
|
422
|
+
});
|
|
423
|
+
const content = result.threadMap.thread_1.thread.messages[0].content;
|
|
424
|
+
expect(content).toHaveLength(1);
|
|
425
|
+
expect(content[0]).toEqual({
|
|
426
|
+
type: "tool_use",
|
|
427
|
+
id: "tool_1",
|
|
428
|
+
name: "get_weather",
|
|
429
|
+
input: {},
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
describe("TOOL_CALL_START event", () => {
|
|
434
|
+
it("uses last message when no parentMessageId provided", () => {
|
|
435
|
+
const state = createTestStreamState("thread_1");
|
|
436
|
+
state.threadMap.thread_1.thread.messages = [
|
|
437
|
+
{
|
|
438
|
+
id: "msg_1",
|
|
439
|
+
role: "assistant",
|
|
440
|
+
content: [],
|
|
441
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
442
|
+
},
|
|
443
|
+
];
|
|
444
|
+
const event = {
|
|
445
|
+
type: core_1.EventType.TOOL_CALL_START,
|
|
446
|
+
toolCallId: "tool_1",
|
|
447
|
+
toolCallName: "get_weather",
|
|
448
|
+
// No parentMessageId - should use last message
|
|
449
|
+
};
|
|
450
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
451
|
+
type: "EVENT",
|
|
452
|
+
event,
|
|
453
|
+
threadId: "thread_1",
|
|
454
|
+
});
|
|
455
|
+
const content = result.threadMap.thread_1.thread.messages[0].content;
|
|
456
|
+
expect(content).toHaveLength(1);
|
|
457
|
+
expect(content[0]).toEqual({
|
|
458
|
+
type: "tool_use",
|
|
459
|
+
id: "tool_1",
|
|
460
|
+
name: "get_weather",
|
|
461
|
+
input: {},
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
it("throws when parentMessageId message not found", () => {
|
|
465
|
+
const state = createTestStreamState("thread_1");
|
|
466
|
+
state.threadMap.thread_1.thread.messages = [
|
|
467
|
+
{
|
|
468
|
+
id: "msg_1",
|
|
469
|
+
role: "assistant",
|
|
470
|
+
content: [],
|
|
471
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
472
|
+
},
|
|
473
|
+
];
|
|
474
|
+
const event = {
|
|
475
|
+
type: core_1.EventType.TOOL_CALL_START,
|
|
476
|
+
toolCallId: "tool_1",
|
|
477
|
+
toolCallName: "get_weather",
|
|
478
|
+
parentMessageId: "unknown_msg",
|
|
479
|
+
};
|
|
480
|
+
expect(() => {
|
|
481
|
+
(0, event_accumulator_1.streamReducer)(state, {
|
|
482
|
+
type: "EVENT",
|
|
483
|
+
event,
|
|
484
|
+
threadId: "thread_1",
|
|
485
|
+
});
|
|
486
|
+
}).toThrow("Message unknown_msg not found for TOOL_CALL_START event");
|
|
487
|
+
});
|
|
488
|
+
it("throws when no messages exist", () => {
|
|
489
|
+
const state = createTestStreamState("thread_1");
|
|
490
|
+
state.threadMap.thread_1.thread.messages = [];
|
|
491
|
+
const event = {
|
|
492
|
+
type: core_1.EventType.TOOL_CALL_START,
|
|
493
|
+
toolCallId: "tool_1",
|
|
494
|
+
toolCallName: "get_weather",
|
|
495
|
+
// No parentMessageId, no messages
|
|
496
|
+
};
|
|
497
|
+
expect(() => {
|
|
498
|
+
(0, event_accumulator_1.streamReducer)(state, {
|
|
499
|
+
type: "EVENT",
|
|
500
|
+
event,
|
|
501
|
+
threadId: "thread_1",
|
|
502
|
+
});
|
|
503
|
+
}).toThrow("No messages exist for TOOL_CALL_START event");
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
describe("TOOL_CALL_ARGS and TOOL_CALL_END events", () => {
|
|
507
|
+
it("handles TOOL_CALL_END with no accumulated args", () => {
|
|
508
|
+
const state = createTestStreamState("thread_1");
|
|
509
|
+
state.threadMap.thread_1.thread.messages = [
|
|
510
|
+
{
|
|
511
|
+
id: "msg_1",
|
|
512
|
+
role: "assistant",
|
|
513
|
+
content: [
|
|
514
|
+
{ type: "tool_use", id: "tool_1", name: "test", input: {} },
|
|
515
|
+
],
|
|
516
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
517
|
+
},
|
|
518
|
+
];
|
|
519
|
+
// Send TOOL_CALL_END without any TOOL_CALL_ARGS first
|
|
520
|
+
const endEvent = {
|
|
521
|
+
type: core_1.EventType.TOOL_CALL_END,
|
|
522
|
+
toolCallId: "tool_1",
|
|
523
|
+
};
|
|
524
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
525
|
+
type: "EVENT",
|
|
526
|
+
event: endEvent,
|
|
527
|
+
threadId: "thread_1",
|
|
528
|
+
});
|
|
529
|
+
// Should return unchanged state since no args were accumulated
|
|
530
|
+
expect(result.threadMap.thread_1).toBe(state.threadMap.thread_1);
|
|
531
|
+
});
|
|
532
|
+
it("accumulates and parses tool arguments", () => {
|
|
533
|
+
const state = createTestStreamState("thread_1");
|
|
534
|
+
state.threadMap.thread_1.thread.messages = [
|
|
535
|
+
{
|
|
536
|
+
id: "msg_1",
|
|
537
|
+
role: "assistant",
|
|
538
|
+
content: [
|
|
539
|
+
{ type: "tool_use", id: "tool_1", name: "test", input: {} },
|
|
540
|
+
],
|
|
541
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
542
|
+
},
|
|
543
|
+
];
|
|
544
|
+
// Send TOOL_CALL_ARGS
|
|
545
|
+
const argsEvent1 = {
|
|
546
|
+
type: core_1.EventType.TOOL_CALL_ARGS,
|
|
547
|
+
toolCallId: "tool_1",
|
|
548
|
+
delta: '{"city":',
|
|
549
|
+
};
|
|
550
|
+
let result = (0, event_accumulator_1.streamReducer)(state, {
|
|
551
|
+
type: "EVENT",
|
|
552
|
+
event: argsEvent1,
|
|
553
|
+
threadId: "thread_1",
|
|
554
|
+
});
|
|
555
|
+
const argsEvent2 = {
|
|
556
|
+
type: core_1.EventType.TOOL_CALL_ARGS,
|
|
557
|
+
toolCallId: "tool_1",
|
|
558
|
+
delta: '"NYC"}',
|
|
559
|
+
};
|
|
560
|
+
result = (0, event_accumulator_1.streamReducer)(result, {
|
|
561
|
+
type: "EVENT",
|
|
562
|
+
event: argsEvent2,
|
|
563
|
+
threadId: "thread_1",
|
|
564
|
+
});
|
|
565
|
+
// Send TOOL_CALL_END
|
|
566
|
+
const endEvent = {
|
|
567
|
+
type: core_1.EventType.TOOL_CALL_END,
|
|
568
|
+
toolCallId: "tool_1",
|
|
569
|
+
};
|
|
570
|
+
result = (0, event_accumulator_1.streamReducer)(result, {
|
|
571
|
+
type: "EVENT",
|
|
572
|
+
event: endEvent,
|
|
573
|
+
threadId: "thread_1",
|
|
574
|
+
});
|
|
575
|
+
const toolContent = asToolUseContent(result.threadMap.thread_1.thread.messages[0].content, 0);
|
|
576
|
+
expect(toolContent.input).toEqual({ city: "NYC" });
|
|
577
|
+
});
|
|
578
|
+
it("throws when tool arguments JSON is invalid", () => {
|
|
579
|
+
const state = createTestStreamState("thread_1");
|
|
580
|
+
state.threadMap.thread_1.thread.messages = [
|
|
581
|
+
{
|
|
582
|
+
id: "msg_1",
|
|
583
|
+
role: "assistant",
|
|
584
|
+
content: [
|
|
585
|
+
{ type: "tool_use", id: "tool_1", name: "test", input: {} },
|
|
586
|
+
],
|
|
587
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
588
|
+
},
|
|
589
|
+
];
|
|
590
|
+
// Send invalid JSON via TOOL_CALL_ARGS
|
|
591
|
+
const argsEvent = {
|
|
592
|
+
type: core_1.EventType.TOOL_CALL_ARGS,
|
|
593
|
+
toolCallId: "tool_1",
|
|
594
|
+
delta: "{invalid json",
|
|
595
|
+
};
|
|
596
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
597
|
+
type: "EVENT",
|
|
598
|
+
event: argsEvent,
|
|
599
|
+
threadId: "thread_1",
|
|
600
|
+
});
|
|
601
|
+
// Send TOOL_CALL_END - should throw when parsing
|
|
602
|
+
const endEvent = {
|
|
603
|
+
type: core_1.EventType.TOOL_CALL_END,
|
|
604
|
+
toolCallId: "tool_1",
|
|
605
|
+
};
|
|
606
|
+
expect(() => {
|
|
607
|
+
(0, event_accumulator_1.streamReducer)(result, {
|
|
608
|
+
type: "EVENT",
|
|
609
|
+
event: endEvent,
|
|
610
|
+
threadId: "thread_1",
|
|
611
|
+
});
|
|
612
|
+
}).toThrow("Failed to parse tool call arguments");
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
describe("custom component events", () => {
|
|
616
|
+
it("handles tambo.component.start event", () => {
|
|
617
|
+
const state = createTestStreamState("thread_1");
|
|
618
|
+
state.threadMap.thread_1.thread.messages = [
|
|
619
|
+
{
|
|
620
|
+
id: "msg_1",
|
|
621
|
+
role: "assistant",
|
|
622
|
+
content: [],
|
|
623
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
624
|
+
},
|
|
625
|
+
];
|
|
626
|
+
const event = {
|
|
627
|
+
type: core_1.EventType.CUSTOM,
|
|
628
|
+
name: "tambo.component.start",
|
|
629
|
+
value: {
|
|
630
|
+
messageId: "msg_1",
|
|
631
|
+
componentId: "comp_1",
|
|
632
|
+
componentName: "WeatherCard",
|
|
633
|
+
},
|
|
634
|
+
};
|
|
635
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
636
|
+
type: "EVENT",
|
|
637
|
+
event,
|
|
638
|
+
threadId: "thread_1",
|
|
639
|
+
});
|
|
640
|
+
const content = result.threadMap.thread_1.thread.messages[0].content;
|
|
641
|
+
expect(content).toHaveLength(1);
|
|
642
|
+
expect(content[0]).toEqual({
|
|
643
|
+
type: "component",
|
|
644
|
+
id: "comp_1",
|
|
645
|
+
name: "WeatherCard",
|
|
646
|
+
props: {},
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
it("handles tambo.component.props_delta event", () => {
|
|
650
|
+
const state = createTestStreamState("thread_1");
|
|
651
|
+
state.threadMap.thread_1.thread.messages = [
|
|
652
|
+
{
|
|
653
|
+
id: "msg_1",
|
|
654
|
+
role: "assistant",
|
|
655
|
+
content: [
|
|
656
|
+
{ type: "component", id: "comp_1", name: "Test", props: {} },
|
|
657
|
+
],
|
|
658
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
659
|
+
},
|
|
660
|
+
];
|
|
661
|
+
const event = {
|
|
662
|
+
type: core_1.EventType.CUSTOM,
|
|
663
|
+
name: "tambo.component.props_delta",
|
|
664
|
+
value: {
|
|
665
|
+
componentId: "comp_1",
|
|
666
|
+
operations: [{ op: "add", path: "/temperature", value: 72 }],
|
|
667
|
+
},
|
|
668
|
+
};
|
|
669
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
670
|
+
type: "EVENT",
|
|
671
|
+
event,
|
|
672
|
+
threadId: "thread_1",
|
|
673
|
+
});
|
|
674
|
+
const component = asComponentContent(result.threadMap.thread_1.thread.messages[0].content, 0);
|
|
675
|
+
expect(component.props).toEqual({ temperature: 72 });
|
|
676
|
+
});
|
|
677
|
+
it("throws when component not found for props_delta", () => {
|
|
678
|
+
const state = createTestStreamState("thread_1");
|
|
679
|
+
state.threadMap.thread_1.thread.messages = [
|
|
680
|
+
{
|
|
681
|
+
id: "msg_1",
|
|
682
|
+
role: "assistant",
|
|
683
|
+
content: [], // No component
|
|
684
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
685
|
+
},
|
|
686
|
+
];
|
|
687
|
+
const event = {
|
|
688
|
+
type: core_1.EventType.CUSTOM,
|
|
689
|
+
name: "tambo.component.props_delta",
|
|
690
|
+
value: {
|
|
691
|
+
componentId: "unknown_comp",
|
|
692
|
+
operations: [{ op: "add", path: "/value", value: 1 }],
|
|
693
|
+
},
|
|
694
|
+
};
|
|
695
|
+
expect(() => {
|
|
696
|
+
(0, event_accumulator_1.streamReducer)(state, {
|
|
697
|
+
type: "EVENT",
|
|
698
|
+
event,
|
|
699
|
+
threadId: "thread_1",
|
|
700
|
+
});
|
|
701
|
+
}).toThrow("component unknown_comp not found");
|
|
702
|
+
});
|
|
703
|
+
it("handles tambo.run.awaiting_input event", () => {
|
|
704
|
+
const state = createTestStreamState("thread_1");
|
|
705
|
+
state.threadMap.thread_1.thread.status = "streaming";
|
|
706
|
+
const event = {
|
|
707
|
+
type: core_1.EventType.CUSTOM,
|
|
708
|
+
name: "tambo.run.awaiting_input",
|
|
709
|
+
value: { pendingToolCallIds: ["tool_1", "tool_2"] },
|
|
710
|
+
};
|
|
711
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
712
|
+
type: "EVENT",
|
|
713
|
+
event,
|
|
714
|
+
threadId: "thread_1",
|
|
715
|
+
});
|
|
716
|
+
expect(result.threadMap.thread_1.thread.status).toBe("waiting");
|
|
717
|
+
expect(result.threadMap.thread_1.streaming.status).toBe("waiting");
|
|
718
|
+
});
|
|
719
|
+
it("handles tambo.component.state_delta event", () => {
|
|
720
|
+
const state = createTestStreamState("thread_1");
|
|
721
|
+
state.threadMap.thread_1.thread.messages = [
|
|
722
|
+
{
|
|
723
|
+
id: "msg_1",
|
|
724
|
+
role: "assistant",
|
|
725
|
+
content: [
|
|
726
|
+
{ type: "component", id: "comp_1", name: "Counter", props: {} },
|
|
727
|
+
],
|
|
728
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
729
|
+
},
|
|
730
|
+
];
|
|
731
|
+
const event = {
|
|
732
|
+
type: core_1.EventType.CUSTOM,
|
|
733
|
+
name: "tambo.component.state_delta",
|
|
734
|
+
value: {
|
|
735
|
+
componentId: "comp_1",
|
|
736
|
+
operations: [{ op: "add", path: "/count", value: 42 }],
|
|
737
|
+
},
|
|
738
|
+
};
|
|
739
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
740
|
+
type: "EVENT",
|
|
741
|
+
event,
|
|
742
|
+
threadId: "thread_1",
|
|
743
|
+
});
|
|
744
|
+
const component = asComponentContent(result.threadMap.thread_1.thread.messages[0].content, 0);
|
|
745
|
+
expect(component.state).toEqual({ count: 42 });
|
|
746
|
+
});
|
|
747
|
+
it("handles tambo.component.end event", () => {
|
|
748
|
+
const state = createTestStreamState("thread_1");
|
|
749
|
+
state.threadMap.thread_1.thread.messages = [
|
|
750
|
+
{
|
|
751
|
+
id: "msg_1",
|
|
752
|
+
role: "assistant",
|
|
753
|
+
content: [
|
|
754
|
+
{ type: "component", id: "comp_1", name: "Test", props: {} },
|
|
755
|
+
],
|
|
756
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
757
|
+
},
|
|
758
|
+
];
|
|
759
|
+
const event = {
|
|
760
|
+
type: core_1.EventType.CUSTOM,
|
|
761
|
+
name: "tambo.component.end",
|
|
762
|
+
value: { componentId: "comp_1" },
|
|
763
|
+
};
|
|
764
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
765
|
+
type: "EVENT",
|
|
766
|
+
event,
|
|
767
|
+
threadId: "thread_1",
|
|
768
|
+
});
|
|
769
|
+
// component.end doesn't change state currently, just returns unchanged
|
|
770
|
+
expect(result.threadMap.thread_1.thread.messages[0].content[0]).toEqual({
|
|
771
|
+
type: "component",
|
|
772
|
+
id: "comp_1",
|
|
773
|
+
name: "Test",
|
|
774
|
+
props: {},
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
describe("snapshot tests for complex state transitions", () => {
|
|
779
|
+
it("matches snapshot for full message flow", () => {
|
|
780
|
+
let state = createTestStreamState("thread_1");
|
|
781
|
+
// RUN_STARTED
|
|
782
|
+
const runStarted = {
|
|
783
|
+
type: core_1.EventType.RUN_STARTED,
|
|
784
|
+
runId: "run_1",
|
|
785
|
+
threadId: "thread_1",
|
|
786
|
+
timestamp: 1704067200000,
|
|
787
|
+
};
|
|
788
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
789
|
+
type: "EVENT",
|
|
790
|
+
event: runStarted,
|
|
791
|
+
threadId: "thread_1",
|
|
792
|
+
});
|
|
793
|
+
// TEXT_MESSAGE_START
|
|
794
|
+
const msgStart = {
|
|
795
|
+
type: core_1.EventType.TEXT_MESSAGE_START,
|
|
796
|
+
messageId: "msg_1",
|
|
797
|
+
role: "assistant",
|
|
798
|
+
};
|
|
799
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
800
|
+
type: "EVENT",
|
|
801
|
+
event: msgStart,
|
|
802
|
+
threadId: "thread_1",
|
|
803
|
+
});
|
|
804
|
+
// TEXT_MESSAGE_CONTENT
|
|
805
|
+
const msgContent = {
|
|
806
|
+
type: core_1.EventType.TEXT_MESSAGE_CONTENT,
|
|
807
|
+
messageId: "msg_1",
|
|
808
|
+
delta: "Hello, how can I help?",
|
|
809
|
+
};
|
|
810
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
811
|
+
type: "EVENT",
|
|
812
|
+
event: msgContent,
|
|
813
|
+
threadId: "thread_1",
|
|
814
|
+
});
|
|
815
|
+
// TEXT_MESSAGE_END
|
|
816
|
+
const msgEnd = {
|
|
817
|
+
type: core_1.EventType.TEXT_MESSAGE_END,
|
|
818
|
+
messageId: "msg_1",
|
|
819
|
+
};
|
|
820
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
821
|
+
type: "EVENT",
|
|
822
|
+
event: msgEnd,
|
|
823
|
+
threadId: "thread_1",
|
|
824
|
+
});
|
|
825
|
+
// RUN_FINISHED
|
|
826
|
+
const runFinished = {
|
|
827
|
+
type: core_1.EventType.RUN_FINISHED,
|
|
828
|
+
runId: "run_1",
|
|
829
|
+
threadId: "thread_1",
|
|
830
|
+
};
|
|
831
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
832
|
+
type: "EVENT",
|
|
833
|
+
event: runFinished,
|
|
834
|
+
threadId: "thread_1",
|
|
835
|
+
});
|
|
836
|
+
// Normalize timestamps for snapshot stability
|
|
837
|
+
const snapshot = {
|
|
838
|
+
...state,
|
|
839
|
+
threadMap: {
|
|
840
|
+
thread_1: {
|
|
841
|
+
...state.threadMap.thread_1,
|
|
842
|
+
thread: {
|
|
843
|
+
...state.threadMap.thread_1.thread,
|
|
844
|
+
messages: state.threadMap.thread_1.thread.messages.map((m) => ({
|
|
845
|
+
...m,
|
|
846
|
+
createdAt: "[TIMESTAMP]",
|
|
847
|
+
})),
|
|
848
|
+
createdAt: "[TIMESTAMP]",
|
|
849
|
+
updatedAt: "[TIMESTAMP]",
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
},
|
|
853
|
+
};
|
|
854
|
+
expect(snapshot).toMatchSnapshot();
|
|
855
|
+
});
|
|
856
|
+
it("matches snapshot for component streaming flow", () => {
|
|
857
|
+
let state = createTestStreamState("thread_1");
|
|
858
|
+
// Add a message
|
|
859
|
+
state.threadMap.thread_1.thread.messages = [
|
|
860
|
+
{
|
|
861
|
+
id: "msg_1",
|
|
862
|
+
role: "assistant",
|
|
863
|
+
content: [],
|
|
864
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
865
|
+
},
|
|
866
|
+
];
|
|
867
|
+
// COMPONENT_START
|
|
868
|
+
const compStart = {
|
|
869
|
+
type: core_1.EventType.CUSTOM,
|
|
870
|
+
name: "tambo.component.start",
|
|
871
|
+
value: {
|
|
872
|
+
messageId: "msg_1",
|
|
873
|
+
componentId: "comp_1",
|
|
874
|
+
componentName: "WeatherCard",
|
|
875
|
+
},
|
|
876
|
+
};
|
|
877
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
878
|
+
type: "EVENT",
|
|
879
|
+
event: compStart,
|
|
880
|
+
threadId: "thread_1",
|
|
881
|
+
});
|
|
882
|
+
// COMPONENT_PROPS_DELTA - add city
|
|
883
|
+
const propsDelta1 = {
|
|
884
|
+
type: core_1.EventType.CUSTOM,
|
|
885
|
+
name: "tambo.component.props_delta",
|
|
886
|
+
value: {
|
|
887
|
+
componentId: "comp_1",
|
|
888
|
+
operations: [{ op: "add", path: "/city", value: "New York" }],
|
|
889
|
+
},
|
|
890
|
+
};
|
|
891
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
892
|
+
type: "EVENT",
|
|
893
|
+
event: propsDelta1,
|
|
894
|
+
threadId: "thread_1",
|
|
895
|
+
});
|
|
896
|
+
// COMPONENT_PROPS_DELTA - add temperature
|
|
897
|
+
const propsDelta2 = {
|
|
898
|
+
type: core_1.EventType.CUSTOM,
|
|
899
|
+
name: "tambo.component.props_delta",
|
|
900
|
+
value: {
|
|
901
|
+
componentId: "comp_1",
|
|
902
|
+
operations: [{ op: "add", path: "/temperature", value: 72 }],
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
906
|
+
type: "EVENT",
|
|
907
|
+
event: propsDelta2,
|
|
908
|
+
threadId: "thread_1",
|
|
909
|
+
});
|
|
910
|
+
// COMPONENT_END
|
|
911
|
+
const compEnd = {
|
|
912
|
+
type: core_1.EventType.CUSTOM,
|
|
913
|
+
name: "tambo.component.end",
|
|
914
|
+
value: { componentId: "comp_1" },
|
|
915
|
+
};
|
|
916
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
917
|
+
type: "EVENT",
|
|
918
|
+
event: compEnd,
|
|
919
|
+
threadId: "thread_1",
|
|
920
|
+
});
|
|
921
|
+
// Normalize timestamps for snapshot stability
|
|
922
|
+
const snapshot = {
|
|
923
|
+
...state,
|
|
924
|
+
threadMap: {
|
|
925
|
+
thread_1: {
|
|
926
|
+
...state.threadMap.thread_1,
|
|
927
|
+
thread: {
|
|
928
|
+
...state.threadMap.thread_1.thread,
|
|
929
|
+
messages: state.threadMap.thread_1.thread.messages.map((m) => ({
|
|
930
|
+
...m,
|
|
931
|
+
createdAt: "[TIMESTAMP]",
|
|
932
|
+
})),
|
|
933
|
+
createdAt: "[TIMESTAMP]",
|
|
934
|
+
updatedAt: "[TIMESTAMP]",
|
|
935
|
+
},
|
|
936
|
+
},
|
|
937
|
+
},
|
|
938
|
+
};
|
|
939
|
+
expect(snapshot).toMatchSnapshot();
|
|
940
|
+
});
|
|
941
|
+
it("matches snapshot for tool call flow", () => {
|
|
942
|
+
let state = createTestStreamState("thread_1");
|
|
943
|
+
// Add a message with tool_use
|
|
944
|
+
state.threadMap.thread_1.thread.messages = [
|
|
945
|
+
{
|
|
946
|
+
id: "msg_1",
|
|
947
|
+
role: "assistant",
|
|
948
|
+
content: [
|
|
949
|
+
{ type: "tool_use", id: "tool_1", name: "get_weather", input: {} },
|
|
950
|
+
],
|
|
951
|
+
createdAt: "2024-01-01T00:00:00.000Z",
|
|
952
|
+
},
|
|
953
|
+
];
|
|
954
|
+
// TOOL_CALL_ARGS
|
|
955
|
+
const toolArgs = {
|
|
956
|
+
type: core_1.EventType.TOOL_CALL_ARGS,
|
|
957
|
+
toolCallId: "tool_1",
|
|
958
|
+
delta: '{"city":"San Francisco","units":"fahrenheit"}',
|
|
959
|
+
};
|
|
960
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
961
|
+
type: "EVENT",
|
|
962
|
+
event: toolArgs,
|
|
963
|
+
threadId: "thread_1",
|
|
964
|
+
});
|
|
965
|
+
// TOOL_CALL_END
|
|
966
|
+
const toolEnd = {
|
|
967
|
+
type: core_1.EventType.TOOL_CALL_END,
|
|
968
|
+
toolCallId: "tool_1",
|
|
969
|
+
};
|
|
970
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
971
|
+
type: "EVENT",
|
|
972
|
+
event: toolEnd,
|
|
973
|
+
threadId: "thread_1",
|
|
974
|
+
});
|
|
975
|
+
// TOOL_CALL_RESULT
|
|
976
|
+
const toolResult = {
|
|
977
|
+
type: core_1.EventType.TOOL_CALL_RESULT,
|
|
978
|
+
toolCallId: "tool_1",
|
|
979
|
+
messageId: "msg_1",
|
|
980
|
+
content: "Temperature: 65°F, Sunny",
|
|
981
|
+
role: "tool",
|
|
982
|
+
};
|
|
983
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
984
|
+
type: "EVENT",
|
|
985
|
+
event: toolResult,
|
|
986
|
+
threadId: "thread_1",
|
|
987
|
+
});
|
|
988
|
+
// Normalize timestamps for snapshot stability
|
|
989
|
+
const snapshot = {
|
|
990
|
+
...state,
|
|
991
|
+
threadMap: {
|
|
992
|
+
thread_1: {
|
|
993
|
+
...state.threadMap.thread_1,
|
|
994
|
+
thread: {
|
|
995
|
+
...state.threadMap.thread_1.thread,
|
|
996
|
+
messages: state.threadMap.thread_1.thread.messages.map((m) => ({
|
|
997
|
+
...m,
|
|
998
|
+
createdAt: "[TIMESTAMP]",
|
|
999
|
+
})),
|
|
1000
|
+
createdAt: "[TIMESTAMP]",
|
|
1001
|
+
updatedAt: "[TIMESTAMP]",
|
|
1002
|
+
},
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
};
|
|
1006
|
+
expect(snapshot).toMatchSnapshot();
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
//# sourceMappingURL=event-accumulator.test.js.map
|