@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
|
@@ -56,11 +56,11 @@ describe("createInitialThreadState", () => {
|
|
|
56
56
|
describe("createInitialState", () => {
|
|
57
57
|
it("creates initial state with placeholder thread", () => {
|
|
58
58
|
const state = (0, event_accumulator_1.createInitialState)();
|
|
59
|
-
expect(state.currentThreadId).toBe(
|
|
60
|
-
expect(state.threadMap.
|
|
61
|
-
expect(state.threadMap.
|
|
62
|
-
expect(state.threadMap.
|
|
63
|
-
expect(state.threadMap.
|
|
59
|
+
expect(state.currentThreadId).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
60
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID]).toBeDefined();
|
|
61
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.id).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
62
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toEqual([]);
|
|
63
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].streaming.status).toBe("idle");
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
describe("streamReducer", () => {
|
|
@@ -138,6 +138,92 @@ describe("streamReducer", () => {
|
|
|
138
138
|
expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining("Unknown custom event name"));
|
|
139
139
|
});
|
|
140
140
|
});
|
|
141
|
+
describe("START_NEW_THREAD action", () => {
|
|
142
|
+
it("creates a new thread and sets it as current", () => {
|
|
143
|
+
const state = (0, event_accumulator_1.createInitialState)();
|
|
144
|
+
const result = (0, event_accumulator_1.streamReducer)(state, {
|
|
145
|
+
type: "START_NEW_THREAD",
|
|
146
|
+
threadId: "new_thread",
|
|
147
|
+
});
|
|
148
|
+
expect(result.currentThreadId).toBe("new_thread");
|
|
149
|
+
expect(result.threadMap.new_thread).toBeDefined();
|
|
150
|
+
expect(result.threadMap.new_thread.thread.messages).toHaveLength(0);
|
|
151
|
+
});
|
|
152
|
+
it("overwrites existing placeholder thread on second call", () => {
|
|
153
|
+
// First call: start a new thread on the placeholder
|
|
154
|
+
let state = (0, event_accumulator_1.createInitialState)();
|
|
155
|
+
const initialThread = {
|
|
156
|
+
messages: [
|
|
157
|
+
{
|
|
158
|
+
id: "seed_1",
|
|
159
|
+
role: "user",
|
|
160
|
+
content: [{ type: "text", text: "Hello" }],
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
};
|
|
164
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
165
|
+
type: "START_NEW_THREAD",
|
|
166
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
167
|
+
initialThread,
|
|
168
|
+
});
|
|
169
|
+
expect(state.currentThreadId).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
170
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
171
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages[0].id).toBe("seed_1");
|
|
172
|
+
// Simulate a completed conversation: add more messages to the placeholder
|
|
173
|
+
const userMsgStart = {
|
|
174
|
+
type: core_1.EventType.TEXT_MESSAGE_START,
|
|
175
|
+
messageId: "extra_msg",
|
|
176
|
+
role: "user",
|
|
177
|
+
};
|
|
178
|
+
const userMsgContent = {
|
|
179
|
+
type: core_1.EventType.TEXT_MESSAGE_CONTENT,
|
|
180
|
+
messageId: "extra_msg",
|
|
181
|
+
delta: "Follow-up",
|
|
182
|
+
};
|
|
183
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
184
|
+
type: "EVENT",
|
|
185
|
+
event: userMsgStart,
|
|
186
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
187
|
+
});
|
|
188
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
189
|
+
type: "EVENT",
|
|
190
|
+
event: userMsgContent,
|
|
191
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
192
|
+
});
|
|
193
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(2);
|
|
194
|
+
// Second call: start a new thread again — should reset to fresh state
|
|
195
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
196
|
+
type: "START_NEW_THREAD",
|
|
197
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
198
|
+
initialThread,
|
|
199
|
+
});
|
|
200
|
+
// Should be reset to just the initial seed message, not the old messages
|
|
201
|
+
expect(state.currentThreadId).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
202
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
203
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages[0].id).toBe("seed_1");
|
|
204
|
+
});
|
|
205
|
+
it("overwrites existing placeholder even without initialThread", () => {
|
|
206
|
+
let state = (0, event_accumulator_1.createInitialState)();
|
|
207
|
+
// Add a message to the placeholder
|
|
208
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
209
|
+
type: "EVENT",
|
|
210
|
+
event: {
|
|
211
|
+
type: core_1.EventType.TEXT_MESSAGE_START,
|
|
212
|
+
messageId: "msg_1",
|
|
213
|
+
role: "user",
|
|
214
|
+
},
|
|
215
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
216
|
+
});
|
|
217
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
218
|
+
// START_NEW_THREAD without initialThread should still reset
|
|
219
|
+
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
220
|
+
type: "START_NEW_THREAD",
|
|
221
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
222
|
+
});
|
|
223
|
+
expect(state.currentThreadId).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
224
|
+
expect(state.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
141
227
|
describe("RUN_STARTED event", () => {
|
|
142
228
|
it("updates thread status to streaming", () => {
|
|
143
229
|
const state = createTestStreamState("thread_1");
|
|
@@ -203,14 +289,14 @@ describe("streamReducer", () => {
|
|
|
203
289
|
event: runStartedEvent,
|
|
204
290
|
threadId: realThreadId,
|
|
205
291
|
});
|
|
206
|
-
expect(result.threadMap.
|
|
207
|
-
expect(result.currentThreadId).toBe(
|
|
292
|
+
expect(result.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
293
|
+
expect(result.currentThreadId).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
208
294
|
});
|
|
209
295
|
it("migrates messages from placeholder thread to real thread", () => {
|
|
210
296
|
// Start with initial state (which has placeholder thread)
|
|
211
297
|
const state = (0, event_accumulator_1.createInitialState)();
|
|
212
298
|
// Verify placeholder thread exists
|
|
213
|
-
expect(state.currentThreadId).toBe(
|
|
299
|
+
expect(state.currentThreadId).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
214
300
|
// Add a user message to the placeholder thread
|
|
215
301
|
const userMsgStart = {
|
|
216
302
|
type: core_1.EventType.TEXT_MESSAGE_START,
|
|
@@ -229,22 +315,23 @@ describe("streamReducer", () => {
|
|
|
229
315
|
let stateWithUserMsg = (0, event_accumulator_1.streamReducer)(state, {
|
|
230
316
|
type: "EVENT",
|
|
231
317
|
event: userMsgStart,
|
|
232
|
-
threadId:
|
|
318
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
233
319
|
});
|
|
234
320
|
stateWithUserMsg = (0, event_accumulator_1.streamReducer)(stateWithUserMsg, {
|
|
235
321
|
type: "EVENT",
|
|
236
322
|
event: userMsgContent,
|
|
237
|
-
threadId:
|
|
323
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
238
324
|
});
|
|
239
325
|
stateWithUserMsg = (0, event_accumulator_1.streamReducer)(stateWithUserMsg, {
|
|
240
326
|
type: "EVENT",
|
|
241
327
|
event: userMsgEnd,
|
|
242
|
-
threadId:
|
|
328
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
243
329
|
});
|
|
244
330
|
// Verify placeholder thread has the message
|
|
245
|
-
expect(stateWithUserMsg.currentThreadId).toBe(
|
|
246
|
-
expect(stateWithUserMsg.threadMap.
|
|
247
|
-
expect(stateWithUserMsg.threadMap.
|
|
331
|
+
expect(stateWithUserMsg.currentThreadId).toBe(event_accumulator_1.PLACEHOLDER_THREAD_ID);
|
|
332
|
+
expect(stateWithUserMsg.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(1);
|
|
333
|
+
expect(stateWithUserMsg.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages[0]
|
|
334
|
+
.content[0]).toEqual({
|
|
248
335
|
type: "text",
|
|
249
336
|
text: "Hello",
|
|
250
337
|
});
|
|
@@ -262,8 +349,8 @@ describe("streamReducer", () => {
|
|
|
262
349
|
threadId: realThreadId,
|
|
263
350
|
});
|
|
264
351
|
// Placeholder thread should be reset to empty (not removed)
|
|
265
|
-
expect(finalState.threadMap.
|
|
266
|
-
expect(finalState.threadMap.
|
|
352
|
+
expect(finalState.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID]).toBeDefined();
|
|
353
|
+
expect(finalState.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
267
354
|
// Real thread should have the migrated user message
|
|
268
355
|
expect(finalState.threadMap[realThreadId]).toBeDefined();
|
|
269
356
|
expect(finalState.threadMap[realThreadId].thread.messages).toHaveLength(1);
|
|
@@ -304,17 +391,17 @@ describe("streamReducer", () => {
|
|
|
304
391
|
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
305
392
|
type: "EVENT",
|
|
306
393
|
event: userMsgStart,
|
|
307
|
-
threadId:
|
|
394
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
308
395
|
});
|
|
309
396
|
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
310
397
|
type: "EVENT",
|
|
311
398
|
event: userMsgContent,
|
|
312
|
-
threadId:
|
|
399
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
313
400
|
});
|
|
314
401
|
state = (0, event_accumulator_1.streamReducer)(state, {
|
|
315
402
|
type: "EVENT",
|
|
316
403
|
event: userMsgEnd,
|
|
317
|
-
threadId:
|
|
404
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
318
405
|
});
|
|
319
406
|
const realThreadId = "thread_real_123";
|
|
320
407
|
const runStartedEvent = {
|
|
@@ -328,7 +415,7 @@ describe("streamReducer", () => {
|
|
|
328
415
|
event: runStartedEvent,
|
|
329
416
|
threadId: realThreadId,
|
|
330
417
|
});
|
|
331
|
-
expect(result.threadMap.
|
|
418
|
+
expect(result.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
332
419
|
expect(result.threadMap[realThreadId].thread.messages).toHaveLength(1);
|
|
333
420
|
expect(result.currentThreadId).toBe("thread_1");
|
|
334
421
|
});
|
|
@@ -353,17 +440,17 @@ describe("streamReducer", () => {
|
|
|
353
440
|
let stateWithUserMsg = (0, event_accumulator_1.streamReducer)(state, {
|
|
354
441
|
type: "EVENT",
|
|
355
442
|
event: userMsgStart,
|
|
356
|
-
threadId:
|
|
443
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
357
444
|
});
|
|
358
445
|
stateWithUserMsg = (0, event_accumulator_1.streamReducer)(stateWithUserMsg, {
|
|
359
446
|
type: "EVENT",
|
|
360
447
|
event: userMsgContent,
|
|
361
|
-
threadId:
|
|
448
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
362
449
|
});
|
|
363
450
|
stateWithUserMsg = (0, event_accumulator_1.streamReducer)(stateWithUserMsg, {
|
|
364
451
|
type: "EVENT",
|
|
365
452
|
event: userMsgEnd,
|
|
366
|
-
threadId:
|
|
453
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
367
454
|
});
|
|
368
455
|
const realThreadId = "thread_real_123";
|
|
369
456
|
const runStartedEvent = {
|
|
@@ -375,9 +462,9 @@ describe("streamReducer", () => {
|
|
|
375
462
|
const result = (0, event_accumulator_1.streamReducer)(stateWithUserMsg, {
|
|
376
463
|
type: "EVENT",
|
|
377
464
|
event: runStartedEvent,
|
|
378
|
-
threadId:
|
|
465
|
+
threadId: event_accumulator_1.PLACEHOLDER_THREAD_ID,
|
|
379
466
|
});
|
|
380
|
-
expect(result.threadMap.
|
|
467
|
+
expect(result.threadMap[event_accumulator_1.PLACEHOLDER_THREAD_ID].thread.messages).toHaveLength(0);
|
|
381
468
|
expect(result.threadMap[realThreadId].thread.messages).toHaveLength(1);
|
|
382
469
|
expect(result.currentThreadId).toBe(realThreadId);
|
|
383
470
|
});
|