@tambo-ai/react 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/v1/hooks/use-tambo-v1-thread-input.test.js +103 -0
- package/dist/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
- package/dist/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
- package/dist/v1/providers/tambo-v1-thread-input-provider.js +31 -15
- package/dist/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
- package/dist/v1/utils/event-accumulator.d.ts.map +1 -1
- package/dist/v1/utils/event-accumulator.js +4 -9
- package/dist/v1/utils/event-accumulator.js.map +1 -1
- package/dist/v1/utils/event-accumulator.test.js +112 -25
- package/dist/v1/utils/event-accumulator.test.js.map +1 -1
- package/esm/v1/hooks/use-tambo-v1-thread-input.test.js +103 -0
- package/esm/v1/hooks/use-tambo-v1-thread-input.test.js.map +1 -1
- package/esm/v1/providers/tambo-v1-thread-input-provider.d.ts.map +1 -1
- package/esm/v1/providers/tambo-v1-thread-input-provider.js +31 -15
- package/esm/v1/providers/tambo-v1-thread-input-provider.js.map +1 -1
- package/esm/v1/utils/event-accumulator.d.ts.map +1 -1
- package/esm/v1/utils/event-accumulator.js +4 -9
- package/esm/v1/utils/event-accumulator.js.map +1 -1
- package/esm/v1/utils/event-accumulator.test.js +113 -26
- package/esm/v1/utils/event-accumulator.test.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventType, } from "@ag-ui/core";
|
|
2
|
-
import { createInitialState, createInitialThreadState, streamReducer, } from "./event-accumulator.js";
|
|
2
|
+
import { PLACEHOLDER_THREAD_ID, createInitialState, createInitialThreadState, streamReducer, } from "./event-accumulator.js";
|
|
3
3
|
/**
|
|
4
4
|
* Helper to extract a ToolUseContent from a message content array.
|
|
5
5
|
* @param content - Content array from a message
|
|
@@ -54,11 +54,11 @@ describe("createInitialThreadState", () => {
|
|
|
54
54
|
describe("createInitialState", () => {
|
|
55
55
|
it("creates initial state with placeholder thread", () => {
|
|
56
56
|
const state = createInitialState();
|
|
57
|
-
expect(state.currentThreadId).toBe(
|
|
58
|
-
expect(state.threadMap
|
|
59
|
-
expect(state.threadMap.
|
|
60
|
-
expect(state.threadMap.
|
|
61
|
-
expect(state.threadMap.
|
|
57
|
+
expect(state.currentThreadId).toBe(PLACEHOLDER_THREAD_ID);
|
|
58
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID]).toBeDefined();
|
|
59
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.id).toBe(PLACEHOLDER_THREAD_ID);
|
|
60
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toEqual([]);
|
|
61
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].streaming.status).toBe("idle");
|
|
62
62
|
});
|
|
63
63
|
});
|
|
64
64
|
describe("streamReducer", () => {
|
|
@@ -136,6 +136,92 @@ describe("streamReducer", () => {
|
|
|
136
136
|
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("Unknown custom event name"));
|
|
137
137
|
});
|
|
138
138
|
});
|
|
139
|
+
describe("START_NEW_THREAD action", () => {
|
|
140
|
+
it("creates a new thread and sets it as current", () => {
|
|
141
|
+
const state = createInitialState();
|
|
142
|
+
const result = streamReducer(state, {
|
|
143
|
+
type: "START_NEW_THREAD",
|
|
144
|
+
threadId: "new_thread",
|
|
145
|
+
});
|
|
146
|
+
expect(result.currentThreadId).toBe("new_thread");
|
|
147
|
+
expect(result.threadMap.new_thread).toBeDefined();
|
|
148
|
+
expect(result.threadMap.new_thread.thread.messages).toHaveLength(0);
|
|
149
|
+
});
|
|
150
|
+
it("overwrites existing placeholder thread on second call", () => {
|
|
151
|
+
// First call: start a new thread on the placeholder
|
|
152
|
+
let state = createInitialState();
|
|
153
|
+
const initialThread = {
|
|
154
|
+
messages: [
|
|
155
|
+
{
|
|
156
|
+
id: "seed_1",
|
|
157
|
+
role: "user",
|
|
158
|
+
content: [{ type: "text", text: "Hello" }],
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
};
|
|
162
|
+
state = streamReducer(state, {
|
|
163
|
+
type: "START_NEW_THREAD",
|
|
164
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
165
|
+
initialThread,
|
|
166
|
+
});
|
|
167
|
+
expect(state.currentThreadId).toBe(PLACEHOLDER_THREAD_ID);
|
|
168
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
169
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages[0].id).toBe("seed_1");
|
|
170
|
+
// Simulate a completed conversation: add more messages to the placeholder
|
|
171
|
+
const userMsgStart = {
|
|
172
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
173
|
+
messageId: "extra_msg",
|
|
174
|
+
role: "user",
|
|
175
|
+
};
|
|
176
|
+
const userMsgContent = {
|
|
177
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
178
|
+
messageId: "extra_msg",
|
|
179
|
+
delta: "Follow-up",
|
|
180
|
+
};
|
|
181
|
+
state = streamReducer(state, {
|
|
182
|
+
type: "EVENT",
|
|
183
|
+
event: userMsgStart,
|
|
184
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
185
|
+
});
|
|
186
|
+
state = streamReducer(state, {
|
|
187
|
+
type: "EVENT",
|
|
188
|
+
event: userMsgContent,
|
|
189
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
190
|
+
});
|
|
191
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(2);
|
|
192
|
+
// Second call: start a new thread again — should reset to fresh state
|
|
193
|
+
state = streamReducer(state, {
|
|
194
|
+
type: "START_NEW_THREAD",
|
|
195
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
196
|
+
initialThread,
|
|
197
|
+
});
|
|
198
|
+
// Should be reset to just the initial seed message, not the old messages
|
|
199
|
+
expect(state.currentThreadId).toBe(PLACEHOLDER_THREAD_ID);
|
|
200
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
201
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages[0].id).toBe("seed_1");
|
|
202
|
+
});
|
|
203
|
+
it("overwrites existing placeholder even without initialThread", () => {
|
|
204
|
+
let state = createInitialState();
|
|
205
|
+
// Add a message to the placeholder
|
|
206
|
+
state = streamReducer(state, {
|
|
207
|
+
type: "EVENT",
|
|
208
|
+
event: {
|
|
209
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
210
|
+
messageId: "msg_1",
|
|
211
|
+
role: "user",
|
|
212
|
+
},
|
|
213
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
214
|
+
});
|
|
215
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
216
|
+
// START_NEW_THREAD without initialThread should still reset
|
|
217
|
+
state = streamReducer(state, {
|
|
218
|
+
type: "START_NEW_THREAD",
|
|
219
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
220
|
+
});
|
|
221
|
+
expect(state.currentThreadId).toBe(PLACEHOLDER_THREAD_ID);
|
|
222
|
+
expect(state.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
139
225
|
describe("RUN_STARTED event", () => {
|
|
140
226
|
it("updates thread status to streaming", () => {
|
|
141
227
|
const state = createTestStreamState("thread_1");
|
|
@@ -201,14 +287,14 @@ describe("streamReducer", () => {
|
|
|
201
287
|
event: runStartedEvent,
|
|
202
288
|
threadId: realThreadId,
|
|
203
289
|
});
|
|
204
|
-
expect(result.threadMap.
|
|
205
|
-
expect(result.currentThreadId).toBe(
|
|
290
|
+
expect(result.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
291
|
+
expect(result.currentThreadId).toBe(PLACEHOLDER_THREAD_ID);
|
|
206
292
|
});
|
|
207
293
|
it("migrates messages from placeholder thread to real thread", () => {
|
|
208
294
|
// Start with initial state (which has placeholder thread)
|
|
209
295
|
const state = createInitialState();
|
|
210
296
|
// Verify placeholder thread exists
|
|
211
|
-
expect(state.currentThreadId).toBe(
|
|
297
|
+
expect(state.currentThreadId).toBe(PLACEHOLDER_THREAD_ID);
|
|
212
298
|
// Add a user message to the placeholder thread
|
|
213
299
|
const userMsgStart = {
|
|
214
300
|
type: EventType.TEXT_MESSAGE_START,
|
|
@@ -227,22 +313,23 @@ describe("streamReducer", () => {
|
|
|
227
313
|
let stateWithUserMsg = streamReducer(state, {
|
|
228
314
|
type: "EVENT",
|
|
229
315
|
event: userMsgStart,
|
|
230
|
-
threadId:
|
|
316
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
231
317
|
});
|
|
232
318
|
stateWithUserMsg = streamReducer(stateWithUserMsg, {
|
|
233
319
|
type: "EVENT",
|
|
234
320
|
event: userMsgContent,
|
|
235
|
-
threadId:
|
|
321
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
236
322
|
});
|
|
237
323
|
stateWithUserMsg = streamReducer(stateWithUserMsg, {
|
|
238
324
|
type: "EVENT",
|
|
239
325
|
event: userMsgEnd,
|
|
240
|
-
threadId:
|
|
326
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
241
327
|
});
|
|
242
328
|
// Verify placeholder thread has the message
|
|
243
|
-
expect(stateWithUserMsg.currentThreadId).toBe(
|
|
244
|
-
expect(stateWithUserMsg.threadMap.
|
|
245
|
-
expect(stateWithUserMsg.threadMap.
|
|
329
|
+
expect(stateWithUserMsg.currentThreadId).toBe(PLACEHOLDER_THREAD_ID);
|
|
330
|
+
expect(stateWithUserMsg.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
331
|
+
expect(stateWithUserMsg.threadMap[PLACEHOLDER_THREAD_ID].thread.messages[0]
|
|
332
|
+
.content[0]).toEqual({
|
|
246
333
|
type: "text",
|
|
247
334
|
text: "Hello",
|
|
248
335
|
});
|
|
@@ -260,8 +347,8 @@ describe("streamReducer", () => {
|
|
|
260
347
|
threadId: realThreadId,
|
|
261
348
|
});
|
|
262
349
|
// Placeholder thread should be reset to empty (not removed)
|
|
263
|
-
expect(finalState.threadMap
|
|
264
|
-
expect(finalState.threadMap.
|
|
350
|
+
expect(finalState.threadMap[PLACEHOLDER_THREAD_ID]).toBeDefined();
|
|
351
|
+
expect(finalState.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
265
352
|
// Real thread should have the migrated user message
|
|
266
353
|
expect(finalState.threadMap[realThreadId]).toBeDefined();
|
|
267
354
|
expect(finalState.threadMap[realThreadId].thread.messages).toHaveLength(1);
|
|
@@ -302,17 +389,17 @@ describe("streamReducer", () => {
|
|
|
302
389
|
state = streamReducer(state, {
|
|
303
390
|
type: "EVENT",
|
|
304
391
|
event: userMsgStart,
|
|
305
|
-
threadId:
|
|
392
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
306
393
|
});
|
|
307
394
|
state = streamReducer(state, {
|
|
308
395
|
type: "EVENT",
|
|
309
396
|
event: userMsgContent,
|
|
310
|
-
threadId:
|
|
397
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
311
398
|
});
|
|
312
399
|
state = streamReducer(state, {
|
|
313
400
|
type: "EVENT",
|
|
314
401
|
event: userMsgEnd,
|
|
315
|
-
threadId:
|
|
402
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
316
403
|
});
|
|
317
404
|
const realThreadId = "thread_real_123";
|
|
318
405
|
const runStartedEvent = {
|
|
@@ -326,7 +413,7 @@ describe("streamReducer", () => {
|
|
|
326
413
|
event: runStartedEvent,
|
|
327
414
|
threadId: realThreadId,
|
|
328
415
|
});
|
|
329
|
-
expect(result.threadMap.
|
|
416
|
+
expect(result.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
330
417
|
expect(result.threadMap[realThreadId].thread.messages).toHaveLength(1);
|
|
331
418
|
expect(result.currentThreadId).toBe("thread_1");
|
|
332
419
|
});
|
|
@@ -351,17 +438,17 @@ describe("streamReducer", () => {
|
|
|
351
438
|
let stateWithUserMsg = streamReducer(state, {
|
|
352
439
|
type: "EVENT",
|
|
353
440
|
event: userMsgStart,
|
|
354
|
-
threadId:
|
|
441
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
355
442
|
});
|
|
356
443
|
stateWithUserMsg = streamReducer(stateWithUserMsg, {
|
|
357
444
|
type: "EVENT",
|
|
358
445
|
event: userMsgContent,
|
|
359
|
-
threadId:
|
|
446
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
360
447
|
});
|
|
361
448
|
stateWithUserMsg = streamReducer(stateWithUserMsg, {
|
|
362
449
|
type: "EVENT",
|
|
363
450
|
event: userMsgEnd,
|
|
364
|
-
threadId:
|
|
451
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
365
452
|
});
|
|
366
453
|
const realThreadId = "thread_real_123";
|
|
367
454
|
const runStartedEvent = {
|
|
@@ -373,9 +460,9 @@ describe("streamReducer", () => {
|
|
|
373
460
|
const result = streamReducer(stateWithUserMsg, {
|
|
374
461
|
type: "EVENT",
|
|
375
462
|
event: runStartedEvent,
|
|
376
|
-
threadId:
|
|
463
|
+
threadId: PLACEHOLDER_THREAD_ID,
|
|
377
464
|
});
|
|
378
|
-
expect(result.threadMap.
|
|
465
|
+
expect(result.threadMap[PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
379
466
|
expect(result.threadMap[realThreadId].thread.messages).toHaveLength(1);
|
|
380
467
|
expect(result.currentThreadId).toBe(realThreadId);
|
|
381
468
|
});
|