@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.
Files changed (73) hide show
  1. package/dist/{CkInput.vue_vue_type_script_setup_true_lang-CyGLItQZ.js → CkInput.vue_vue_type_script_setup_true_lang-C5C09u0z.js} +9 -8
  2. package/dist/adapters/index.d.ts +18 -0
  3. package/dist/adapters/index.d.ts.map +1 -0
  4. package/dist/adapters/index.js +4 -0
  5. package/dist/adapters/responses-api.d.ts +180 -0
  6. package/dist/adapters/responses-api.d.ts.map +1 -0
  7. package/dist/adapters/responses-api.js +153 -0
  8. package/dist/components/base/CkPromptActionButton/CkPromptActionButton.vue.d.ts +1 -1
  9. package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +11 -3
  10. package/dist/components/composed/CkConversation/CkConversation.stories.d.ts.map +1 -1
  11. package/dist/components/composed/CkConversation/CkConversation.vue.d.ts +5 -1
  12. package/dist/components/composed/CkConversation/CkConversation.vue.d.ts.map +1 -1
  13. package/dist/components/index.js +1 -1
  14. package/dist/composables/fileUpload/index.d.ts +7 -0
  15. package/dist/composables/fileUpload/index.d.ts.map +1 -0
  16. package/dist/composables/fileUpload/types.d.ts +295 -0
  17. package/dist/composables/fileUpload/types.d.ts.map +1 -0
  18. package/dist/composables/fileUpload/useFileUpload.d.ts +4 -0
  19. package/dist/composables/fileUpload/useFileUpload.d.ts.map +1 -0
  20. package/dist/composables/fileUpload/useFileUploadWrapper.d.ts +3 -0
  21. package/dist/composables/fileUpload/useFileUploadWrapper.d.ts.map +1 -0
  22. package/dist/composables/index.d.ts +2 -1
  23. package/dist/composables/index.d.ts.map +1 -1
  24. package/dist/composables/index.js +5 -2
  25. package/dist/composables/useConversations.d.ts +12 -0
  26. package/dist/composables/useConversations.d.ts.map +1 -1
  27. package/dist/generateId-BHf0W4Rq.js +6 -0
  28. package/dist/handlers/index.js +1 -1
  29. package/dist/handlers/useMessageHandler.d.ts +2 -2
  30. package/dist/handlers/useMessageHandler.d.ts.map +1 -1
  31. package/dist/index-BbTgIoYa.js +1557 -0
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +6 -8
  35. package/dist/services/index.d.ts +0 -2
  36. package/dist/services/index.d.ts.map +1 -1
  37. package/dist/services/index.js +1 -37
  38. package/dist/services/types.d.ts +2 -2
  39. package/dist/services/types.d.ts.map +1 -1
  40. package/dist/types.d.ts +47 -2
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/{useMessageHandler-BmBdu2EW.js → useMessageHandler-ZRC-gIOJ.js} +5 -5
  43. package/package.json +10 -2
  44. package/src/adapters/index.ts +17 -0
  45. package/src/adapters/responses-api.spec.ts +365 -0
  46. package/src/adapters/responses-api.ts +444 -0
  47. package/src/components/composed/CkConversation/CkConversation.spec.ts +171 -25
  48. package/src/components/composed/CkConversation/CkConversation.stories.ts +266 -46
  49. package/src/components/composed/CkConversation/CkConversation.vue +19 -7
  50. package/src/composables/fileUpload/index.spec.ts +213 -0
  51. package/src/composables/fileUpload/index.ts +83 -0
  52. package/src/composables/fileUpload/types.ts +409 -0
  53. package/src/composables/fileUpload/useFileUpload.ts +1242 -0
  54. package/src/composables/fileUpload/useFileUploadWrapper.ts +382 -0
  55. package/src/composables/index.ts +2 -1
  56. package/src/composables/useConversations.spec.ts +118 -0
  57. package/src/composables/useConversations.ts +32 -0
  58. package/src/handlers/useChatKitLabels.spec.ts +16 -2
  59. package/src/handlers/useMessageHandler.spec.ts +112 -44
  60. package/src/handlers/useMessageHandler.ts +16 -2
  61. package/src/helpers/conversationTree.spec.ts +46 -19
  62. package/src/index.ts +11 -2
  63. package/src/services/index.ts +0 -2
  64. package/src/services/types.ts +2 -2
  65. package/src/types.ts +55 -2
  66. package/dist/ChatService-LogwJNxh.js +0 -9
  67. package/dist/services/ChatService.d.ts +0 -42
  68. package/dist/services/ChatService.d.ts.map +0 -1
  69. package/dist/services/MockChatService.d.ts +0 -10
  70. package/dist/services/MockChatService.d.ts.map +0 -1
  71. package/dist/useConversationSessions-ulFhtpQr.js +0 -243
  72. package/src/services/ChatService.ts +0 -60
  73. package/src/services/MockChatService.ts +0 -47
@@ -1,6 +1,29 @@
1
- import type { ConversationTree, TreeMessage } from '../types';
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).toBe(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: text,
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: string;
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 { ConversationTree, TreeMessage } from '../types';
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({ id: 'u2', parentId: 'a1', content: 'Tell me more' }),
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';
@@ -1,5 +1,3 @@
1
- export { ChatService, type IChatService } from './ChatService';
2
- export { MockChatService } from './MockChatService';
3
1
  export type {
4
2
  ChatConfig,
5
3
  ChatMessage,
@@ -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: string;
64
+ content: MessageContent[];
65
65
  }
66
66
 
67
67
  /**