@aleph-alpha/chat-kit 5.8.0 → 5.10.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/{CkHistory.vue_vue_type_script_setup_true_lang-DG96wNug.js → CkHistory.vue_vue_type_script_setup_true_lang-CEqJtMuI.js} +61 -40
- package/dist/adapters/responses-api.d.ts +1 -3
- package/dist/adapters/responses-api.d.ts.map +1 -1
- package/dist/adapters/responses-api.js +55 -65
- package/dist/chat-kit.css +1 -1
- package/dist/components/base/CkConversationLayout/CkConversationLayout.stories.d.ts +7 -0
- package/dist/components/base/CkConversationLayout/CkConversationLayout.stories.d.ts.map +1 -1
- package/dist/components/base/CkConversationLayout/CkConversationLayout.vue.d.ts +3 -1
- package/dist/components/base/CkConversationLayout/CkConversationLayout.vue.d.ts.map +1 -1
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +23 -3
- package/dist/components/composed/CkConversation/CkConversation.stories.d.ts.map +1 -1
- package/dist/components/composed/CkConversation/CkConversation.vue.d.ts +17 -2
- package/dist/components/composed/CkConversation/CkConversation.vue.d.ts.map +1 -1
- package/dist/components/composed/CkHistory/CkHistory.vue.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/adapters/arena.spec.ts +12 -18
- package/src/adapters/responses-api.spec.ts +6 -3
- package/src/adapters/responses-api.ts +80 -86
- package/src/components/base/CkConversationLayout/CkConversationLayout.spec.ts +209 -1
- package/src/components/base/CkConversationLayout/CkConversationLayout.stories.ts +84 -0
- package/src/components/base/CkConversationLayout/CkConversationLayout.vue +59 -21
- package/src/components/composed/CkConversation/CkConversation.spec.ts +96 -5
- package/src/components/composed/CkConversation/CkConversation.stories.ts +328 -4
- package/src/components/composed/CkConversation/CkConversation.vue +38 -8
|
@@ -15,6 +15,13 @@ export declare const Default: Story;
|
|
|
15
15
|
* Use when streaming tokens from an LLM into the last agent message.
|
|
16
16
|
*/
|
|
17
17
|
export declare const StreamingSimulation: Story;
|
|
18
|
+
/**
|
|
19
|
+
* Programmatic navigation via the exposed `scrollToMessage(index)` method. Enter
|
|
20
|
+
* a zero-based row index and the layout scrolls that message to the top. Index
|
|
21
|
+
* counts all rows regardless of role (a `thread-footer`, when present, is the
|
|
22
|
+
* trailing row); omitting the index scrolls to the last row.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ScrollToMessage: Story;
|
|
18
25
|
/**
|
|
19
26
|
* Empty state for when there are no messages. Use as an initial state before
|
|
20
27
|
* the first user turn, or when history has been cleared.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CkConversationLayout.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/base/CkConversationLayout/CkConversationLayout.stories.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAI3C,QAAA,MAAM,oBAAoB,EACoB,eAAe,CAAC;AAoB9D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,oBAAoB,CAU3C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"CkConversationLayout.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/base/CkConversationLayout/CkConversationLayout.stories.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAI3C,QAAA,MAAM,oBAAoB,EACoB,eAAe,CAAC;AAoB9D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,oBAAoB,CAU3C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AA6GnC;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,KAsBrB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAiFjC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,KA0C7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,KAenB,CAAC"}
|
|
@@ -7,7 +7,9 @@ declare const __VLS_export: <T extends {
|
|
|
7
7
|
}> & (typeof globalThis extends {
|
|
8
8
|
__VLS_PROPS_FALLBACK: infer P;
|
|
9
9
|
} ? P : {});
|
|
10
|
-
expose: (exposed: {
|
|
10
|
+
expose: (exposed: import("vue").ShallowUnwrapRef<{
|
|
11
|
+
scrollToMessage: (index?: number) => void;
|
|
12
|
+
}>) => void;
|
|
11
13
|
attrs: any;
|
|
12
14
|
slots: {
|
|
13
15
|
'user-message': (props: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CkConversationLayout.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/base/CkConversationLayout/CkConversationLayout.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CkConversationLayout.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/base/CkConversationLayout/CkConversationLayout.vue"],"names":[],"mappings":"AA+TA,QAAA,MAAM,YAAY,GAAK,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC5D,aAAa,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAC9D,YAAY,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,EAC3G,gBAAgB,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EAClE;WAwYO,OAAO,KAAK,EAAE,WAAW,GAAG,mBAAmB;kBApY3C,CAAC,EAAE;MAoY2D,GAAG,CAAC,OAAO,UAAU,SAAS;QAAE,oBAAoB,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,CAAC,GAAG,EAAE,CAAC;YAC1I,CAAC,OAAO,EAAE,OAAO,KAAK,EAAE,gBAAgB;kCAjPb,MAAM,KAAG,IAAI;MAiPsB,KAAK,IAAI;WACxE,GAAG;;wBAjYO,CAAC,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC,CAAA;SAAE,KAAK,IAAI;yBAC9B,CAAC,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC,CAAA;SAAE,KAAK,IAAI;QAChD;;;;WAIG;yBACc,MAAM,IAAI;;UA4XtB,EAAE;EAEL,KACQ,OAAO,KAAK,EAAE,KAAK,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC,OAAO,WAAW,CAAC,CAAA;CAAI,CAAC;wBACvD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,CAAC,GAAG,EAAE,CAAC"}
|
|
@@ -12,6 +12,13 @@ type Story = StoryObj<typeof meta>;
|
|
|
12
12
|
* `#agent-message-header-content`.
|
|
13
13
|
*/
|
|
14
14
|
export declare const Default: Story;
|
|
15
|
+
/**
|
|
16
|
+
* A diagram of how CkConversation's slots nest. Every slot has a default
|
|
17
|
+
* rendering; overriding a parent slot replaces everything nested beneath it,
|
|
18
|
+
* so the child slots below it no longer apply. Collapse a node to see which
|
|
19
|
+
* slots "fold away" when you take over its parent.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SlotAnatomy: Story;
|
|
15
22
|
/**
|
|
16
23
|
* Uses a remote avatar image (with fallback text). Use when you want a
|
|
17
24
|
* branded agent identity in each reply.
|
|
@@ -70,11 +77,24 @@ export declare const StreamingSimulation: Story;
|
|
|
70
77
|
*/
|
|
71
78
|
export declare const WithCustomMarkdownClasses: Story;
|
|
72
79
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
80
|
+
* Fenced code blocks in agent markdown render with `UiCodeBlock` out of the
|
|
81
|
+
* box — syntax highlighting, a language label, and a copy button — without any
|
|
82
|
+
* slot wiring.
|
|
83
|
+
*/
|
|
84
|
+
export declare const DefaultCodeBlock: Story;
|
|
85
|
+
/**
|
|
86
|
+
* Fenced code blocks render with `UiCodeBlock` (syntax highlighting + copy
|
|
87
|
+
* button) by default. Override that with a custom renderer (Prism, a bespoke
|
|
88
|
+
* theme, etc.) via `#agent-message-code-block`, as shown here.
|
|
76
89
|
*/
|
|
77
90
|
export declare const WithCustomCodeBlock: Story;
|
|
91
|
+
/**
|
|
92
|
+
* Programmatic navigation via the exposed `scrollToMessage(index)` method. Enter
|
|
93
|
+
* a zero-based row index and the conversation scrolls that message to the top.
|
|
94
|
+
* Index counts all rows regardless of role (a `thread-footer`, when present, is
|
|
95
|
+
* the trailing row); omitting the index scrolls to the last row.
|
|
96
|
+
*/
|
|
97
|
+
export declare const ScrollToMessage: Story;
|
|
78
98
|
/**
|
|
79
99
|
* Empty state — an initial view before the first turn. Use to show a welcome
|
|
80
100
|
* screen, quick prompts, or onboarding content above this surface.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CkConversation.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.stories.ts"],"names":[],"mappings":"AASA,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAwI3D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAkCrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"CkConversation.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.stories.ts"],"names":[],"mappings":"AASA,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAwI3D,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAkCrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AA6WnC;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,KAerB,CAAC;AA2HF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,KAgCzB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,KAYtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,KASpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,KAe5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,KAU7B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAmC/B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,KAiBnC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,KAiBpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KA4GjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,KAmBvC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,KA6C9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAoDjC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,KAsC7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,KAMnB,CAAC"}
|
|
@@ -49,7 +49,11 @@ type __VLS_Slots = {
|
|
|
49
49
|
'agent-message-footer': (props: {
|
|
50
50
|
message: BaseMessage;
|
|
51
51
|
}) => void;
|
|
52
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Replace the default code block rendering inside agent markdown. When not
|
|
54
|
+
* provided, fenced code blocks render with `UiCodeBlock` (syntax highlighting
|
|
55
|
+
* + copy button).
|
|
56
|
+
*/
|
|
53
57
|
'agent-message-code-block': (props: {
|
|
54
58
|
code: string;
|
|
55
59
|
language: string;
|
|
@@ -61,7 +65,18 @@ type __VLS_Slots = {
|
|
|
61
65
|
*/
|
|
62
66
|
'thread-footer': () => void;
|
|
63
67
|
};
|
|
64
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Scroll so the message at `index` sits at the top of the viewport. `index` is
|
|
70
|
+
* the zero-based position among the rendered rows (any `thread-footer` counts
|
|
71
|
+
* as the trailing row); when omitted it scrolls to the last row.
|
|
72
|
+
*
|
|
73
|
+
* This only performs the scroll — it does not focus the message. Any
|
|
74
|
+
* accompanying focus handling must be done separately by the caller.
|
|
75
|
+
*/
|
|
76
|
+
declare function scrollToMessage(index?: number): void;
|
|
77
|
+
declare const __VLS_base: import("vue").DefineComponent<CkConversationProps, {
|
|
78
|
+
scrollToMessage: typeof scrollToMessage;
|
|
79
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<CkConversationProps> & Readonly<{}>, {
|
|
65
80
|
agentAvatarImage: string;
|
|
66
81
|
agentAvatarFallback: string;
|
|
67
82
|
agentLoadingImage: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CkConversation.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CkConversation.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/composed/CkConversation/CkConversation.vue"],"names":[],"mappings":"AAqRA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,gBAAgB,CAAC;AAOtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA0BnD,KAAK,WAAW,GAAG;IACjB,gDAAgD;IAChD,cAAc,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1D,yDAAyD;IACzD,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,iDAAiD;IACjD,eAAe,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3D,oCAAoC;IACpC,oBAAoB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAChE,8EAA8E;IAC9E,4BAA4B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IACxE,iFAAiF;IACjF,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE,6EAA6E;IAC7E,8BAA8B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1E;;;;OAIG;IACH,+BAA+B,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3E;;;;OAIG;IACH,uBAAuB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IACnE,mDAAmD;IACnD,sBAAsB,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,IAAI,CAAC;IAClE;;;;OAIG;IACH,0BAA0B,EAAE,CAAC,KAAK,EAAE;QAClC,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,IAAI,CAAC;IACX;;;;OAIG;IACH,eAAe,EAAE,MAAM,IAAI,CAAC;CAC7B,CAAC;AA8DF;;;;;;;GAOG;AACH,iBAAS,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAE7C;AAkVD,QAAA,MAAM,UAAU;;;;;;6EAId,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AAWzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -40,10 +40,10 @@ declare const __VLS_base: import("vue").DefineComponent<CkHistoryProps, {}, {},
|
|
|
40
40
|
}>, {
|
|
41
41
|
showAction: boolean;
|
|
42
42
|
showActionOnHover: boolean;
|
|
43
|
+
maxHeight: string;
|
|
43
44
|
activeId: string;
|
|
44
45
|
timeFormatter: (updatedAtSeconds: number) => string;
|
|
45
46
|
collapsedLimit: number;
|
|
46
|
-
maxHeight: string;
|
|
47
47
|
contentClass: string;
|
|
48
48
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
49
49
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
package/dist/components/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _, a, C, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z } from "../CkHistory.vue_vue_type_script_setup_true_lang-
|
|
1
|
+
import { _, a, C, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z } from "../CkHistory.vue_vue_type_script_setup_true_lang-CEqJtMuI.js";
|
|
2
2
|
export {
|
|
3
3
|
_ as CkAgentMessage,
|
|
4
4
|
a as CkAgentMessageHeader,
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { u } from "./useMessageHandler-Tellov3w.js";
|
|
2
2
|
import { C, d, p, u as u2, b } from "./useChatKitLabels-CMOr3Kjq.js";
|
|
3
3
|
import { S, c, d as d2, f, u as u3, a, b as b2, e } from "./index-Dn4ctxk8.js";
|
|
4
|
-
import { _, a as a2, C as C2, b as b3, c as c2, d as d3, e as e2, f as f2, g, h, i, j, k, l, m, n, o, p as p2, q, r, s, t, u as u4, v, w, x, y, z, A } from "./CkHistory.vue_vue_type_script_setup_true_lang-
|
|
4
|
+
import { _, a as a2, C as C2, b as b3, c as c2, d as d3, e as e2, f as f2, g, h, i, j, k, l, m, n, o, p as p2, q, r, s, t, u as u4, v, w, x, y, z, A } from "./CkHistory.vue_vue_type_script_setup_true_lang-CEqJtMuI.js";
|
|
5
5
|
export {
|
|
6
6
|
C as CHAT_KIT_LABELS,
|
|
7
7
|
_ as CkAgentMessage,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aleph-alpha/chat-kit",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.0",
|
|
4
4
|
"description": "Chat kit package for building chat interfaces with Vue",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
"vue-tsc": "^3.2.4",
|
|
109
109
|
"wait-on": "9.0.3",
|
|
110
110
|
"@aleph-alpha/eslint-config-frontend": "0.7.0",
|
|
111
|
-
"@aleph-alpha/
|
|
112
|
-
"@aleph-alpha/
|
|
111
|
+
"@aleph-alpha/tsconfig-frontend": "0.7.1",
|
|
112
|
+
"@aleph-alpha/prettier-config-frontend": "2.1.0"
|
|
113
113
|
},
|
|
114
114
|
"scripts": {
|
|
115
115
|
"build": "vite build && vue-tsc --project tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist",
|
|
@@ -32,7 +32,6 @@ function arenaResponse(
|
|
|
32
32
|
matchup_id: matchupId,
|
|
33
33
|
slot,
|
|
34
34
|
model_id: modelId,
|
|
35
|
-
variant: 'king-of-the-hill',
|
|
36
35
|
}),
|
|
37
36
|
},
|
|
38
37
|
} as StoredResponse;
|
|
@@ -54,29 +53,24 @@ describe('responsesToTree arena collapse', () => {
|
|
|
54
53
|
|
|
55
54
|
const children = root.childrenIds.map((id) => tree.nodes[id]!);
|
|
56
55
|
expect(children.every((c) => c.role === 'assistant')).toBe(true);
|
|
57
|
-
//
|
|
56
|
+
// Ordered slot a then b.
|
|
58
57
|
const slots = children.map(
|
|
59
58
|
(c) => (c.metadata.arena as { slot?: string }).slot,
|
|
60
59
|
);
|
|
61
60
|
expect(slots).toEqual(['a', 'b']);
|
|
61
|
+
// Blind until the vote: no model label leaks (neither arena.modelId nor
|
|
62
|
+
// metadata.model), so the columns render "Response A/B".
|
|
63
|
+
for (const c of children) {
|
|
64
|
+
expect(
|
|
65
|
+
(c.metadata.arena as { modelId?: string }).modelId,
|
|
66
|
+
).toBeUndefined();
|
|
67
|
+
expect(c.metadata.model).toBeUndefined();
|
|
68
|
+
}
|
|
62
69
|
});
|
|
63
70
|
|
|
64
|
-
it('
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
arenaResponse('respA', 'm1', 'a', 'phoenix-latest', 'A'),
|
|
68
|
-
arenaResponse('respB', 'm1', 'b', 'kimi-k2.6', 'B'),
|
|
69
|
-
],
|
|
70
|
-
{ arenaWinners: { m1: 'respB' } },
|
|
71
|
-
);
|
|
72
|
-
const root = tree.nodes[tree.rootId!]!;
|
|
73
|
-
// Winner is respB's assistant node (its wire message id).
|
|
74
|
-
expect(root.comparisonWinnerId).toBe('msg-respB');
|
|
75
|
-
expect(root.childrenIds[root.activeChildIndex]).toBe('msg-respB');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('leaves a single surviving response as an ordinary linear turn', () => {
|
|
79
|
-
// 7-Discard: after continue, the BE dropped the loser — one response left.
|
|
71
|
+
it('leaves a single (server-collapsed resolved) response as a linear turn', () => {
|
|
72
|
+
// The backend collapses a resolved matchup to its winning branch, so a
|
|
73
|
+
// resolved pair arrives as a lone response — an ordinary linear turn.
|
|
80
74
|
const tree = responsesToTree([
|
|
81
75
|
arenaResponse('respA', 'm1', 'a', 'phoenix-latest', 'A'),
|
|
82
76
|
]);
|
|
@@ -456,9 +456,12 @@ describe('responsesToTree', () => {
|
|
|
456
456
|
},
|
|
457
457
|
],
|
|
458
458
|
});
|
|
459
|
-
// The assistant turn does not inherit attachments but still carries
|
|
460
|
-
//
|
|
461
|
-
expect(tree.nodes['msg-a1']!.metadata).toEqual({
|
|
459
|
+
// The assistant turn does not inherit attachments but still carries its
|
|
460
|
+
// responseId stamp and the response's model id.
|
|
461
|
+
expect(tree.nodes['msg-a1']!.metadata).toEqual({
|
|
462
|
+
responseId: 'r1',
|
|
463
|
+
model: 'pharia-1',
|
|
464
|
+
});
|
|
462
465
|
});
|
|
463
466
|
|
|
464
467
|
it('skips malformed metadata.attachments without polluting the tree', () => {
|
|
@@ -462,6 +462,53 @@ function parseResponseAttachments(
|
|
|
462
462
|
return attachments as ResponseAttachment[];
|
|
463
463
|
}
|
|
464
464
|
|
|
465
|
+
// Maps response-level attachments onto the shape stored in node metadata.
|
|
466
|
+
function toMessageAttachments(
|
|
467
|
+
attachments: ResponseAttachment[],
|
|
468
|
+
): ResponseMessageAttachment[] {
|
|
469
|
+
return attachments.map(
|
|
470
|
+
(a): ResponseMessageAttachment => ({
|
|
471
|
+
name: a.fileName,
|
|
472
|
+
fileId: a.fileId,
|
|
473
|
+
fileName: a.fileName,
|
|
474
|
+
searchStoreId: a.searchStoreId,
|
|
475
|
+
...(a.documentId ? { documentId: a.documentId } : {}),
|
|
476
|
+
}),
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Builds the user node for a single `request_input`, or null when the input is
|
|
481
|
+
// not a user turn / carries no visible content. Attachments bind to the first
|
|
482
|
+
// user node only (`isFirstUserNode`).
|
|
483
|
+
function buildUserNode(
|
|
484
|
+
resp: StoredResponse,
|
|
485
|
+
input: unknown,
|
|
486
|
+
idx: number,
|
|
487
|
+
responseAttachments: ResponseAttachment[] | null,
|
|
488
|
+
isFirstUserNode: boolean,
|
|
489
|
+
): ResponseMessageNode | null {
|
|
490
|
+
const role = (input as { role?: string }).role;
|
|
491
|
+
const text = extractInputText((input as { content?: unknown }).content);
|
|
492
|
+
const hasContent = text || responseAttachments?.length;
|
|
493
|
+
if (role !== 'user' || !hasContent) return null;
|
|
494
|
+
|
|
495
|
+
const metadata: ResponseTreeMessageMetadata = {
|
|
496
|
+
responseId: resp.id,
|
|
497
|
+
...(resp.model ? { model: resp.model } : {}),
|
|
498
|
+
};
|
|
499
|
+
if (responseAttachments && isFirstUserNode) {
|
|
500
|
+
metadata.attachments = toMessageAttachments(responseAttachments);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
return {
|
|
504
|
+
id: `user-${resp.id}-${idx}`,
|
|
505
|
+
role: 'user',
|
|
506
|
+
content: [textPart(text)],
|
|
507
|
+
createdAt: new Date(resp.created_at * 1000),
|
|
508
|
+
metadata,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
465
512
|
// Within a single response, the visible messages chain linearly: every user
|
|
466
513
|
// input is followed by exactly one assistant turn (whose reasoning, tool,
|
|
467
514
|
// and text outputs are merged into one node). Across responses, branching
|
|
@@ -473,40 +520,15 @@ function responseToMessageNodes(resp: StoredResponse): ResponseMessageNode[] {
|
|
|
473
520
|
// the user turn that started the response. We bind them to the first
|
|
474
521
|
// user `request_input` (mirroring the existing wire convention).
|
|
475
522
|
const responseAttachments = parseResponseAttachments(resp);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const metadata: ResponseTreeMessageMetadata = {
|
|
486
|
-
responseId: resp.id,
|
|
487
|
-
...(resp.model ? { model: resp.model } : {}),
|
|
488
|
-
};
|
|
489
|
-
if (responseAttachments && userNodesPushed === 0) {
|
|
490
|
-
metadata.attachments = responseAttachments.map(
|
|
491
|
-
(a): ResponseMessageAttachment => ({
|
|
492
|
-
name: a.fileName,
|
|
493
|
-
fileId: a.fileId,
|
|
494
|
-
fileName: a.fileName,
|
|
495
|
-
searchStoreId: a.searchStoreId,
|
|
496
|
-
...(a.documentId ? { documentId: a.documentId } : {}),
|
|
497
|
-
}),
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
nodes.push({
|
|
502
|
-
id: `user-${resp.id}-${idx}`,
|
|
503
|
-
role: 'user',
|
|
504
|
-
content: [textPart(text)],
|
|
505
|
-
createdAt: new Date(resp.created_at * 1000),
|
|
506
|
-
metadata,
|
|
507
|
-
});
|
|
508
|
-
userNodesPushed += 1;
|
|
509
|
-
}
|
|
523
|
+
for (const [idx, input] of resp.request_input?.entries() ?? []) {
|
|
524
|
+
const userNode = buildUserNode(
|
|
525
|
+
resp,
|
|
526
|
+
input,
|
|
527
|
+
idx,
|
|
528
|
+
responseAttachments,
|
|
529
|
+
nodes.length === 0,
|
|
530
|
+
);
|
|
531
|
+
if (userNode) nodes.push(userNode);
|
|
510
532
|
}
|
|
511
533
|
|
|
512
534
|
const assistantParts = toAssistantParts(resp);
|
|
@@ -516,7 +538,12 @@ function responseToMessageNodes(resp: StoredResponse): ResponseMessageNode[] {
|
|
|
516
538
|
role: 'assistant',
|
|
517
539
|
content: assistantParts,
|
|
518
540
|
createdAt: new Date((resp.completed_at ?? resp.created_at) * 1000),
|
|
519
|
-
|
|
541
|
+
// A stored response carries a single `model` covering both its input and
|
|
542
|
+
// output, so the assistant node gets the same model id as its user node.
|
|
543
|
+
metadata: {
|
|
544
|
+
responseId: resp.id,
|
|
545
|
+
...(resp.model ? { model: resp.model } : {}),
|
|
546
|
+
},
|
|
520
547
|
});
|
|
521
548
|
}
|
|
522
549
|
return nodes;
|
|
@@ -676,7 +703,6 @@ function linkResponse(state: TreeBuildState, resp: StoredResponse): void {
|
|
|
676
703
|
*/
|
|
677
704
|
export function responsesToTree(
|
|
678
705
|
responses: StoredResponse[],
|
|
679
|
-
opts?: { arenaWinners?: Record<string, string> },
|
|
680
706
|
): ResponseConversationTree {
|
|
681
707
|
if (responses.length === 0) {
|
|
682
708
|
return { nodes: {}, rootId: null };
|
|
@@ -696,7 +722,6 @@ export function responsesToTree(
|
|
|
696
722
|
const rootId = collapseArenaComparisons(
|
|
697
723
|
state.treeNodes,
|
|
698
724
|
responses,
|
|
699
|
-
opts?.arenaWinners ?? {},
|
|
700
725
|
state.rootMessageId,
|
|
701
726
|
state.userMessageByResponse,
|
|
702
727
|
);
|
|
@@ -708,8 +733,6 @@ interface ParsedArena {
|
|
|
708
733
|
matchupId: string;
|
|
709
734
|
modelId?: string;
|
|
710
735
|
slot?: string;
|
|
711
|
-
slotSeed?: string;
|
|
712
|
-
variant?: string;
|
|
713
736
|
}
|
|
714
737
|
|
|
715
738
|
/** Parse a stored response's `metadata.arena` (JSON string or dict). */
|
|
@@ -728,16 +751,12 @@ function parseArenaMeta(resp: StoredResponse): ParsedArena | null {
|
|
|
728
751
|
matchup_id?: unknown;
|
|
729
752
|
model_id?: unknown;
|
|
730
753
|
slot?: unknown;
|
|
731
|
-
slot_seed?: unknown;
|
|
732
|
-
variant?: unknown;
|
|
733
754
|
};
|
|
734
755
|
if (typeof a.matchup_id !== 'string') return null;
|
|
735
756
|
return {
|
|
736
757
|
matchupId: a.matchup_id,
|
|
737
758
|
modelId: typeof a.model_id === 'string' ? a.model_id : undefined,
|
|
738
759
|
slot: typeof a.slot === 'string' ? a.slot : undefined,
|
|
739
|
-
slotSeed: typeof a.slot_seed === 'string' ? a.slot_seed : undefined,
|
|
740
|
-
variant: typeof a.variant === 'string' ? a.variant : undefined,
|
|
741
760
|
};
|
|
742
761
|
}
|
|
743
762
|
|
|
@@ -749,18 +768,17 @@ function parseArenaMeta(resp: StoredResponse): ParsedArena | null {
|
|
|
749
768
|
* collapses those duplicates so reload matches the live shape (one prompt, N
|
|
750
769
|
* lanes) that {@link projectActivePath} renders.
|
|
751
770
|
*
|
|
752
|
-
* Only pairs still present as ≥2 responses are folded
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
*
|
|
771
|
+
* Only pairs still present as ≥2 responses are folded, which now means only an
|
|
772
|
+
* unresolved (pending) pair: the backend collapses a resolved matchup to its
|
|
773
|
+
* winning branch server-side, so a resolved pair arrives as a single response
|
|
774
|
+
* and is left as an ordinary linear turn. A folded pending pair renders blind
|
|
775
|
+
* (no winner marked) until the user votes.
|
|
757
776
|
*
|
|
758
777
|
* Returns the (possibly updated) root id. Best-effort per group.
|
|
759
778
|
*/
|
|
760
779
|
function collapseArenaComparisons(
|
|
761
780
|
nodes: Record<string, ResponseTreeMessage>,
|
|
762
781
|
responses: StoredResponse[],
|
|
763
|
-
arenaWinners: Record<string, string>,
|
|
764
782
|
rootMessageId: string | null,
|
|
765
783
|
userMessageByResponse: Map<string, string>,
|
|
766
784
|
): string | null {
|
|
@@ -770,18 +788,12 @@ function collapseArenaComparisons(
|
|
|
770
788
|
};
|
|
771
789
|
|
|
772
790
|
let root = rootMessageId;
|
|
773
|
-
for (const [
|
|
774
|
-
// Fewer than two survivors is no comparison: a
|
|
775
|
-
//
|
|
791
|
+
for (const [, group] of groupResponsesByMatchup(responses)) {
|
|
792
|
+
// Fewer than two survivors is no comparison: a resolved matchup is already
|
|
793
|
+
// collapsed to its winner server-side, so it arrives as a lone response.
|
|
776
794
|
if (group.length < 2) continue;
|
|
777
795
|
try {
|
|
778
|
-
root = foldArenaGroup(
|
|
779
|
-
nodes,
|
|
780
|
-
group,
|
|
781
|
-
arenaWinners[matchupId],
|
|
782
|
-
userNodeFor,
|
|
783
|
-
root,
|
|
784
|
-
);
|
|
796
|
+
root = foldArenaGroup(nodes, group, userNodeFor, root);
|
|
785
797
|
} catch {
|
|
786
798
|
// Leave this group as-is on any structural surprise.
|
|
787
799
|
}
|
|
@@ -831,7 +843,12 @@ function stampLaneAssistant(
|
|
|
831
843
|
const meta = parseArenaMeta(resp);
|
|
832
844
|
const assistant = nodes[getAssistantNodeId(resp)];
|
|
833
845
|
if (assistant && meta) {
|
|
834
|
-
|
|
846
|
+
// A folded pair is always still pending (resolved matchups arrive collapsed
|
|
847
|
+
// server-side), so it renders BLIND until the user votes: keep only `slot`
|
|
848
|
+
// for column order and drop any model label so the lane shows "Response
|
|
849
|
+
// A/B", not the model name. The vote response reveals the real names.
|
|
850
|
+
assistant.metadata.arena = { slot: meta.slot };
|
|
851
|
+
delete assistant.metadata.model;
|
|
835
852
|
}
|
|
836
853
|
}
|
|
837
854
|
|
|
@@ -886,23 +903,6 @@ function activateForkNode(
|
|
|
886
903
|
return root;
|
|
887
904
|
}
|
|
888
905
|
|
|
889
|
-
// Reveal the recorded winner lane: mark it and make it the active child. No-op
|
|
890
|
-
// when no winner is recorded or its assistant is not among the lanes.
|
|
891
|
-
function applyArenaWinner(
|
|
892
|
-
canonicalUser: ResponseTreeMessage,
|
|
893
|
-
group: StoredResponse[],
|
|
894
|
-
winnerRespId: string | undefined,
|
|
895
|
-
): void {
|
|
896
|
-
if (!winnerRespId) return;
|
|
897
|
-
const winnerResp = group.find((r) => r.id === winnerRespId);
|
|
898
|
-
if (!winnerResp) return;
|
|
899
|
-
const winnerAssistantId = getAssistantNodeId(winnerResp);
|
|
900
|
-
const idx = canonicalUser.childrenIds.indexOf(winnerAssistantId);
|
|
901
|
-
if (idx === -1) return;
|
|
902
|
-
canonicalUser.comparisonWinnerId = winnerAssistantId;
|
|
903
|
-
canonicalUser.activeChildIndex = idx;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
906
|
// Fold one matchup's sibling responses onto a single comparison node: the first
|
|
907
907
|
// lane's user turn becomes the shared fork parent, every lane's assistant becomes
|
|
908
908
|
// a child, and the duplicate echoed-user turns are dropped. The first lane's
|
|
@@ -911,7 +911,6 @@ function applyArenaWinner(
|
|
|
911
911
|
function foldArenaGroup(
|
|
912
912
|
nodes: Record<string, ResponseTreeMessage>,
|
|
913
913
|
group: StoredResponse[],
|
|
914
|
-
winnerRespId: string | undefined,
|
|
915
914
|
userNodeFor: (responseId: string) => ResponseTreeMessage | undefined,
|
|
916
915
|
root: string | null,
|
|
917
916
|
): string | null {
|
|
@@ -921,15 +920,11 @@ function foldArenaGroup(
|
|
|
921
920
|
if (!canonicalUser) return root;
|
|
922
921
|
const sharedParentId = canonicalUser.parentId;
|
|
923
922
|
|
|
924
|
-
// Stamp matchup
|
|
925
|
-
// voted on (the host
|
|
923
|
+
// Stamp the matchup id on the fork parent so a reloaded comparison can be
|
|
924
|
+
// voted on (the host builds the vote payload from `matchup_id`).
|
|
926
925
|
const forkMeta = parseArenaMeta(first);
|
|
927
926
|
if (forkMeta) {
|
|
928
|
-
canonicalUser.metadata.arena = {
|
|
929
|
-
matchupId: forkMeta.matchupId,
|
|
930
|
-
slotSeed: forkMeta.slotSeed,
|
|
931
|
-
variant: forkMeta.variant,
|
|
932
|
-
};
|
|
927
|
+
canonicalUser.metadata.arena = { matchupId: forkMeta.matchupId };
|
|
933
928
|
}
|
|
934
929
|
|
|
935
930
|
let newRoot = root;
|
|
@@ -955,6 +950,5 @@ function foldArenaGroup(
|
|
|
955
950
|
|
|
956
951
|
canonicalUser.comparing = true;
|
|
957
952
|
newRoot = activateForkNode(nodes, canonicalUser, sharedParentId, newRoot);
|
|
958
|
-
applyArenaWinner(canonicalUser, group, winnerRespId);
|
|
959
953
|
return newRoot;
|
|
960
954
|
}
|