@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,13 +1,35 @@
|
|
|
1
1
|
import { CkCopyMessageActionButton } from '../../base/CkCopyMessageActionButton';
|
|
2
2
|
import { CkFeedbackButton } from '../../base/CkFeedbackButton';
|
|
3
3
|
import type { CkFeedbackValue } from '../../base/CkFeedbackButton';
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
BaseMessage,
|
|
6
|
+
MessageContent,
|
|
7
|
+
MessageTextContent,
|
|
8
|
+
} from '../../../types';
|
|
5
9
|
import CkConversation from './CkConversation.vue';
|
|
6
10
|
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
7
11
|
import { ref } from 'vue';
|
|
8
12
|
|
|
9
13
|
const now = new Date();
|
|
10
14
|
|
|
15
|
+
let textIdCounter = 0;
|
|
16
|
+
function text(value: string): MessageTextContent {
|
|
17
|
+
textIdCounter += 1;
|
|
18
|
+
return {
|
|
19
|
+
type: 'text',
|
|
20
|
+
id: `txt-${textIdCounter}`,
|
|
21
|
+
text: value,
|
|
22
|
+
status: 'completed',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getMessageText(message: BaseMessage): string {
|
|
27
|
+
return message.content
|
|
28
|
+
.filter((c): c is MessageTextContent => c.type === 'text')
|
|
29
|
+
.map((c) => c.text)
|
|
30
|
+
.join('');
|
|
31
|
+
}
|
|
32
|
+
|
|
11
33
|
const markdownContent = [
|
|
12
34
|
'Based on a review of internal documents, here are the **key lessons learned** and best practices from the *Digital Transformation Phase 1* project, broken down by category.',
|
|
13
35
|
'',
|
|
@@ -37,31 +59,64 @@ const sampleMessages: BaseMessage[] = [
|
|
|
37
59
|
{
|
|
38
60
|
id: '1',
|
|
39
61
|
role: 'user',
|
|
40
|
-
content: 'Hello! Can you help me with something?',
|
|
62
|
+
content: [text('Hello! Can you help me with something?')],
|
|
41
63
|
createdAt: now,
|
|
42
64
|
status: 'completed',
|
|
43
65
|
},
|
|
44
66
|
{
|
|
45
67
|
id: '2',
|
|
46
68
|
role: 'assistant',
|
|
47
|
-
content:
|
|
69
|
+
content: [
|
|
70
|
+
text('Of course! I am here to help. What would you like to know?'),
|
|
71
|
+
],
|
|
48
72
|
createdAt: now,
|
|
49
73
|
status: 'completed',
|
|
50
74
|
},
|
|
51
75
|
{
|
|
52
76
|
id: '3',
|
|
53
77
|
role: 'user',
|
|
54
|
-
content:
|
|
55
|
-
|
|
78
|
+
content: [
|
|
79
|
+
text(
|
|
80
|
+
"Find the lessons learned and best practices from the 'Digital Transformation Phase 1' project, specifically focusing on process optimization and digital tool integration.",
|
|
81
|
+
),
|
|
82
|
+
],
|
|
56
83
|
createdAt: now,
|
|
57
84
|
status: 'completed',
|
|
58
85
|
},
|
|
59
86
|
{
|
|
60
87
|
id: '4',
|
|
61
88
|
role: 'assistant',
|
|
62
|
-
content: markdownContent,
|
|
89
|
+
content: [text(markdownContent)],
|
|
90
|
+
createdAt: now,
|
|
91
|
+
status: 'completed',
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const chainOfThoughtMessages: BaseMessage[] = [
|
|
96
|
+
{
|
|
97
|
+
id: '1',
|
|
98
|
+
role: 'user',
|
|
99
|
+
content: [
|
|
100
|
+
text(
|
|
101
|
+
'Look up the recent revenue numbers for Q3 and tell me how they compare to last year.',
|
|
102
|
+
),
|
|
103
|
+
],
|
|
104
|
+
createdAt: now,
|
|
105
|
+
status: 'completed',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: '2',
|
|
109
|
+
role: 'assistant',
|
|
63
110
|
createdAt: now,
|
|
64
111
|
status: 'completed',
|
|
112
|
+
content: [
|
|
113
|
+
{ type: 'reasoning', id: 'r-1', status: 'completed' },
|
|
114
|
+
{ type: 'tool', id: 't-1', name: 'finance_lookup' },
|
|
115
|
+
{ type: 'tool', id: 't-2', name: 'compare_periods' },
|
|
116
|
+
text(
|
|
117
|
+
'## Revenue summary\n\n- **Q3 2025:** **$48.2M** (+12% YoY)\n- **Q3 2024:** $43.0M\n\nThe biggest contributor was the EMEA segment, which grew **18%** year over year. North America was roughly flat.',
|
|
118
|
+
),
|
|
119
|
+
],
|
|
65
120
|
},
|
|
66
121
|
];
|
|
67
122
|
|
|
@@ -109,8 +164,20 @@ import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
|
109
164
|
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
110
165
|
|
|
111
166
|
const messages: BaseMessage[] = [
|
|
112
|
-
{
|
|
113
|
-
|
|
167
|
+
{
|
|
168
|
+
id: '1',
|
|
169
|
+
role: 'user',
|
|
170
|
+
content: [{ type: 'text', id: 't1', text: 'Hello!', status: 'completed' }],
|
|
171
|
+
createdAt: new Date(),
|
|
172
|
+
status: 'completed',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: '2',
|
|
176
|
+
role: 'assistant',
|
|
177
|
+
content: [{ type: 'text', id: 't2', text: 'Hi there!', status: 'completed' }],
|
|
178
|
+
createdAt: new Date(),
|
|
179
|
+
status: 'completed',
|
|
180
|
+
},
|
|
114
181
|
];
|
|
115
182
|
</script>
|
|
116
183
|
|
|
@@ -118,6 +185,56 @@ const messages: BaseMessage[] = [
|
|
|
118
185
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;" />
|
|
119
186
|
</template>`;
|
|
120
187
|
|
|
188
|
+
const chainOfThoughtTemplateSource = `<script setup lang="ts">
|
|
189
|
+
import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
190
|
+
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
191
|
+
|
|
192
|
+
const messages: BaseMessage[] = [
|
|
193
|
+
{
|
|
194
|
+
id: '1',
|
|
195
|
+
role: 'user',
|
|
196
|
+
content: [{ type: 'text', id: 't1', text: 'Compare Q3 revenue.', status: 'completed' }],
|
|
197
|
+
createdAt: new Date(),
|
|
198
|
+
status: 'completed',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: '2',
|
|
202
|
+
role: 'assistant',
|
|
203
|
+
createdAt: new Date(),
|
|
204
|
+
status: 'completed',
|
|
205
|
+
content: [
|
|
206
|
+
{ type: 'reasoning', id: 'r1', status: 'completed' },
|
|
207
|
+
{ type: 'tool', id: 'tl1', name: 'finance_lookup' },
|
|
208
|
+
{ type: 'tool', id: 'tl2', name: 'compare_periods' },
|
|
209
|
+
{ type: 'text', id: 't2', text: 'Q3 grew **12% YoY**.', status: 'completed' },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
</script>
|
|
214
|
+
|
|
215
|
+
<template>
|
|
216
|
+
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
217
|
+
<!--
|
|
218
|
+
Use #agent-message-content to take over rendering of the entire body.
|
|
219
|
+
The slot exposes the full message so you can iterate every content
|
|
220
|
+
part (text, tool, reasoning) and render them however you like.
|
|
221
|
+
-->
|
|
222
|
+
<template #agent-message-content="{ message }">
|
|
223
|
+
<div class="flex flex-col gap-2">
|
|
224
|
+
<template v-for="part in message.content" :key="part.id">
|
|
225
|
+
<span v-if="part.type === 'reasoning'" class="text-xs italic opacity-60">
|
|
226
|
+
Reasoning: {{ part.status }}
|
|
227
|
+
</span>
|
|
228
|
+
<span v-else-if="part.type === 'tool'" class="text-xs font-mono opacity-70">
|
|
229
|
+
⚙️ {{ part.name }}
|
|
230
|
+
</span>
|
|
231
|
+
<p v-else-if="part.type === 'text'">{{ part.text }}</p>
|
|
232
|
+
</template>
|
|
233
|
+
</div>
|
|
234
|
+
</template>
|
|
235
|
+
</CkConversation>
|
|
236
|
+
</template>`;
|
|
237
|
+
|
|
121
238
|
const withIconTemplateSource = `<script setup lang="ts">
|
|
122
239
|
import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
123
240
|
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
@@ -182,7 +299,13 @@ import type { BaseMessage, CkFeedbackValue } from '@aleph-alpha/chat-kit';
|
|
|
182
299
|
|
|
183
300
|
const messages: BaseMessage[] = [/* ... */];
|
|
184
301
|
|
|
185
|
-
|
|
302
|
+
function getMessageText(message: BaseMessage): string {
|
|
303
|
+
return message.content
|
|
304
|
+
.filter((c) => c.type === 'text')
|
|
305
|
+
.map((c) => c.text)
|
|
306
|
+
.join('');
|
|
307
|
+
}
|
|
308
|
+
|
|
186
309
|
const feedback = ref<Record<string, { value: CkFeedbackValue; locked: boolean }>>({});
|
|
187
310
|
function onFeedback(messageId: string, value: CkFeedbackValue) {
|
|
188
311
|
feedback.value[messageId] = { value, locked: value !== null };
|
|
@@ -192,7 +315,7 @@ function onFeedback(messageId: string, value: CkFeedbackValue) {
|
|
|
192
315
|
<template>
|
|
193
316
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
194
317
|
<template #agent-message-footer="{ message }">
|
|
195
|
-
<CkCopyMessageActionButton :text="message
|
|
318
|
+
<CkCopyMessageActionButton :text="getMessageText(message)" />
|
|
196
319
|
<CkFeedbackButton
|
|
197
320
|
:value="feedback[message.id]?.value ?? null"
|
|
198
321
|
:locked="feedback[message.id]?.locked ?? false"
|
|
@@ -207,12 +330,19 @@ import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
|
207
330
|
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
208
331
|
|
|
209
332
|
const messages: BaseMessage[] = [/* ... */];
|
|
333
|
+
|
|
334
|
+
function getMessageText(message: BaseMessage): string {
|
|
335
|
+
return message.content
|
|
336
|
+
.filter((c) => c.type === 'text')
|
|
337
|
+
.map((c) => c.text)
|
|
338
|
+
.join('');
|
|
339
|
+
}
|
|
210
340
|
</script>
|
|
211
341
|
|
|
212
342
|
<template>
|
|
213
343
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
214
344
|
<template #user-message-content="{ message }">
|
|
215
|
-
<strong>{{ message
|
|
345
|
+
<strong>{{ getMessageText(message) }}</strong>
|
|
216
346
|
</template>
|
|
217
347
|
</CkConversation>
|
|
218
348
|
</template>`;
|
|
@@ -222,12 +352,19 @@ import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
|
222
352
|
import type { BaseMessage } from '@aleph-alpha/chat-kit';
|
|
223
353
|
|
|
224
354
|
const messages: BaseMessage[] = [/* ... */];
|
|
355
|
+
|
|
356
|
+
function getMessageText(message: BaseMessage): string {
|
|
357
|
+
return message.content
|
|
358
|
+
.filter((c) => c.type === 'text')
|
|
359
|
+
.map((c) => c.text)
|
|
360
|
+
.join('');
|
|
361
|
+
}
|
|
225
362
|
</script>
|
|
226
363
|
|
|
227
364
|
<template>
|
|
228
365
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
229
366
|
<template #agent-message-content="{ message }">
|
|
230
|
-
<div class="whitespace-pre-wrap font-mono text-sm">{{ message
|
|
367
|
+
<div class="whitespace-pre-wrap font-mono text-sm">{{ getMessageText(message) }}</div>
|
|
231
368
|
</template>
|
|
232
369
|
</CkConversation>
|
|
233
370
|
</template>`;
|
|
@@ -242,19 +379,37 @@ const messages = ref<BaseMessage[]>([]);
|
|
|
242
379
|
function sendAndStream(): void {
|
|
243
380
|
messages.value = [
|
|
244
381
|
...messages.value,
|
|
245
|
-
{
|
|
382
|
+
{
|
|
383
|
+
id: '1',
|
|
384
|
+
role: 'user',
|
|
385
|
+
content: [{ type: 'text', id: 'u-t', text: 'Tell me something.', status: 'completed' }],
|
|
386
|
+
createdAt: new Date(),
|
|
387
|
+
status: 'completed',
|
|
388
|
+
},
|
|
246
389
|
];
|
|
390
|
+
const replyId = 'a-t';
|
|
247
391
|
messages.value = [
|
|
248
392
|
...messages.value,
|
|
249
|
-
{
|
|
393
|
+
{
|
|
394
|
+
id: '2',
|
|
395
|
+
role: 'assistant',
|
|
396
|
+
content: [{ type: 'text', id: replyId, text: '', status: 'in_progress' }],
|
|
397
|
+
createdAt: new Date(),
|
|
398
|
+
status: 'sending',
|
|
399
|
+
},
|
|
250
400
|
];
|
|
251
401
|
const reply = 'Key lessons learned from the project...';
|
|
252
402
|
let i = 0;
|
|
253
403
|
const interval = setInterval(() => {
|
|
254
404
|
i = Math.min(i + 3, reply.length);
|
|
255
405
|
const last = messages.value.length - 1;
|
|
256
|
-
|
|
257
|
-
|
|
406
|
+
const completed = i >= reply.length;
|
|
407
|
+
messages.value[last] = {
|
|
408
|
+
...messages.value[last],
|
|
409
|
+
content: [{ type: 'text', id: replyId, text: reply.slice(0, i), status: completed ? 'completed' : 'in_progress' }],
|
|
410
|
+
status: completed ? 'completed' : 'sending',
|
|
411
|
+
};
|
|
412
|
+
if (completed) clearInterval(interval);
|
|
258
413
|
}, 30);
|
|
259
414
|
}
|
|
260
415
|
</script>
|
|
@@ -312,9 +467,10 @@ import { CkConversation } from '@aleph-alpha/chat-kit';
|
|
|
312
467
|
</template>`;
|
|
313
468
|
|
|
314
469
|
/**
|
|
315
|
-
* Drop-in conversation UI. Pass `messages` with `role`, `content
|
|
316
|
-
*
|
|
317
|
-
* and a copy button on
|
|
470
|
+
* Drop-in conversation UI. Pass `messages` with `role`, `content` (a list of
|
|
471
|
+
* text / tool / reasoning parts), and `status` fields and CkConversation
|
|
472
|
+
* renders user bubbles, assistant markdown, avatars, and a copy button on
|
|
473
|
+
* each reply.
|
|
318
474
|
*/
|
|
319
475
|
export const Default: Story = {
|
|
320
476
|
render: () => ({
|
|
@@ -328,6 +484,39 @@ export const Default: Story = {
|
|
|
328
484
|
parameters: { docs: { source: { code: defaultTemplateSource } } },
|
|
329
485
|
};
|
|
330
486
|
|
|
487
|
+
/**
|
|
488
|
+
* Demonstrates a chain-of-thought assistant turn with reasoning and tool
|
|
489
|
+
* calls in `message.content`. By default only text parts are rendered, so
|
|
490
|
+
* use `#agent-message-content` to opt-in to a custom rendering of the
|
|
491
|
+
* non-text parts. The host owns the visual treatment for those segments.
|
|
492
|
+
*/
|
|
493
|
+
export const ChainOfThought: Story = {
|
|
494
|
+
render: () => ({
|
|
495
|
+
components: { CkConversation },
|
|
496
|
+
setup() {
|
|
497
|
+
return { messages: chainOfThoughtMessages };
|
|
498
|
+
},
|
|
499
|
+
template: `
|
|
500
|
+
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
501
|
+
<template #agent-message-content="{ message }">
|
|
502
|
+
<div style="display: flex; flex-direction: column; gap: 8px;">
|
|
503
|
+
<template v-for="part in message.content" :key="part.id">
|
|
504
|
+
<span v-if="part.type === 'reasoning'" style="font-size: 12px; font-style: italic; opacity: 0.6;">
|
|
505
|
+
Reasoning: {{ part.status }}
|
|
506
|
+
</span>
|
|
507
|
+
<span v-else-if="part.type === 'tool'" style="font-size: 12px; font-family: monospace; opacity: 0.7;">
|
|
508
|
+
⚙️ {{ part.name }}
|
|
509
|
+
</span>
|
|
510
|
+
<p v-else-if="part.type === 'text'" style="margin: 0;">{{ part.text }}</p>
|
|
511
|
+
</template>
|
|
512
|
+
</div>
|
|
513
|
+
</template>
|
|
514
|
+
</CkConversation>
|
|
515
|
+
`,
|
|
516
|
+
}),
|
|
517
|
+
parameters: { docs: { source: { code: chainOfThoughtTemplateSource } } },
|
|
518
|
+
};
|
|
519
|
+
|
|
331
520
|
/**
|
|
332
521
|
* Uses a remote avatar image (with fallback text). Use when you want a
|
|
333
522
|
* branded agent identity in each reply.
|
|
@@ -419,12 +608,17 @@ export const WithFooterActions: Story = {
|
|
|
419
608
|
function onFeedback(messageId: string, value: CkFeedbackValue) {
|
|
420
609
|
feedback.value[messageId] = { value, locked: value !== null };
|
|
421
610
|
}
|
|
422
|
-
return {
|
|
611
|
+
return {
|
|
612
|
+
messages: sampleMessages,
|
|
613
|
+
feedback,
|
|
614
|
+
onFeedback,
|
|
615
|
+
getMessageText,
|
|
616
|
+
};
|
|
423
617
|
},
|
|
424
618
|
template: `
|
|
425
619
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
426
620
|
<template #agent-message-footer="{ message }">
|
|
427
|
-
<CkCopyMessageActionButton :text="message
|
|
621
|
+
<CkCopyMessageActionButton :text="getMessageText(message)" />
|
|
428
622
|
<CkFeedbackButton
|
|
429
623
|
:value="feedback[message.id]?.value ?? null"
|
|
430
624
|
:locked="feedback[message.id]?.locked ?? false"
|
|
@@ -445,12 +639,12 @@ export const WithCustomUserContent: Story = {
|
|
|
445
639
|
render: () => ({
|
|
446
640
|
components: { CkConversation },
|
|
447
641
|
setup() {
|
|
448
|
-
return { messages: sampleMessages };
|
|
642
|
+
return { messages: sampleMessages, getMessageText };
|
|
449
643
|
},
|
|
450
644
|
template: `
|
|
451
645
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
452
646
|
<template #user-message-content="{ message }">
|
|
453
|
-
<strong>{{ message
|
|
647
|
+
<strong>{{ getMessageText(message) }}</strong>
|
|
454
648
|
</template>
|
|
455
649
|
</CkConversation>
|
|
456
650
|
`,
|
|
@@ -469,12 +663,12 @@ export const WithCustomAgentContent: Story = {
|
|
|
469
663
|
render: () => ({
|
|
470
664
|
components: { CkConversation },
|
|
471
665
|
setup() {
|
|
472
|
-
return { messages: sampleMessages };
|
|
666
|
+
return { messages: sampleMessages, getMessageText };
|
|
473
667
|
},
|
|
474
668
|
template: `
|
|
475
669
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
476
670
|
<template #agent-message-content="{ message }">
|
|
477
|
-
<div style="white-space: pre-wrap; font-family: monospace; font-size: 13px;">{{ message
|
|
671
|
+
<div style="white-space: pre-wrap; font-family: monospace; font-size: 13px;">{{ getMessageText(message) }}</div>
|
|
478
672
|
</template>
|
|
479
673
|
</CkConversation>
|
|
480
674
|
`,
|
|
@@ -497,14 +691,14 @@ export const StreamingSimulation: Story = {
|
|
|
497
691
|
{
|
|
498
692
|
id: '1',
|
|
499
693
|
role: 'user',
|
|
500
|
-
content: 'Hello!',
|
|
694
|
+
content: [text('Hello!')],
|
|
501
695
|
createdAt: now,
|
|
502
696
|
status: 'completed',
|
|
503
697
|
},
|
|
504
698
|
{
|
|
505
699
|
id: '2',
|
|
506
700
|
role: 'assistant',
|
|
507
|
-
content: 'Hi there!',
|
|
701
|
+
content: [text('Hi there!')],
|
|
508
702
|
createdAt: now,
|
|
509
703
|
status: 'completed',
|
|
510
704
|
},
|
|
@@ -513,6 +707,7 @@ export const StreamingSimulation: Story = {
|
|
|
513
707
|
let streamInterval: ReturnType<typeof setInterval> | null = null;
|
|
514
708
|
let charIndex = 0;
|
|
515
709
|
let nextId = 3;
|
|
710
|
+
let streamingTextId = '';
|
|
516
711
|
|
|
517
712
|
function streamNextChunk(): void {
|
|
518
713
|
if (charIndex >= markdownContent.length) {
|
|
@@ -523,20 +718,38 @@ export const StreamingSimulation: Story = {
|
|
|
523
718
|
const chunkSize = Math.floor(Math.random() * 5) + 3;
|
|
524
719
|
charIndex = Math.min(charIndex + chunkSize, markdownContent.length);
|
|
525
720
|
const lastIdx = messages.value.length - 1;
|
|
721
|
+
const completed = charIndex >= markdownContent.length;
|
|
722
|
+
const content: MessageContent[] = [
|
|
723
|
+
{
|
|
724
|
+
type: 'text',
|
|
725
|
+
id: streamingTextId,
|
|
726
|
+
text: markdownContent.slice(0, charIndex),
|
|
727
|
+
status: completed ? 'completed' : 'in_progress',
|
|
728
|
+
},
|
|
729
|
+
];
|
|
526
730
|
messages.value[lastIdx] = {
|
|
527
731
|
...messages.value[lastIdx],
|
|
528
|
-
content
|
|
732
|
+
content,
|
|
733
|
+
status: completed ? 'completed' : 'sending',
|
|
529
734
|
};
|
|
530
735
|
}
|
|
531
736
|
|
|
532
737
|
function startStreaming(): void {
|
|
533
738
|
const id = String(nextId++);
|
|
739
|
+
streamingTextId = `txt-stream-${id}`;
|
|
534
740
|
messages.value = [
|
|
535
741
|
...messages.value,
|
|
536
742
|
{
|
|
537
743
|
id,
|
|
538
744
|
role: 'assistant',
|
|
539
|
-
content:
|
|
745
|
+
content: [
|
|
746
|
+
{
|
|
747
|
+
type: 'text',
|
|
748
|
+
id: streamingTextId,
|
|
749
|
+
text: '',
|
|
750
|
+
status: 'in_progress',
|
|
751
|
+
},
|
|
752
|
+
],
|
|
540
753
|
createdAt: new Date(),
|
|
541
754
|
status: 'sending',
|
|
542
755
|
},
|
|
@@ -552,8 +765,11 @@ export const StreamingSimulation: Story = {
|
|
|
552
765
|
{
|
|
553
766
|
id,
|
|
554
767
|
role: 'user',
|
|
555
|
-
content:
|
|
556
|
-
|
|
768
|
+
content: [
|
|
769
|
+
text(
|
|
770
|
+
'Tell me about Digital Transformation Phase 1 lessons learned.',
|
|
771
|
+
),
|
|
772
|
+
],
|
|
557
773
|
createdAt: new Date(),
|
|
558
774
|
status: 'completed',
|
|
559
775
|
},
|
|
@@ -616,7 +832,7 @@ export const WithCustomCodeBlock: Story = {
|
|
|
616
832
|
{
|
|
617
833
|
id: '1',
|
|
618
834
|
role: 'user',
|
|
619
|
-
content: 'Show me a code example',
|
|
835
|
+
content: [text('Show me a code example')],
|
|
620
836
|
createdAt: now,
|
|
621
837
|
status: 'completed',
|
|
622
838
|
},
|
|
@@ -624,20 +840,24 @@ export const WithCustomCodeBlock: Story = {
|
|
|
624
840
|
id: '2',
|
|
625
841
|
role: 'assistant',
|
|
626
842
|
content: [
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
843
|
+
text(
|
|
844
|
+
[
|
|
845
|
+
'Here is an example:',
|
|
846
|
+
'',
|
|
847
|
+
'```typescript',
|
|
848
|
+
'const greeting = "Hello, world!";',
|
|
849
|
+
'console.log(greeting);',
|
|
850
|
+
'```',
|
|
851
|
+
'',
|
|
852
|
+
'And another one:',
|
|
853
|
+
'',
|
|
854
|
+
'```python',
|
|
855
|
+
'def greet(name: str) -> str:',
|
|
856
|
+
' return f"Hello, {name}!"',
|
|
857
|
+
'```',
|
|
858
|
+
].join('\n'),
|
|
859
|
+
),
|
|
860
|
+
],
|
|
641
861
|
createdAt: now,
|
|
642
862
|
status: 'completed',
|
|
643
863
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { useChatKitLabels } from '../../../handlers/useChatKitLabels';
|
|
3
|
-
import type { BaseMessage } from '../../../types';
|
|
3
|
+
import type { BaseMessage, MessageTextContent } from '../../../types';
|
|
4
4
|
import CkAgentMessage from '../../base/CkAgentMessage/CkAgentMessage.vue';
|
|
5
5
|
import CkConversationLayout from '../../base/CkConversationLayout/CkConversationLayout.vue';
|
|
6
6
|
import CkCopyMessageActionButton from '../../base/CkCopyMessageActionButton/CkCopyMessageActionButton.vue';
|
|
@@ -36,7 +36,11 @@ const slots = defineSlots<{
|
|
|
36
36
|
'agent-message-loading-icon': (props: { message: BaseMessage }) => void;
|
|
37
37
|
/** Replace the agent message header */
|
|
38
38
|
'agent-message-header': (props: { message: BaseMessage }) => void;
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Replace the agent message body. Receives the full `BaseMessage` so the
|
|
41
|
+
* consumer can inspect every content part (text, tool calls, reasoning)
|
|
42
|
+
* and render them however they want.
|
|
43
|
+
*/
|
|
40
44
|
'agent-message-content': (props: { message: BaseMessage }) => void;
|
|
41
45
|
/** Provide footer actions for the agent message */
|
|
42
46
|
'agent-message-footer': (props: { message: BaseMessage }) => void;
|
|
@@ -59,6 +63,14 @@ const hasIcon = computed(
|
|
|
59
63
|
|
|
60
64
|
const isLoading = (message: BaseMessage) =>
|
|
61
65
|
message.status === 'started' || message.status === 'sending';
|
|
66
|
+
|
|
67
|
+
/** Concatenated text from every text content part in a message. */
|
|
68
|
+
function getMessageText(message: BaseMessage): string {
|
|
69
|
+
return message.content
|
|
70
|
+
.filter((c): c is MessageTextContent => c.type === 'text')
|
|
71
|
+
.map((c) => c.text)
|
|
72
|
+
.join('');
|
|
73
|
+
}
|
|
62
74
|
</script>
|
|
63
75
|
|
|
64
76
|
<template>
|
|
@@ -66,9 +78,9 @@ const isLoading = (message: BaseMessage) =>
|
|
|
66
78
|
<template #user-message="{ message }">
|
|
67
79
|
<div :class="{ 'ml-8': hasIcon }">
|
|
68
80
|
<slot name="user-message" :message="message">
|
|
69
|
-
<CkUserMessage
|
|
81
|
+
<CkUserMessage>
|
|
70
82
|
<slot name="user-message-content" :message="message">
|
|
71
|
-
{{ message
|
|
83
|
+
{{ getMessageText(message) }}
|
|
72
84
|
</slot>
|
|
73
85
|
</CkUserMessage>
|
|
74
86
|
</slot>
|
|
@@ -117,7 +129,7 @@ const isLoading = (message: BaseMessage) =>
|
|
|
117
129
|
|
|
118
130
|
<slot name="agent-message-content" :message="message">
|
|
119
131
|
<CkMarkdownRenderer
|
|
120
|
-
:content="message
|
|
132
|
+
:content="getMessageText(message)"
|
|
121
133
|
:classes="agentMarkdownClasses"
|
|
122
134
|
>
|
|
123
135
|
<template
|
|
@@ -132,8 +144,8 @@ const isLoading = (message: BaseMessage) =>
|
|
|
132
144
|
<template #footer>
|
|
133
145
|
<slot name="agent-message-footer" :message="message">
|
|
134
146
|
<CkCopyMessageActionButton
|
|
135
|
-
v-if="message
|
|
136
|
-
:text="message
|
|
147
|
+
v-if="getMessageText(message) && !isLoading(message)"
|
|
148
|
+
:text="getMessageText(message)"
|
|
137
149
|
:aria-label-copy="labels.copyMessageAction"
|
|
138
150
|
:aria-label-copied="labels.copyMessageActionCopied"
|
|
139
151
|
/>
|