@aleph-alpha/chat-kit 1.3.1 → 1.5.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/{CkInput.vue_vue_type_script_setup_true_lang-CyGLItQZ.js → CkInput.vue_vue_type_script_setup_true_lang-C5C09u0z.js} +9 -8
- package/dist/adapters/index.d.ts +18 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +4 -0
- package/dist/adapters/responses-api.d.ts +180 -0
- package/dist/adapters/responses-api.d.ts.map +1 -0
- package/dist/adapters/responses-api.js +153 -0
- package/dist/components/base/CkPromptActionButton/CkPromptActionButton.vue.d.ts +1 -1
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +11 -3
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts.map +1 -1
- package/dist/components/composed/CkConversation/CkConversation.vue.d.ts +5 -1
- package/dist/components/composed/CkConversation/CkConversation.vue.d.ts.map +1 -1
- package/dist/components/index.js +1 -1
- package/dist/composables/fileUpload/index.d.ts +7 -0
- package/dist/composables/fileUpload/index.d.ts.map +1 -0
- package/dist/composables/fileUpload/types.d.ts +295 -0
- package/dist/composables/fileUpload/types.d.ts.map +1 -0
- package/dist/composables/fileUpload/useFileUpload.d.ts +4 -0
- package/dist/composables/fileUpload/useFileUpload.d.ts.map +1 -0
- package/dist/composables/fileUpload/useFileUploadWrapper.d.ts +3 -0
- package/dist/composables/fileUpload/useFileUploadWrapper.d.ts.map +1 -0
- package/dist/composables/index.d.ts +2 -1
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/index.js +5 -2
- package/dist/composables/useConversations.d.ts +12 -0
- package/dist/composables/useConversations.d.ts.map +1 -1
- package/dist/generateId-BHf0W4Rq.js +6 -0
- package/dist/handlers/index.js +1 -1
- package/dist/handlers/useMessageHandler.d.ts +2 -2
- package/dist/handlers/useMessageHandler.d.ts.map +1 -1
- package/dist/index-BbTgIoYa.js +1557 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -8
- package/dist/services/index.d.ts +0 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -37
- package/dist/services/types.d.ts +2 -2
- package/dist/services/types.d.ts.map +1 -1
- package/dist/types.d.ts +47 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/{useMessageHandler-BmBdu2EW.js → useMessageHandler-ZRC-gIOJ.js} +5 -5
- package/package.json +10 -2
- package/src/adapters/index.ts +17 -0
- package/src/adapters/responses-api.spec.ts +365 -0
- package/src/adapters/responses-api.ts +444 -0
- package/src/components/composed/CkConversation/CkConversation.spec.ts +171 -25
- package/src/components/composed/CkConversation/CkConversation.stories.ts +266 -46
- package/src/components/composed/CkConversation/CkConversation.vue +19 -7
- package/src/composables/fileUpload/index.spec.ts +213 -0
- package/src/composables/fileUpload/index.ts +83 -0
- package/src/composables/fileUpload/types.ts +409 -0
- package/src/composables/fileUpload/useFileUpload.ts +1242 -0
- package/src/composables/fileUpload/useFileUploadWrapper.ts +382 -0
- package/src/composables/index.ts +2 -1
- package/src/composables/useConversations.spec.ts +118 -0
- package/src/composables/useConversations.ts +32 -0
- package/src/handlers/useChatKitLabels.spec.ts +16 -2
- package/src/handlers/useMessageHandler.spec.ts +112 -44
- package/src/handlers/useMessageHandler.ts +16 -2
- package/src/helpers/conversationTree.spec.ts +46 -19
- package/src/index.ts +11 -2
- package/src/services/index.ts +0 -2
- package/src/services/types.ts +2 -2
- package/src/types.ts +55 -2
- package/dist/ChatService-LogwJNxh.js +0 -9
- package/dist/services/ChatService.d.ts +0 -42
- package/dist/services/ChatService.d.ts.map +0 -1
- package/dist/services/MockChatService.d.ts +0 -10
- package/dist/services/MockChatService.d.ts.map +0 -1
- package/dist/useConversationSessions-ulFhtpQr.js +0 -243
- package/src/services/ChatService.ts +0 -60
- package/src/services/MockChatService.ts +0 -47
|
@@ -1,6 +1,29 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ConversationTree,
|
|
3
|
+
MessageContent,
|
|
4
|
+
MessageTextContent,
|
|
5
|
+
TreeMessage,
|
|
6
|
+
} from '../types';
|
|
2
7
|
import { useMessageHandler } from './useMessageHandler';
|
|
3
8
|
|
|
9
|
+
let textIdCounter = 0;
|
|
10
|
+
function text(value: string): MessageTextContent {
|
|
11
|
+
textIdCounter += 1;
|
|
12
|
+
return {
|
|
13
|
+
type: 'text',
|
|
14
|
+
id: `txt-${textIdCounter}`,
|
|
15
|
+
text: value,
|
|
16
|
+
status: 'completed',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getText(content: MessageContent[]): string {
|
|
21
|
+
return content
|
|
22
|
+
.filter((c): c is MessageTextContent => c.type === 'text')
|
|
23
|
+
.map((c) => c.text)
|
|
24
|
+
.join('');
|
|
25
|
+
}
|
|
26
|
+
|
|
4
27
|
function makeTreeMessage(
|
|
5
28
|
overrides: Partial<TreeMessage> & { id: string },
|
|
6
29
|
): TreeMessage {
|
|
@@ -9,7 +32,7 @@ function makeTreeMessage(
|
|
|
9
32
|
childrenIds: [],
|
|
10
33
|
activeChildIndex: 0,
|
|
11
34
|
role: 'user',
|
|
12
|
-
content:
|
|
35
|
+
content: [],
|
|
13
36
|
status: 'completed',
|
|
14
37
|
createdAt: new Date('2025-01-01'),
|
|
15
38
|
...overrides,
|
|
@@ -43,11 +66,26 @@ describe('useMessageHandler', () => {
|
|
|
43
66
|
const msg = handler.commitUserMessage();
|
|
44
67
|
|
|
45
68
|
expect(msg.role).toBe('user');
|
|
46
|
-
expect(msg.content).toBe('Hello');
|
|
69
|
+
expect(getText(msg.content)).toBe('Hello');
|
|
47
70
|
expect(handler.messages.value).toHaveLength(1);
|
|
48
71
|
expect(handler.messages.value[0].id).toBe(msg.id);
|
|
49
72
|
});
|
|
50
73
|
|
|
74
|
+
it('produces a single text content item with status completed', () => {
|
|
75
|
+
const handler = useMessageHandler();
|
|
76
|
+
handler.setCurrentUserMessage('Hello');
|
|
77
|
+
const msg = handler.commitUserMessage();
|
|
78
|
+
|
|
79
|
+
expect(msg.content).toHaveLength(1);
|
|
80
|
+
const part = msg.content[0];
|
|
81
|
+
expect(part.type).toBe('text');
|
|
82
|
+
if (part.type === 'text') {
|
|
83
|
+
expect(part.text).toBe('Hello');
|
|
84
|
+
expect(part.status).toBe('completed');
|
|
85
|
+
expect(part.id).toBeTruthy();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
51
89
|
it('appends consecutive messages in order', () => {
|
|
52
90
|
const handler = useMessageHandler();
|
|
53
91
|
|
|
@@ -90,25 +128,25 @@ describe('useMessageHandler', () => {
|
|
|
90
128
|
|
|
91
129
|
handler.updateMessage({
|
|
92
130
|
id: msg.id,
|
|
93
|
-
content: 'Updated',
|
|
131
|
+
content: [text('Updated')],
|
|
94
132
|
role: 'user',
|
|
95
133
|
status: 'completed',
|
|
96
134
|
});
|
|
97
135
|
|
|
98
|
-
expect(handler.messages.value[0].content).toBe('Updated');
|
|
136
|
+
expect(getText(handler.messages.value[0].content)).toBe('Updated');
|
|
99
137
|
});
|
|
100
138
|
|
|
101
139
|
it('creates a new node when id does not exist (upsert)', () => {
|
|
102
140
|
const handler = useMessageHandler();
|
|
103
141
|
handler.updateMessage({
|
|
104
142
|
id: 'new-1',
|
|
105
|
-
content: 'Upserted',
|
|
143
|
+
content: [text('Upserted')],
|
|
106
144
|
role: 'assistant',
|
|
107
145
|
status: 'completed',
|
|
108
146
|
});
|
|
109
147
|
|
|
110
148
|
expect(handler.messages.value).toHaveLength(1);
|
|
111
|
-
expect(handler.messages.value[0].content).toBe('Upserted');
|
|
149
|
+
expect(getText(handler.messages.value[0].content)).toBe('Upserted');
|
|
112
150
|
expect(handler.messages.value[0].id).toBe('new-1');
|
|
113
151
|
});
|
|
114
152
|
|
|
@@ -119,14 +157,40 @@ describe('useMessageHandler', () => {
|
|
|
119
157
|
|
|
120
158
|
handler.updateMessage({
|
|
121
159
|
id: 'assistant-1',
|
|
122
|
-
content: 'Reply',
|
|
160
|
+
content: [text('Reply')],
|
|
123
161
|
role: 'assistant',
|
|
124
162
|
status: 'completed',
|
|
125
163
|
});
|
|
126
164
|
|
|
127
165
|
expect(handler.messages.value).toHaveLength(2);
|
|
128
166
|
expect(handler.messages.value[1].id).toBe('assistant-1');
|
|
129
|
-
expect(handler.messages.value[1].content).toBe('Reply');
|
|
167
|
+
expect(getText(handler.messages.value[1].content)).toBe('Reply');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('preserves multi-part content (text + tool + reasoning)', () => {
|
|
171
|
+
const handler = useMessageHandler();
|
|
172
|
+
const content: MessageContent[] = [
|
|
173
|
+
{ type: 'reasoning', id: 'r1', status: 'completed' },
|
|
174
|
+
{ type: 'tool', id: 't1', name: 'search' },
|
|
175
|
+
{
|
|
176
|
+
type: 'text',
|
|
177
|
+
id: 'txt-multi',
|
|
178
|
+
text: 'Final answer',
|
|
179
|
+
status: 'completed',
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
handler.updateMessage({
|
|
183
|
+
id: 'a-multi',
|
|
184
|
+
content,
|
|
185
|
+
role: 'assistant',
|
|
186
|
+
status: 'completed',
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const stored = handler.messages.value[0];
|
|
190
|
+
expect(stored.content).toHaveLength(3);
|
|
191
|
+
expect(stored.content[0].type).toBe('reasoning');
|
|
192
|
+
expect(stored.content[1].type).toBe('tool');
|
|
193
|
+
expect(stored.content[2].type).toBe('text');
|
|
130
194
|
});
|
|
131
195
|
|
|
132
196
|
it('updates a mid-tree node in place without truncating the path', () => {
|
|
@@ -134,23 +198,23 @@ describe('useMessageHandler', () => {
|
|
|
134
198
|
handler.setTree(
|
|
135
199
|
makeTree(
|
|
136
200
|
{
|
|
137
|
-
u1: makeTreeMessage({ id: 'u1', content: 'First' }),
|
|
201
|
+
u1: makeTreeMessage({ id: 'u1', content: [text('First')] }),
|
|
138
202
|
a1: makeTreeMessage({
|
|
139
203
|
id: 'a1',
|
|
140
204
|
parentId: 'u1',
|
|
141
205
|
role: 'assistant',
|
|
142
|
-
content: 'Reply 1',
|
|
206
|
+
content: [text('Reply 1')],
|
|
143
207
|
}),
|
|
144
208
|
u2: makeTreeMessage({
|
|
145
209
|
id: 'u2',
|
|
146
210
|
parentId: 'a1',
|
|
147
|
-
content: 'Second',
|
|
211
|
+
content: [text('Second')],
|
|
148
212
|
}),
|
|
149
213
|
a2: makeTreeMessage({
|
|
150
214
|
id: 'a2',
|
|
151
215
|
parentId: 'u2',
|
|
152
216
|
role: 'assistant',
|
|
153
|
-
content: 'Reply 2',
|
|
217
|
+
content: [text('Reply 2')],
|
|
154
218
|
}),
|
|
155
219
|
},
|
|
156
220
|
'u1',
|
|
@@ -159,13 +223,13 @@ describe('useMessageHandler', () => {
|
|
|
159
223
|
|
|
160
224
|
handler.updateMessage({
|
|
161
225
|
id: 'a1',
|
|
162
|
-
content: 'Regenerated',
|
|
226
|
+
content: [text('Regenerated')],
|
|
163
227
|
role: 'assistant',
|
|
164
228
|
status: 'sending',
|
|
165
229
|
});
|
|
166
230
|
|
|
167
231
|
expect(handler.messages.value).toHaveLength(4);
|
|
168
|
-
expect(handler.messages.value[1].content).toBe('Regenerated');
|
|
232
|
+
expect(getText(handler.messages.value[1].content)).toBe('Regenerated');
|
|
169
233
|
});
|
|
170
234
|
|
|
171
235
|
it('does not truncate when updating the last message', () => {
|
|
@@ -173,12 +237,12 @@ describe('useMessageHandler', () => {
|
|
|
173
237
|
handler.setTree(
|
|
174
238
|
makeTree(
|
|
175
239
|
{
|
|
176
|
-
u1: makeTreeMessage({ id: 'u1', content: 'Hello' }),
|
|
240
|
+
u1: makeTreeMessage({ id: 'u1', content: [text('Hello')] }),
|
|
177
241
|
a1: makeTreeMessage({
|
|
178
242
|
id: 'a1',
|
|
179
243
|
parentId: 'u1',
|
|
180
244
|
role: 'assistant',
|
|
181
|
-
content: 'Hi',
|
|
245
|
+
content: [text('Hi')],
|
|
182
246
|
}),
|
|
183
247
|
},
|
|
184
248
|
'u1',
|
|
@@ -187,13 +251,13 @@ describe('useMessageHandler', () => {
|
|
|
187
251
|
|
|
188
252
|
handler.updateMessage({
|
|
189
253
|
id: 'a1',
|
|
190
|
-
content: 'Streaming...',
|
|
254
|
+
content: [text('Streaming...')],
|
|
191
255
|
role: 'assistant',
|
|
192
256
|
status: 'sending',
|
|
193
257
|
});
|
|
194
258
|
|
|
195
259
|
expect(handler.messages.value).toHaveLength(2);
|
|
196
|
-
expect(handler.messages.value[1].content).toBe('Streaming...');
|
|
260
|
+
expect(getText(handler.messages.value[1].content)).toBe('Streaming...');
|
|
197
261
|
});
|
|
198
262
|
});
|
|
199
263
|
|
|
@@ -206,7 +270,7 @@ describe('useMessageHandler', () => {
|
|
|
206
270
|
const found = handler.getMessageById(msg.id);
|
|
207
271
|
expect(found).toBeDefined();
|
|
208
272
|
expect(found?.id).toBe(msg.id);
|
|
209
|
-
expect(found?.content).toBe('Hello');
|
|
273
|
+
expect(getText(found?.content ?? [])).toBe('Hello');
|
|
210
274
|
expect(found?.role).toBe('user');
|
|
211
275
|
});
|
|
212
276
|
|
|
@@ -240,7 +304,7 @@ describe('useMessageHandler', () => {
|
|
|
240
304
|
|
|
241
305
|
const renamed = handler.getMessageById('renamed');
|
|
242
306
|
expect(renamed?.role).toBe(role);
|
|
243
|
-
expect(renamed?.content).
|
|
307
|
+
expect(renamed?.content).toEqual(content);
|
|
244
308
|
expect(renamed?.status).toBe(status);
|
|
245
309
|
expect(renamed?.createdAt).toEqual(createdAt);
|
|
246
310
|
});
|
|
@@ -250,14 +314,18 @@ describe('useMessageHandler', () => {
|
|
|
250
314
|
handler.setTree(
|
|
251
315
|
makeTree(
|
|
252
316
|
{
|
|
253
|
-
a: makeTreeMessage({ id: 'a', content: 'A' }),
|
|
317
|
+
a: makeTreeMessage({ id: 'a', content: [text('A')] }),
|
|
254
318
|
b: makeTreeMessage({
|
|
255
319
|
id: 'b',
|
|
256
320
|
parentId: 'a',
|
|
257
321
|
role: 'assistant',
|
|
258
|
-
content: 'B',
|
|
322
|
+
content: [text('B')],
|
|
323
|
+
}),
|
|
324
|
+
c: makeTreeMessage({
|
|
325
|
+
id: 'c',
|
|
326
|
+
parentId: 'b',
|
|
327
|
+
content: [text('C')],
|
|
259
328
|
}),
|
|
260
|
-
c: makeTreeMessage({ id: 'c', parentId: 'b', content: 'C' }),
|
|
261
329
|
},
|
|
262
330
|
'a',
|
|
263
331
|
),
|
|
@@ -291,7 +359,7 @@ describe('useMessageHandler', () => {
|
|
|
291
359
|
handler.commitUserMessage();
|
|
292
360
|
handler.updateMessage({
|
|
293
361
|
id: 'a1',
|
|
294
|
-
content: 'Reply',
|
|
362
|
+
content: [text('Reply')],
|
|
295
363
|
role: 'assistant',
|
|
296
364
|
status: 'completed',
|
|
297
365
|
});
|
|
@@ -323,7 +391,7 @@ describe('useMessageHandler', () => {
|
|
|
323
391
|
|
|
324
392
|
handler.updateMessage({
|
|
325
393
|
id: 'a1',
|
|
326
|
-
content: 'Reply 1',
|
|
394
|
+
content: [text('Reply 1')],
|
|
327
395
|
role: 'assistant',
|
|
328
396
|
status: 'completed',
|
|
329
397
|
});
|
|
@@ -333,7 +401,7 @@ describe('useMessageHandler', () => {
|
|
|
333
401
|
|
|
334
402
|
handler.updateMessage({
|
|
335
403
|
id: 'a2',
|
|
336
|
-
content: 'Reply 2',
|
|
404
|
+
content: [text('Reply 2')],
|
|
337
405
|
role: 'assistant',
|
|
338
406
|
status: 'completed',
|
|
339
407
|
});
|
|
@@ -341,7 +409,7 @@ describe('useMessageHandler', () => {
|
|
|
341
409
|
const roles = handler.messages.value.map((m) => m.role);
|
|
342
410
|
expect(roles).toEqual(['user', 'assistant', 'user', 'assistant']);
|
|
343
411
|
|
|
344
|
-
const contents = handler.messages.value.map((m) => m.content);
|
|
412
|
+
const contents = handler.messages.value.map((m) => getText(m.content));
|
|
345
413
|
expect(contents).toEqual(['One', 'Reply 1', 'Two', 'Reply 2']);
|
|
346
414
|
});
|
|
347
415
|
|
|
@@ -355,7 +423,7 @@ describe('useMessageHandler', () => {
|
|
|
355
423
|
|
|
356
424
|
handler.updateMessage({
|
|
357
425
|
id: 'a1',
|
|
358
|
-
content: 'Hi',
|
|
426
|
+
content: [text('Hi')],
|
|
359
427
|
role: 'assistant',
|
|
360
428
|
status: 'completed',
|
|
361
429
|
});
|
|
@@ -369,12 +437,12 @@ describe('useMessageHandler', () => {
|
|
|
369
437
|
handler.setTree(
|
|
370
438
|
makeTree(
|
|
371
439
|
{
|
|
372
|
-
u1: makeTreeMessage({ id: 'u1', content: 'Hello' }),
|
|
440
|
+
u1: makeTreeMessage({ id: 'u1', content: [text('Hello')] }),
|
|
373
441
|
a1: makeTreeMessage({
|
|
374
442
|
id: 'a1',
|
|
375
443
|
parentId: 'u1',
|
|
376
444
|
role: 'assistant',
|
|
377
|
-
content: 'Hi',
|
|
445
|
+
content: [text('Hi')],
|
|
378
446
|
}),
|
|
379
447
|
},
|
|
380
448
|
'u1',
|
|
@@ -395,13 +463,13 @@ describe('useMessageHandler', () => {
|
|
|
395
463
|
id: 'a',
|
|
396
464
|
parentId: 'r',
|
|
397
465
|
role: 'assistant',
|
|
398
|
-
content: 'A',
|
|
466
|
+
content: [text('A')],
|
|
399
467
|
}),
|
|
400
468
|
b: makeTreeMessage({
|
|
401
469
|
id: 'b',
|
|
402
470
|
parentId: 'r',
|
|
403
471
|
role: 'assistant',
|
|
404
|
-
content: 'B',
|
|
472
|
+
content: [text('B')],
|
|
405
473
|
}),
|
|
406
474
|
},
|
|
407
475
|
'r',
|
|
@@ -419,23 +487,23 @@ describe('useMessageHandler', () => {
|
|
|
419
487
|
handler.setTree(
|
|
420
488
|
makeTree(
|
|
421
489
|
{
|
|
422
|
-
u1: makeTreeMessage({ id: 'u1', content: 'Hello' }),
|
|
490
|
+
u1: makeTreeMessage({ id: 'u1', content: [text('Hello')] }),
|
|
423
491
|
a1: makeTreeMessage({
|
|
424
492
|
id: 'a1',
|
|
425
493
|
parentId: 'u1',
|
|
426
494
|
role: 'assistant',
|
|
427
|
-
content: 'Hi',
|
|
495
|
+
content: [text('Hi')],
|
|
428
496
|
}),
|
|
429
497
|
a2: makeTreeMessage({
|
|
430
498
|
id: 'a2',
|
|
431
499
|
parentId: 'u1',
|
|
432
500
|
role: 'assistant',
|
|
433
|
-
content: 'Hey',
|
|
501
|
+
content: [text('Hey')],
|
|
434
502
|
}),
|
|
435
503
|
u2: makeTreeMessage({
|
|
436
504
|
id: 'u2',
|
|
437
505
|
parentId: 'a2',
|
|
438
|
-
content: 'Follow-up',
|
|
506
|
+
content: [text('Follow-up')],
|
|
439
507
|
}),
|
|
440
508
|
},
|
|
441
509
|
'u1',
|
|
@@ -459,18 +527,18 @@ describe('useMessageHandler', () => {
|
|
|
459
527
|
handler.setTree(
|
|
460
528
|
makeTree(
|
|
461
529
|
{
|
|
462
|
-
u1: makeTreeMessage({ id: 'u1', content: 'Hello' }),
|
|
530
|
+
u1: makeTreeMessage({ id: 'u1', content: [text('Hello')] }),
|
|
463
531
|
a1: makeTreeMessage({
|
|
464
532
|
id: 'a1',
|
|
465
533
|
parentId: 'u1',
|
|
466
534
|
role: 'assistant',
|
|
467
|
-
content: 'Hi',
|
|
535
|
+
content: [text('Hi')],
|
|
468
536
|
}),
|
|
469
537
|
a2: makeTreeMessage({
|
|
470
538
|
id: 'a2',
|
|
471
539
|
parentId: 'u1',
|
|
472
540
|
role: 'assistant',
|
|
473
|
-
content: 'Hey',
|
|
541
|
+
content: [text('Hey')],
|
|
474
542
|
}),
|
|
475
543
|
},
|
|
476
544
|
'u1',
|
|
@@ -502,23 +570,23 @@ describe('useMessageHandler', () => {
|
|
|
502
570
|
handler.setTree(
|
|
503
571
|
makeTree(
|
|
504
572
|
{
|
|
505
|
-
u1: makeTreeMessage({ id: 'u1', content: 'Hello' }),
|
|
573
|
+
u1: makeTreeMessage({ id: 'u1', content: [text('Hello')] }),
|
|
506
574
|
a1: makeTreeMessage({
|
|
507
575
|
id: 'a1',
|
|
508
576
|
parentId: 'u1',
|
|
509
577
|
role: 'assistant',
|
|
510
|
-
content: 'Hi',
|
|
578
|
+
content: [text('Hi')],
|
|
511
579
|
}),
|
|
512
580
|
a2: makeTreeMessage({
|
|
513
581
|
id: 'a2',
|
|
514
582
|
parentId: 'u1',
|
|
515
583
|
role: 'assistant',
|
|
516
|
-
content: 'Hey',
|
|
584
|
+
content: [text('Hey')],
|
|
517
585
|
}),
|
|
518
586
|
u2: makeTreeMessage({
|
|
519
587
|
id: 'u2',
|
|
520
588
|
parentId: 'a2',
|
|
521
|
-
content: 'Follow-up',
|
|
589
|
+
content: [text('Follow-up')],
|
|
522
590
|
}),
|
|
523
591
|
},
|
|
524
592
|
'u1',
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import type {
|
|
11
11
|
BaseMessage,
|
|
12
12
|
ConversationTree,
|
|
13
|
+
MessageContent,
|
|
13
14
|
MessageRole,
|
|
14
15
|
MessageStatus,
|
|
15
16
|
TreeMessage,
|
|
@@ -40,13 +41,17 @@ export const useMessageHandler = () => {
|
|
|
40
41
|
const text = currentUserMessage.value.trim();
|
|
41
42
|
if (!text) throw new Error('No message');
|
|
42
43
|
|
|
44
|
+
const content: MessageContent[] = [
|
|
45
|
+
{ type: 'text', id: generateId(), text, status: 'completed' },
|
|
46
|
+
];
|
|
47
|
+
|
|
43
48
|
const node: TreeMessage = {
|
|
44
49
|
id,
|
|
45
50
|
parentId: currentLeafId.value,
|
|
46
51
|
childrenIds: [],
|
|
47
52
|
activeChildIndex: 0,
|
|
48
53
|
role: 'user',
|
|
49
|
-
content
|
|
54
|
+
content,
|
|
50
55
|
status: 'completed',
|
|
51
56
|
createdAt: new Date(),
|
|
52
57
|
};
|
|
@@ -65,7 +70,7 @@ export const useMessageHandler = () => {
|
|
|
65
70
|
|
|
66
71
|
function updateMessage(message: {
|
|
67
72
|
id: string;
|
|
68
|
-
content:
|
|
73
|
+
content: MessageContent[];
|
|
69
74
|
role: MessageRole;
|
|
70
75
|
status: MessageStatus;
|
|
71
76
|
}) {
|
|
@@ -112,6 +117,15 @@ export const useMessageHandler = () => {
|
|
|
112
117
|
Object.assign(tree, createEmptyTree());
|
|
113
118
|
}
|
|
114
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Replace the entire tree. Pair with a format adapter (e.g.
|
|
122
|
+
* `responsesToTree` from `@aleph-alpha/chat-kit/adapters/responses-api`)
|
|
123
|
+
* when hydrating from a server transcript:
|
|
124
|
+
*
|
|
125
|
+
* ```ts
|
|
126
|
+
* setTree(responsesToTree(await fetchResponses(id)));
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
115
129
|
function setTree(incoming: ConversationTree) {
|
|
116
130
|
tree.nodes = incoming.nodes;
|
|
117
131
|
tree.rootId = incoming.rootId;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ConversationTree,
|
|
3
|
+
MessageContent,
|
|
4
|
+
MessageTextContent,
|
|
5
|
+
TreeMessage,
|
|
6
|
+
} from '../types';
|
|
2
7
|
import {
|
|
3
8
|
addNode,
|
|
4
9
|
createEmptyTree,
|
|
@@ -8,6 +13,17 @@ import {
|
|
|
8
13
|
updateNodeId,
|
|
9
14
|
} from './conversationTree';
|
|
10
15
|
|
|
16
|
+
let textIdCounter = 0;
|
|
17
|
+
function text(value: string): MessageTextContent {
|
|
18
|
+
textIdCounter += 1;
|
|
19
|
+
return {
|
|
20
|
+
type: 'text',
|
|
21
|
+
id: `txt-${textIdCounter}`,
|
|
22
|
+
text: value,
|
|
23
|
+
status: 'completed',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
11
27
|
function makeTreeMessage(
|
|
12
28
|
overrides: Partial<TreeMessage> & { id: string },
|
|
13
29
|
): TreeMessage {
|
|
@@ -16,7 +32,7 @@ function makeTreeMessage(
|
|
|
16
32
|
childrenIds: [],
|
|
17
33
|
activeChildIndex: 0,
|
|
18
34
|
role: 'user',
|
|
19
|
-
content:
|
|
35
|
+
content: [],
|
|
20
36
|
status: 'completed',
|
|
21
37
|
createdAt: new Date('2025-01-01'),
|
|
22
38
|
...overrides,
|
|
@@ -42,6 +58,13 @@ function makeTree(
|
|
|
42
58
|
return { nodes, rootId };
|
|
43
59
|
}
|
|
44
60
|
|
|
61
|
+
function getText(content: MessageContent[]): string {
|
|
62
|
+
return content
|
|
63
|
+
.filter((c): c is MessageTextContent => c.type === 'text')
|
|
64
|
+
.map((c) => c.text)
|
|
65
|
+
.join('');
|
|
66
|
+
}
|
|
67
|
+
|
|
45
68
|
/**
|
|
46
69
|
* Builds a simple branching tree for tests:
|
|
47
70
|
*
|
|
@@ -58,42 +81,42 @@ function makeTree(
|
|
|
58
81
|
function makeBranchingTree(): ConversationTree {
|
|
59
82
|
return makeTree(
|
|
60
83
|
{
|
|
61
|
-
u1: makeTreeMessage({ id: 'u1', role: 'user', content: 'Hello' }),
|
|
84
|
+
u1: makeTreeMessage({ id: 'u1', role: 'user', content: [text('Hello')] }),
|
|
62
85
|
a1: makeTreeMessage({
|
|
63
86
|
id: 'a1',
|
|
64
87
|
parentId: 'u1',
|
|
65
88
|
role: 'assistant',
|
|
66
|
-
content: 'Hi',
|
|
89
|
+
content: [text('Hi')],
|
|
67
90
|
}),
|
|
68
91
|
u2: makeTreeMessage({
|
|
69
92
|
id: 'u2',
|
|
70
93
|
parentId: 'a1',
|
|
71
94
|
role: 'user',
|
|
72
|
-
content: 'Question',
|
|
95
|
+
content: [text('Question')],
|
|
73
96
|
}),
|
|
74
97
|
a2: makeTreeMessage({
|
|
75
98
|
id: 'a2',
|
|
76
99
|
parentId: 'u2',
|
|
77
100
|
role: 'assistant',
|
|
78
|
-
content: 'Answer 1',
|
|
101
|
+
content: [text('Answer 1')],
|
|
79
102
|
}),
|
|
80
103
|
a3: makeTreeMessage({
|
|
81
104
|
id: 'a3',
|
|
82
105
|
parentId: 'u2',
|
|
83
106
|
role: 'assistant',
|
|
84
|
-
content: 'Answer 2',
|
|
107
|
+
content: [text('Answer 2')],
|
|
85
108
|
}),
|
|
86
109
|
a1b: makeTreeMessage({
|
|
87
110
|
id: 'a1b',
|
|
88
111
|
parentId: 'u1',
|
|
89
112
|
role: 'assistant',
|
|
90
|
-
content: 'Alt hi',
|
|
113
|
+
content: [text('Alt hi')],
|
|
91
114
|
}),
|
|
92
115
|
u3: makeTreeMessage({
|
|
93
116
|
id: 'u3',
|
|
94
117
|
parentId: 'a1b',
|
|
95
118
|
role: 'user',
|
|
96
|
-
content: 'Follow-up',
|
|
119
|
+
content: [text('Follow-up')],
|
|
97
120
|
}),
|
|
98
121
|
},
|
|
99
122
|
'u1',
|
|
@@ -166,7 +189,7 @@ describe('getLinearPath', () => {
|
|
|
166
189
|
|
|
167
190
|
const u1Msg = path[0];
|
|
168
191
|
expect(u1Msg.role).toBe('user');
|
|
169
|
-
expect(u1Msg.content).toBe('Hello');
|
|
192
|
+
expect(getText(u1Msg.content)).toBe('Hello');
|
|
170
193
|
expect(u1Msg.status).toBe('completed');
|
|
171
194
|
expect(u1Msg.createdAt).toEqual(new Date('2025-01-01'));
|
|
172
195
|
});
|
|
@@ -175,7 +198,7 @@ describe('getLinearPath', () => {
|
|
|
175
198
|
describe('addNode', () => {
|
|
176
199
|
it('adds the first node as root', () => {
|
|
177
200
|
const tree = createEmptyTree();
|
|
178
|
-
const node = makeTreeMessage({ id: 'u1', content: 'Hello' });
|
|
201
|
+
const node = makeTreeMessage({ id: 'u1', content: [text('Hello')] });
|
|
179
202
|
|
|
180
203
|
addNode(tree, node);
|
|
181
204
|
|
|
@@ -248,8 +271,8 @@ describe('addNode', () => {
|
|
|
248
271
|
describe('updateNode', () => {
|
|
249
272
|
it('updates content of an existing node', () => {
|
|
250
273
|
const tree = makeBranchingTree();
|
|
251
|
-
updateNode(tree, 'a1', { content: 'Updated hi' });
|
|
252
|
-
expect(tree.nodes.a1.content).toBe('Updated hi');
|
|
274
|
+
updateNode(tree, 'a1', { content: [text('Updated hi')] });
|
|
275
|
+
expect(getText(tree.nodes.a1.content)).toBe('Updated hi');
|
|
253
276
|
});
|
|
254
277
|
|
|
255
278
|
it('updates status of an existing node', () => {
|
|
@@ -261,7 +284,7 @@ describe('updateNode', () => {
|
|
|
261
284
|
it('is a no-op for nonexistent id', () => {
|
|
262
285
|
const tree = makeBranchingTree();
|
|
263
286
|
const nodeCount = Object.keys(tree.nodes).length;
|
|
264
|
-
updateNode(tree, 'nonexistent', { content: 'x' });
|
|
287
|
+
updateNode(tree, 'nonexistent', { content: [text('x')] });
|
|
265
288
|
expect(Object.keys(tree.nodes).length).toBe(nodeCount);
|
|
266
289
|
});
|
|
267
290
|
});
|
|
@@ -366,19 +389,23 @@ describe('integration: full branching scenario', () => {
|
|
|
366
389
|
it('simulates send → regenerate → navigate workflow', () => {
|
|
367
390
|
const tree = createEmptyTree();
|
|
368
391
|
|
|
369
|
-
addNode(tree, makeTreeMessage({ id: 'u1', content: 'Hello' }));
|
|
392
|
+
addNode(tree, makeTreeMessage({ id: 'u1', content: [text('Hello')] }));
|
|
370
393
|
addNode(
|
|
371
394
|
tree,
|
|
372
395
|
makeTreeMessage({
|
|
373
396
|
id: 'a1',
|
|
374
397
|
parentId: 'u1',
|
|
375
398
|
role: 'assistant',
|
|
376
|
-
content: 'Hi there',
|
|
399
|
+
content: [text('Hi there')],
|
|
377
400
|
}),
|
|
378
401
|
);
|
|
379
402
|
addNode(
|
|
380
403
|
tree,
|
|
381
|
-
makeTreeMessage({
|
|
404
|
+
makeTreeMessage({
|
|
405
|
+
id: 'u2',
|
|
406
|
+
parentId: 'a1',
|
|
407
|
+
content: [text('Tell me more')],
|
|
408
|
+
}),
|
|
382
409
|
);
|
|
383
410
|
addNode(
|
|
384
411
|
tree,
|
|
@@ -386,7 +413,7 @@ describe('integration: full branching scenario', () => {
|
|
|
386
413
|
id: 'a2',
|
|
387
414
|
parentId: 'u2',
|
|
388
415
|
role: 'assistant',
|
|
389
|
-
content: 'Sure thing',
|
|
416
|
+
content: [text('Sure thing')],
|
|
390
417
|
}),
|
|
391
418
|
);
|
|
392
419
|
|
|
@@ -399,7 +426,7 @@ describe('integration: full branching scenario', () => {
|
|
|
399
426
|
id: 'a3',
|
|
400
427
|
parentId: 'u2',
|
|
401
428
|
role: 'assistant',
|
|
402
|
-
content: 'Certainly!',
|
|
429
|
+
content: [text('Certainly!')],
|
|
403
430
|
}),
|
|
404
431
|
);
|
|
405
432
|
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// Services
|
|
2
2
|
export {
|
|
3
|
-
ChatService,
|
|
4
|
-
type IChatService,
|
|
5
3
|
type ChatConfig,
|
|
6
4
|
type ChatMessage,
|
|
7
5
|
type ChatResponse,
|
|
@@ -12,6 +10,12 @@ export {
|
|
|
12
10
|
type StreamEvent,
|
|
13
11
|
} from './services';
|
|
14
12
|
|
|
13
|
+
// Adapters' wire-level types (e.g. Responses-API) are intentionally NOT
|
|
14
|
+
// re-exported at the top level — import them from
|
|
15
|
+
// `@aleph-alpha/chat-kit/adapters/responses-api` (or the aggregate
|
|
16
|
+
// `@aleph-alpha/chat-kit/adapters`) to keep the format you depend on
|
|
17
|
+
// explicit.
|
|
18
|
+
|
|
15
19
|
// Handlers
|
|
16
20
|
export {
|
|
17
21
|
useMessageHandler,
|
|
@@ -83,7 +87,12 @@ export type {
|
|
|
83
87
|
BaseMessage,
|
|
84
88
|
Conversation,
|
|
85
89
|
ConversationTree,
|
|
90
|
+
MessageContent,
|
|
91
|
+
MessageContentStatus,
|
|
92
|
+
MessageReasoningContent,
|
|
86
93
|
MessageRole,
|
|
87
94
|
MessageStatus,
|
|
95
|
+
MessageTextContent,
|
|
96
|
+
MessageToolContent,
|
|
88
97
|
TreeMessage,
|
|
89
98
|
} from './types';
|
package/src/services/index.ts
CHANGED
package/src/services/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseMessage, MessageRole } from '../types';
|
|
1
|
+
import type { BaseMessage, MessageContent, MessageRole } from '../types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Minimal HTTP transport contract that chat-kit composables depend on.
|
|
@@ -61,7 +61,7 @@ export interface RawConversationList {
|
|
|
61
61
|
export interface ChatMessage {
|
|
62
62
|
id: string;
|
|
63
63
|
role: MessageRole;
|
|
64
|
-
content:
|
|
64
|
+
content: MessageContent[];
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|