@aleph-alpha/chat-kit 1.3.1 → 1.4.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/index.js +1 -1
- 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.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/{useConversationSessions-ulFhtpQr.js → useConversationSessions-GLZV_Ctr.js} +18 -2
- 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/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/src/services/ChatService.ts +0 -60
- package/src/services/MockChatService.ts +0 -47
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseMessage } from '../../../types';
|
|
1
|
+
import type { BaseMessage, MessageContent } from '../../../types';
|
|
2
2
|
import CkConversation from './CkConversation.vue';
|
|
3
3
|
import { defaultMarkdownClasses } from '../../base/CkMarkdownRenderer/defaultMarkdownClasses';
|
|
4
4
|
import { render, screen } from '@testing-library/vue';
|
|
@@ -6,45 +6,62 @@ import { h } from 'vue';
|
|
|
6
6
|
|
|
7
7
|
const now = new Date();
|
|
8
8
|
|
|
9
|
+
let textIdCounter = 0;
|
|
10
|
+
function text(value: string): MessageContent {
|
|
11
|
+
textIdCounter += 1;
|
|
12
|
+
return {
|
|
13
|
+
type: 'text',
|
|
14
|
+
id: `txt-${textIdCounter}`,
|
|
15
|
+
text: value,
|
|
16
|
+
status: 'completed',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
9
20
|
const messages: BaseMessage[] = [
|
|
10
21
|
{
|
|
11
22
|
id: '1',
|
|
12
23
|
role: 'user',
|
|
13
|
-
content: 'Hello there',
|
|
24
|
+
content: [text('Hello there')],
|
|
14
25
|
createdAt: now,
|
|
15
26
|
status: 'completed',
|
|
16
27
|
},
|
|
17
28
|
{
|
|
18
29
|
id: '2',
|
|
19
30
|
role: 'assistant',
|
|
20
|
-
content: 'Hi! How can I help?',
|
|
31
|
+
content: [text('Hi! How can I help?')],
|
|
21
32
|
createdAt: now,
|
|
22
33
|
status: 'completed',
|
|
23
34
|
},
|
|
24
35
|
{
|
|
25
36
|
id: '3',
|
|
26
37
|
role: 'user',
|
|
27
|
-
content: 'Tell me a joke',
|
|
38
|
+
content: [text('Tell me a joke')],
|
|
28
39
|
createdAt: now,
|
|
29
40
|
status: 'completed',
|
|
30
41
|
},
|
|
31
42
|
{
|
|
32
43
|
id: '4',
|
|
33
44
|
role: 'assistant',
|
|
34
|
-
content: 'Why did the chicken cross the road?',
|
|
45
|
+
content: [text('Why did the chicken cross the road?')],
|
|
35
46
|
createdAt: now,
|
|
36
47
|
status: 'completed',
|
|
37
48
|
},
|
|
38
49
|
];
|
|
39
50
|
|
|
40
|
-
function agentMessage(
|
|
51
|
+
function agentMessage(
|
|
52
|
+
overrides: Partial<Omit<BaseMessage, 'content'>> & {
|
|
53
|
+
content?: MessageContent[];
|
|
54
|
+
text?: string;
|
|
55
|
+
} = {},
|
|
56
|
+
): BaseMessage {
|
|
57
|
+
const { content, text: textOverride, ...rest } = overrides;
|
|
41
58
|
return {
|
|
42
59
|
id: '1',
|
|
43
60
|
role: 'assistant',
|
|
44
|
-
content: 'Hello',
|
|
45
61
|
createdAt: now,
|
|
46
62
|
status: 'completed',
|
|
47
|
-
...
|
|
63
|
+
...rest,
|
|
64
|
+
content: content ?? [text(textOverride ?? 'Hello')],
|
|
48
65
|
};
|
|
49
66
|
}
|
|
50
67
|
|
|
@@ -65,6 +82,44 @@ describe('CkConversation', () => {
|
|
|
65
82
|
screen.getByText('Why did the chicken cross the road?'),
|
|
66
83
|
).toBeInTheDocument();
|
|
67
84
|
});
|
|
85
|
+
|
|
86
|
+
it('joins multiple text content parts in agent messages', () => {
|
|
87
|
+
const message: BaseMessage = {
|
|
88
|
+
id: 'a-multi',
|
|
89
|
+
role: 'assistant',
|
|
90
|
+
content: [text('Part one. '), text('Part two.')],
|
|
91
|
+
createdAt: now,
|
|
92
|
+
status: 'completed',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const { container } = render(CkConversation, {
|
|
96
|
+
props: { messages: [message] },
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const renderer = container.querySelector('.markdown-renderer');
|
|
100
|
+
expect(renderer).not.toBeNull();
|
|
101
|
+
expect(renderer?.textContent).toBe('Part one. Part two.');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('ignores non-text content parts in default rendering', () => {
|
|
105
|
+
const message: BaseMessage = {
|
|
106
|
+
id: 'a-skip',
|
|
107
|
+
role: 'assistant',
|
|
108
|
+
content: [
|
|
109
|
+
{ type: 'reasoning', id: 'r-1', status: 'completed' },
|
|
110
|
+
{ type: 'tool', id: 't-1', name: 'search' },
|
|
111
|
+
text('Final answer.'),
|
|
112
|
+
],
|
|
113
|
+
createdAt: now,
|
|
114
|
+
status: 'completed',
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
render(CkConversation, { props: { messages: [message] } });
|
|
118
|
+
|
|
119
|
+
expect(screen.getByText('Final answer.')).toBeInTheDocument();
|
|
120
|
+
// Tool name and reasoning lifecycle are not rendered by default
|
|
121
|
+
expect(screen.queryByText('search')).not.toBeInTheDocument();
|
|
122
|
+
});
|
|
68
123
|
});
|
|
69
124
|
|
|
70
125
|
describe('agent icon', () => {
|
|
@@ -81,12 +136,10 @@ describe('CkConversation', () => {
|
|
|
81
136
|
props: { messages: [agentMessage()] },
|
|
82
137
|
});
|
|
83
138
|
|
|
84
|
-
// The icon container inside CkAgentMessage should not be rendered
|
|
85
139
|
const agentMessageEl = container.querySelector(
|
|
86
140
|
'.flex.w-full.items-start.gap-2',
|
|
87
141
|
);
|
|
88
142
|
expect(agentMessageEl).toBeInTheDocument();
|
|
89
|
-
// The flex-shrink-0 icon wrapper should not exist
|
|
90
143
|
expect(
|
|
91
144
|
agentMessageEl?.querySelector(
|
|
92
145
|
'.flex.flex-shrink-0.items-center.justify-center',
|
|
@@ -216,6 +269,51 @@ describe('CkConversation', () => {
|
|
|
216
269
|
});
|
|
217
270
|
});
|
|
218
271
|
|
|
272
|
+
describe('copy action', () => {
|
|
273
|
+
it('exposes the joined text via the copy action button', () => {
|
|
274
|
+
const message: BaseMessage = {
|
|
275
|
+
id: 'mix2',
|
|
276
|
+
role: 'assistant',
|
|
277
|
+
content: [
|
|
278
|
+
{ type: 'reasoning', id: 'r', status: 'completed' },
|
|
279
|
+
text('Hello '),
|
|
280
|
+
{ type: 'tool', id: 'tl', name: 'search' },
|
|
281
|
+
text('world.'),
|
|
282
|
+
],
|
|
283
|
+
createdAt: now,
|
|
284
|
+
status: 'completed',
|
|
285
|
+
};
|
|
286
|
+
const { container } = render(CkConversation, {
|
|
287
|
+
props: { messages: [message] },
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const copyButton = container.querySelector(
|
|
291
|
+
'button[aria-label="Copy response"]',
|
|
292
|
+
);
|
|
293
|
+
expect(copyButton).not.toBeNull();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('hides the copy button when there is no text content', () => {
|
|
297
|
+
const message: BaseMessage = {
|
|
298
|
+
id: 'mix3',
|
|
299
|
+
role: 'assistant',
|
|
300
|
+
content: [
|
|
301
|
+
{ type: 'reasoning', id: 'r', status: 'completed' },
|
|
302
|
+
{ type: 'tool', id: 'tl', name: 'search' },
|
|
303
|
+
],
|
|
304
|
+
createdAt: now,
|
|
305
|
+
status: 'completed',
|
|
306
|
+
};
|
|
307
|
+
const { container } = render(CkConversation, {
|
|
308
|
+
props: { messages: [message] },
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
expect(
|
|
312
|
+
container.querySelector('button[aria-label="Copy response"]'),
|
|
313
|
+
).toBeNull();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
219
317
|
describe('slot overrides', () => {
|
|
220
318
|
it('allows replacing user message content via user-message-content slot', () => {
|
|
221
319
|
render(CkConversation, {
|
|
@@ -224,7 +322,7 @@ describe('CkConversation', () => {
|
|
|
224
322
|
{
|
|
225
323
|
id: '1',
|
|
226
324
|
role: 'user',
|
|
227
|
-
content: 'Original',
|
|
325
|
+
content: [text('Original')],
|
|
228
326
|
createdAt: now,
|
|
229
327
|
status: 'completed',
|
|
230
328
|
} as BaseMessage,
|
|
@@ -244,7 +342,7 @@ describe('CkConversation', () => {
|
|
|
244
342
|
{
|
|
245
343
|
id: '1',
|
|
246
344
|
role: 'user',
|
|
247
|
-
content: 'Original',
|
|
345
|
+
content: [text('Original')],
|
|
248
346
|
createdAt: now,
|
|
249
347
|
status: 'completed',
|
|
250
348
|
} as BaseMessage,
|
|
@@ -260,7 +358,7 @@ describe('CkConversation', () => {
|
|
|
260
358
|
|
|
261
359
|
it('allows replacing agent content via agent-message-content slot', () => {
|
|
262
360
|
render(CkConversation, {
|
|
263
|
-
props: { messages: [agentMessage({
|
|
361
|
+
props: { messages: [agentMessage({ text: 'Original' })] },
|
|
264
362
|
slots: { 'agent-message-content': 'Rich agent content' },
|
|
265
363
|
});
|
|
266
364
|
|
|
@@ -270,7 +368,7 @@ describe('CkConversation', () => {
|
|
|
270
368
|
|
|
271
369
|
it('allows replacing the entire agent message via agent-message slot', () => {
|
|
272
370
|
render(CkConversation, {
|
|
273
|
-
props: { messages: [agentMessage({
|
|
371
|
+
props: { messages: [agentMessage({ text: 'Original' })] },
|
|
274
372
|
slots: {
|
|
275
373
|
'agent-message':
|
|
276
374
|
'<div data-testid="custom-agent">Fully replaced</div>',
|
|
@@ -306,22 +404,71 @@ describe('CkConversation', () => {
|
|
|
306
404
|
|
|
307
405
|
expect(screen.getByText('Copy')).toBeInTheDocument();
|
|
308
406
|
});
|
|
407
|
+
|
|
408
|
+
it('exposes the full content array to agent-message-content for custom rendering', () => {
|
|
409
|
+
const message: BaseMessage = {
|
|
410
|
+
id: 'cot',
|
|
411
|
+
role: 'assistant',
|
|
412
|
+
content: [
|
|
413
|
+
{ type: 'reasoning', id: 'r', status: 'completed' },
|
|
414
|
+
{ type: 'tool', id: 'tl', name: 'web_search' },
|
|
415
|
+
text('Done.'),
|
|
416
|
+
],
|
|
417
|
+
createdAt: now,
|
|
418
|
+
status: 'completed',
|
|
419
|
+
};
|
|
420
|
+
function describePart(part: MessageContent): string {
|
|
421
|
+
if (part.type === 'tool') return part.name;
|
|
422
|
+
if (part.type === 'text') return part.text;
|
|
423
|
+
return part.status;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const { container } = render(CkConversation, {
|
|
427
|
+
props: { messages: [message] },
|
|
428
|
+
slots: {
|
|
429
|
+
'agent-message-content': ({ message: m }: { message: BaseMessage }) =>
|
|
430
|
+
h(
|
|
431
|
+
'ul',
|
|
432
|
+
{ 'data-testid': 'parts' },
|
|
433
|
+
m.content.map((part) =>
|
|
434
|
+
h(
|
|
435
|
+
'li',
|
|
436
|
+
{ 'data-type': part.type, key: part.id },
|
|
437
|
+
describePart(part),
|
|
438
|
+
),
|
|
439
|
+
),
|
|
440
|
+
),
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
const list = container.querySelector('[data-testid="parts"]');
|
|
445
|
+
expect(list).not.toBeNull();
|
|
446
|
+
expect(list?.querySelectorAll('li')).toHaveLength(3);
|
|
447
|
+
expect(list?.querySelector('[data-type="reasoning"]')?.textContent).toBe(
|
|
448
|
+
'completed',
|
|
449
|
+
);
|
|
450
|
+
expect(list?.querySelector('[data-type="tool"]')?.textContent).toBe(
|
|
451
|
+
'web_search',
|
|
452
|
+
);
|
|
453
|
+
expect(list?.querySelector('[data-type="text"]')?.textContent).toBe(
|
|
454
|
+
'Done.',
|
|
455
|
+
);
|
|
456
|
+
});
|
|
309
457
|
});
|
|
310
458
|
|
|
311
459
|
describe('empty conversation', () => {
|
|
312
460
|
it('renders nothing when messages array is empty', () => {
|
|
313
461
|
const { container } = render(CkConversation, { props: { messages: [] } });
|
|
314
462
|
|
|
315
|
-
// Only the scroll container should exist, with no message content
|
|
316
463
|
expect(container.querySelectorAll('[data-message-row]')).toHaveLength(0);
|
|
317
464
|
});
|
|
318
465
|
});
|
|
319
466
|
|
|
320
467
|
describe('markdown rendering', () => {
|
|
321
|
-
it('renders agent
|
|
468
|
+
it('renders agent text content as markdown by default', () => {
|
|
322
469
|
const { container } = render(CkConversation, {
|
|
323
470
|
props: {
|
|
324
|
-
messages: [agentMessage({
|
|
471
|
+
messages: [agentMessage({ text: 'This has **bold** text' })],
|
|
325
472
|
},
|
|
326
473
|
});
|
|
327
474
|
|
|
@@ -332,7 +479,7 @@ describe('CkConversation', () => {
|
|
|
332
479
|
const { container } = render(CkConversation, {
|
|
333
480
|
props: {
|
|
334
481
|
messages: [
|
|
335
|
-
agentMessage({
|
|
482
|
+
agentMessage({ text: '## Title\n\n- item one\n- item two' }),
|
|
336
483
|
],
|
|
337
484
|
},
|
|
338
485
|
});
|
|
@@ -344,7 +491,7 @@ describe('CkConversation', () => {
|
|
|
344
491
|
it('applies default markdown classes to elements', () => {
|
|
345
492
|
const { container } = render(CkConversation, {
|
|
346
493
|
props: {
|
|
347
|
-
messages: [agentMessage({
|
|
494
|
+
messages: [agentMessage({ text: 'A paragraph' })],
|
|
348
495
|
},
|
|
349
496
|
});
|
|
350
497
|
|
|
@@ -356,7 +503,7 @@ describe('CkConversation', () => {
|
|
|
356
503
|
it('merges agentMarkdownClasses prop with defaults', () => {
|
|
357
504
|
const { container } = render(CkConversation, {
|
|
358
505
|
props: {
|
|
359
|
-
messages: [agentMessage({
|
|
506
|
+
messages: [agentMessage({ text: 'A paragraph' })],
|
|
360
507
|
agentMarkdownClasses: { p: 'custom-paragraph' },
|
|
361
508
|
},
|
|
362
509
|
});
|
|
@@ -371,8 +518,7 @@ describe('CkConversation', () => {
|
|
|
371
518
|
props: {
|
|
372
519
|
messages: [
|
|
373
520
|
agentMessage({
|
|
374
|
-
|
|
375
|
-
'## Heading\n\nParagraph with [link](https://example.com)',
|
|
521
|
+
text: '## Heading\n\nParagraph with [link](https://example.com)',
|
|
376
522
|
}),
|
|
377
523
|
],
|
|
378
524
|
agentMarkdownClasses: { p: 'custom-paragraph' },
|
|
@@ -388,7 +534,7 @@ describe('CkConversation', () => {
|
|
|
388
534
|
|
|
389
535
|
it('does not render CkMarkdownRenderer when agent-message-content slot is provided', () => {
|
|
390
536
|
const { container } = render(CkConversation, {
|
|
391
|
-
props: { messages: [agentMessage({
|
|
537
|
+
props: { messages: [agentMessage({ text: '**bold**' })] },
|
|
392
538
|
slots: {
|
|
393
539
|
'agent-message-content':
|
|
394
540
|
'<div data-testid="custom-content">Custom</div>',
|
|
@@ -403,7 +549,7 @@ describe('CkConversation', () => {
|
|
|
403
549
|
const markdown = '```python\nprint("hello")\n```';
|
|
404
550
|
|
|
405
551
|
const { container } = render(CkConversation, {
|
|
406
|
-
props: { messages: [agentMessage({
|
|
552
|
+
props: { messages: [agentMessage({ text: markdown })] },
|
|
407
553
|
slots: {
|
|
408
554
|
'agent-message-code-block': (props: {
|
|
409
555
|
code: string;
|
|
@@ -428,7 +574,7 @@ describe('CkConversation', () => {
|
|
|
428
574
|
const markdown = '```javascript\nconsole.log("hi");\n```';
|
|
429
575
|
|
|
430
576
|
const { container } = render(CkConversation, {
|
|
431
|
-
props: { messages: [agentMessage({
|
|
577
|
+
props: { messages: [agentMessage({ text: markdown })] },
|
|
432
578
|
});
|
|
433
579
|
|
|
434
580
|
const codeEl = container.querySelector('pre > code');
|