@aleph-alpha/chat-kit 1.2.0 → 1.3.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-CRymGkQs.js → CkInput.vue_vue_type_script_setup_true_lang-BoUxOQbH.js} +154 -39
- package/dist/chat-kit.css +18 -0
- package/dist/components/base/CkFeedbackButton/CkFeedbackButton.stories.d.ts +38 -0
- package/dist/components/base/CkFeedbackButton/CkFeedbackButton.stories.d.ts.map +1 -0
- package/dist/components/base/CkFeedbackButton/CkFeedbackButton.vue.d.ts +16 -0
- package/dist/components/base/CkFeedbackButton/CkFeedbackButton.vue.d.ts.map +1 -0
- package/dist/components/base/CkFeedbackButton/index.d.ts +3 -0
- package/dist/components/base/CkFeedbackButton/index.d.ts.map +1 -0
- package/dist/components/base/CkFeedbackButton/types.d.ts +60 -0
- package/dist/components/base/CkFeedbackButton/types.d.ts.map +1 -0
- package/dist/components/base/index.d.ts +2 -0
- package/dist/components/base/index.d.ts.map +1 -1
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +4 -3
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts.map +1 -1
- package/dist/components/index.d.ts +2 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +15 -14
- package/dist/handlers/index.js +1 -1
- package/dist/handlers/useChatKitLabels.d.ts +18 -0
- package/dist/handlers/useChatKitLabels.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -15
- package/dist/{useChatKitLabels-BmWmMyqX.js → useChatKitLabels-ftC52m7f.js} +3 -0
- package/package.json +2 -2
- package/src/components/base/CkFeedbackButton/CkFeedbackButton.spec.ts +174 -0
- package/src/components/base/CkFeedbackButton/CkFeedbackButton.stories.ts +179 -0
- package/src/components/base/CkFeedbackButton/CkFeedbackButton.vue +157 -0
- package/src/components/base/CkFeedbackButton/index.ts +2 -0
- package/src/components/base/CkFeedbackButton/types.ts +61 -0
- package/src/components/base/index.ts +5 -0
- package/src/components/composed/CkConversation/CkConversation.stories.ts +46 -17
- package/src/components/index.ts +3 -0
- package/src/handlers/useChatKitLabels.spec.ts +70 -50
- package/src/handlers/useChatKitLabels.ts +21 -0
- package/src/index.ts +3 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { CkCopyMessageActionButton } from '../../base/CkCopyMessageActionButton';
|
|
2
|
+
import { CkFeedbackButton } from '../../base/CkFeedbackButton';
|
|
3
|
+
import type { CkFeedbackValue } from '../../base/CkFeedbackButton';
|
|
1
4
|
import type { BaseMessage } from '../../../types';
|
|
2
5
|
import CkConversation from './CkConversation.vue';
|
|
3
6
|
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
@@ -169,19 +172,32 @@ const messages: BaseMessage[] = [/* ... */];
|
|
|
169
172
|
</template>`;
|
|
170
173
|
|
|
171
174
|
const withFooterActionsTemplateSource = `<script setup lang="ts">
|
|
172
|
-
import {
|
|
173
|
-
import
|
|
175
|
+
import { ref } from 'vue';
|
|
176
|
+
import {
|
|
177
|
+
CkConversation,
|
|
178
|
+
CkCopyMessageActionButton,
|
|
179
|
+
CkFeedbackButton,
|
|
180
|
+
} from '@aleph-alpha/chat-kit';
|
|
181
|
+
import type { BaseMessage, CkFeedbackValue } from '@aleph-alpha/chat-kit';
|
|
174
182
|
|
|
175
183
|
const messages: BaseMessage[] = [/* ... */];
|
|
184
|
+
|
|
185
|
+
// In a real app, key feedback by message id and persist to your backend.
|
|
186
|
+
const feedback = ref<Record<string, { value: CkFeedbackValue; locked: boolean }>>({});
|
|
187
|
+
function onFeedback(messageId: string, value: CkFeedbackValue) {
|
|
188
|
+
feedback.value[messageId] = { value, locked: value !== null };
|
|
189
|
+
}
|
|
176
190
|
</script>
|
|
177
191
|
|
|
178
192
|
<template>
|
|
179
193
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
180
|
-
<template #agent-message-footer>
|
|
181
|
-
<
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
194
|
+
<template #agent-message-footer="{ message }">
|
|
195
|
+
<CkCopyMessageActionButton :text="message.content" />
|
|
196
|
+
<CkFeedbackButton
|
|
197
|
+
:value="feedback[message.id]?.value ?? null"
|
|
198
|
+
:locked="feedback[message.id]?.locked ?? false"
|
|
199
|
+
@update:value="(v) => onFeedback(message.id, v)"
|
|
200
|
+
/>
|
|
185
201
|
</template>
|
|
186
202
|
</CkConversation>
|
|
187
203
|
</template>`;
|
|
@@ -384,23 +400,36 @@ export const WithCustomIcon: Story = {
|
|
|
384
400
|
};
|
|
385
401
|
|
|
386
402
|
/**
|
|
387
|
-
* Overrides `#agent-message-footer` to add
|
|
388
|
-
*
|
|
389
|
-
*
|
|
403
|
+
* Overrides `#agent-message-footer` to add real action buttons next to each
|
|
404
|
+
* assistant reply: `CkCopyMessageActionButton` for clipboard copy and
|
|
405
|
+
* `CkFeedbackButton` for binary thumbs up / thumbs down feedback. The
|
|
406
|
+
* feedback state is owned by the host and locked once submitted.
|
|
390
407
|
*/
|
|
391
408
|
export const WithFooterActions: Story = {
|
|
392
409
|
render: () => ({
|
|
393
|
-
components: {
|
|
410
|
+
components: {
|
|
411
|
+
CkConversation,
|
|
412
|
+
CkCopyMessageActionButton,
|
|
413
|
+
CkFeedbackButton,
|
|
414
|
+
},
|
|
394
415
|
setup() {
|
|
395
|
-
|
|
416
|
+
const feedback = ref<
|
|
417
|
+
Record<string, { value: CkFeedbackValue; locked: boolean }>
|
|
418
|
+
>({});
|
|
419
|
+
function onFeedback(messageId: string, value: CkFeedbackValue) {
|
|
420
|
+
feedback.value[messageId] = { value, locked: value !== null };
|
|
421
|
+
}
|
|
422
|
+
return { messages: sampleMessages, feedback, onFeedback };
|
|
396
423
|
},
|
|
397
424
|
template: `
|
|
398
425
|
<CkConversation :messages="messages" agent-avatar-fallback="AI" style="height: 100%;">
|
|
399
|
-
<template #agent-message-footer>
|
|
400
|
-
<
|
|
401
|
-
<
|
|
402
|
-
|
|
403
|
-
|
|
426
|
+
<template #agent-message-footer="{ message }">
|
|
427
|
+
<CkCopyMessageActionButton :text="message.content" />
|
|
428
|
+
<CkFeedbackButton
|
|
429
|
+
:value="feedback[message.id]?.value ?? null"
|
|
430
|
+
:locked="feedback[message.id]?.locked ?? false"
|
|
431
|
+
@update:value="(v) => onFeedback(message.id, v)"
|
|
432
|
+
/>
|
|
404
433
|
</template>
|
|
405
434
|
</CkConversation>
|
|
406
435
|
`,
|
package/src/components/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export {
|
|
|
3
3
|
CkAgentMessage,
|
|
4
4
|
CkConversationLayout,
|
|
5
5
|
CkCopyMessageActionButton,
|
|
6
|
+
CkFeedbackButton,
|
|
6
7
|
CkHistoryItem,
|
|
7
8
|
CkHistoryWrapper,
|
|
8
9
|
CkMarkdownRenderer,
|
|
@@ -17,6 +18,8 @@ export {
|
|
|
17
18
|
} from './base';
|
|
18
19
|
export type {
|
|
19
20
|
CkConversationLayoutProps,
|
|
21
|
+
CkFeedbackButtonProps,
|
|
22
|
+
CkFeedbackValue,
|
|
20
23
|
CkHistoryItemEmits,
|
|
21
24
|
CkHistoryItemProps,
|
|
22
25
|
CkHistoryWrapperProps,
|
|
@@ -40,6 +40,9 @@ describe('useChatKitLabels', () => {
|
|
|
40
40
|
inputPlaceholder: 'Nachricht eingeben...',
|
|
41
41
|
copyMessageAction: 'Nachricht kopieren',
|
|
42
42
|
copyMessageActionCopied: 'Kopiert',
|
|
43
|
+
feedbackThumbsUp: 'Gute Antwort',
|
|
44
|
+
feedbackThumbsDown: 'Schlechte Antwort',
|
|
45
|
+
feedbackGroup: 'Antwort bewerten',
|
|
43
46
|
historyItemUntitled: 'Unbenannte Konversation',
|
|
44
47
|
historyItemActionAriaLabel: 'Konversationsoptionen',
|
|
45
48
|
historyItemEditAction: 'Bearbeiten',
|
|
@@ -108,6 +111,9 @@ describe('useChatKitLabels', () => {
|
|
|
108
111
|
inputPlaceholder: 'Custom placeholder',
|
|
109
112
|
copyMessageAction: 'Custom copy',
|
|
110
113
|
copyMessageActionCopied: 'Custom copied',
|
|
114
|
+
feedbackThumbsUp: 'Custom up',
|
|
115
|
+
feedbackThumbsDown: 'Custom down',
|
|
116
|
+
feedbackGroup: 'Custom group',
|
|
111
117
|
historyItemUntitled: 'Custom untitled',
|
|
112
118
|
historyItemActionAriaLabel: 'Custom options',
|
|
113
119
|
historyItemEditAction: 'Custom edit',
|
|
@@ -236,55 +242,69 @@ describe('useChatKitLabels', () => {
|
|
|
236
242
|
});
|
|
237
243
|
});
|
|
238
244
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
render(CkConversation, {
|
|
245
|
-
props: {
|
|
246
|
-
messages: [
|
|
247
|
-
{
|
|
248
|
-
id: '1',
|
|
249
|
-
role: 'assistant',
|
|
250
|
-
content: 'Hi',
|
|
251
|
-
createdAt: new Date(),
|
|
252
|
-
status: 'completed',
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
agentAvatarImage: 'bot.png',
|
|
256
|
-
},
|
|
257
|
-
});
|
|
245
|
+
// These two tests dynamically import CkConversation, which transitively pulls
|
|
246
|
+
// in the markdown renderer + ui-library tree. First-time module load + jsdom
|
|
247
|
+
// render can take ~5s under suite-wide load (vitest's default `testTimeout`),
|
|
248
|
+
// so we bump the per-test timeout to keep them stable on slower CI runners.
|
|
249
|
+
const CK_CONVERSATION_RENDER_TIMEOUT_MS = 15_000;
|
|
258
250
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
251
|
+
describe('CkConversation uses injected labels', () => {
|
|
252
|
+
it(
|
|
253
|
+
'uses default agentAvatarAlt when no labels are provided',
|
|
254
|
+
async () => {
|
|
255
|
+
const { default: CkConversation } =
|
|
256
|
+
await import('../components/composed/CkConversation/CkConversation.vue');
|
|
257
|
+
|
|
258
|
+
render(CkConversation, {
|
|
259
|
+
props: {
|
|
260
|
+
messages: [
|
|
261
|
+
{
|
|
262
|
+
id: '1',
|
|
263
|
+
role: 'assistant',
|
|
264
|
+
content: 'Hi',
|
|
265
|
+
createdAt: new Date(),
|
|
266
|
+
status: 'completed',
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
agentAvatarImage: 'bot.png',
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(screen.getByAltText('Agent avatar')).toBeInTheDocument();
|
|
274
|
+
},
|
|
275
|
+
CK_CONVERSATION_RENDER_TIMEOUT_MS,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
it(
|
|
279
|
+
'uses injected agentAvatarAlt from provideChatKitLabels',
|
|
280
|
+
async () => {
|
|
281
|
+
const { default: CkConversation } =
|
|
282
|
+
await import('../components/composed/CkConversation/CkConversation.vue');
|
|
283
|
+
|
|
284
|
+
const Wrapper = defineComponent({
|
|
285
|
+
setup() {
|
|
286
|
+
provideChatKitLabels(() => ({ agentAvatarAlt: 'Bot-Bild' }));
|
|
287
|
+
return () =>
|
|
288
|
+
h(CkConversation, {
|
|
289
|
+
messages: [
|
|
290
|
+
{
|
|
291
|
+
id: '1',
|
|
292
|
+
role: 'assistant',
|
|
293
|
+
content: 'Hi',
|
|
294
|
+
createdAt: new Date(),
|
|
295
|
+
status: 'completed',
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
agentAvatarImage: 'bot.png',
|
|
299
|
+
});
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
render(Wrapper);
|
|
304
|
+
|
|
305
|
+
expect(screen.getByAltText('Bot-Bild')).toBeInTheDocument();
|
|
306
|
+
expect(screen.queryByAltText('Agent avatar')).not.toBeInTheDocument();
|
|
307
|
+
},
|
|
308
|
+
CK_CONVERSATION_RENDER_TIMEOUT_MS,
|
|
309
|
+
);
|
|
290
310
|
});
|
|
@@ -29,6 +29,24 @@ export type ChatKitLabels = {
|
|
|
29
29
|
copyMessageAction: string;
|
|
30
30
|
/** Accessible label / tooltip for the copy-message action button after a successful copy */
|
|
31
31
|
copyMessageActionCopied: string;
|
|
32
|
+
/**
|
|
33
|
+
* Accessible label / tooltip for the thumbs-up button in `CkFeedbackButton`.
|
|
34
|
+
* Optional for backward compatibility; falls back to
|
|
35
|
+
* `defaultChatKitLabels.feedbackThumbsUp` when omitted.
|
|
36
|
+
*/
|
|
37
|
+
feedbackThumbsUp?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Accessible label / tooltip for the thumbs-down button in `CkFeedbackButton`.
|
|
40
|
+
* Optional for backward compatibility; falls back to
|
|
41
|
+
* `defaultChatKitLabels.feedbackThumbsDown` when omitted.
|
|
42
|
+
*/
|
|
43
|
+
feedbackThumbsDown?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Accessible group label announced when focus enters `CkFeedbackButton`.
|
|
46
|
+
* Optional for backward compatibility; falls back to
|
|
47
|
+
* `defaultChatKitLabels.feedbackGroup` when omitted.
|
|
48
|
+
*/
|
|
49
|
+
feedbackGroup?: string;
|
|
32
50
|
/** Fallback title used by `CkHistory` when a conversation has no `title`. */
|
|
33
51
|
historyItemUntitled: string;
|
|
34
52
|
/** Accessible name for the trailing kebab trigger on each `CkHistory` row. */
|
|
@@ -46,6 +64,9 @@ export const defaultChatKitLabels: ChatKitLabels = {
|
|
|
46
64
|
inputPlaceholder: 'Type a message...',
|
|
47
65
|
copyMessageAction: 'Copy response',
|
|
48
66
|
copyMessageActionCopied: 'Copied!',
|
|
67
|
+
feedbackThumbsUp: 'Good response',
|
|
68
|
+
feedbackThumbsDown: 'Bad response',
|
|
69
|
+
feedbackGroup: 'Rate this response',
|
|
49
70
|
historyItemUntitled: 'Untitled conversation',
|
|
50
71
|
historyItemActionAriaLabel: 'Conversation options',
|
|
51
72
|
historyItemEditAction: 'Edit',
|
package/src/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ export {
|
|
|
48
48
|
CkConversation,
|
|
49
49
|
CkConversationLayout,
|
|
50
50
|
CkCopyMessageActionButton,
|
|
51
|
+
CkFeedbackButton,
|
|
51
52
|
CkHistory,
|
|
52
53
|
CkHistoryItem,
|
|
53
54
|
CkHistoryWrapper,
|
|
@@ -65,6 +66,8 @@ export {
|
|
|
65
66
|
export type {
|
|
66
67
|
CkConversationLayoutProps,
|
|
67
68
|
CkConversationProps,
|
|
69
|
+
CkFeedbackButtonProps,
|
|
70
|
+
CkFeedbackValue,
|
|
68
71
|
CkHistoryEmits,
|
|
69
72
|
CkHistoryItemEmits,
|
|
70
73
|
CkHistoryItemProps,
|