@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
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { type ChatConfig, type ChatMessage, type ChatResponse, type ConversationSummary, type HttpClient, type RawConversation, type RawConversationList, type StreamEvent, } from './services';
|
|
2
2
|
export { useMessageHandler, useMarkdownParser, provideChatKitLabels, useChatKitLabels, defaultChatKitLabels, CHAT_KIT_LABELS, } from './handlers';
|
|
3
3
|
export type { ChatKitLabels } from './handlers';
|
|
4
4
|
export { useChat, useConversationSessions, useConversations, } from './composables';
|
|
5
5
|
export type { ChatActions, ChatState, ConversationSession, CreateConversationInput, MessageHandler, MutationOptions, UseChatReturn, UseConversationSessionsReturn, UseConversationsOptions, UseConversationsReturn, } from './composables';
|
|
6
6
|
export { CkAgentMessage, CkConversation, CkConversationLayout, CkCopyMessageActionButton, CkFeedbackButton, CkHistory, CkHistoryItem, CkHistoryWrapper, CkInput, CkMarkdownRenderer, CkMessageActionButton, CkPromptActionButton, CkPromptStopButton, CkPromptInput, CkPromptInputDisclaimer, CkSendIcon, CkStopIcon, CkUserMessage, } from './components';
|
|
7
7
|
export type { CkConversationLayoutProps, CkConversationProps, CkFeedbackButtonProps, CkFeedbackValue, CkHistoryEmits, CkHistoryItemEmits, CkHistoryItemProps, CkHistoryProps, CkHistoryWrapperProps, CkMarkdownElementClasses, CkMarkdownRendererProps, CkUserMessageProps, } from './components';
|
|
8
|
-
export type { BaseMessage, Conversation, ConversationTree, MessageRole, MessageStatus, TreeMessage, } from './types';
|
|
8
|
+
export type { BaseMessage, Conversation, ConversationTree, MessageContent, MessageContentStatus, MessageReasoningContent, MessageRole, MessageStatus, MessageTextContent, MessageToolContent, TreeMessage, } from './types';
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AASpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EACL,OAAO,EACP,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,aAAa,EACb,6BAA6B,EAC7B,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,uBAAuB,EACvB,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { u } from "./
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { _, a as a3, b as b2, C as C3, c, d as d2, e, f, g, h, i, j, k, l, m, n, o, p as p2 } from "./CkInput.vue_vue_type_script_setup_true_lang-CyGLItQZ.js";
|
|
1
|
+
import { u } from "./useMessageHandler-ZRC-gIOJ.js";
|
|
2
|
+
import { C, d, p, u as u2, a } from "./useChatKitLabels-ftC52m7f.js";
|
|
3
|
+
import { u as u3, a as a2, b } from "./index-BbTgIoYa.js";
|
|
4
|
+
import { _, a as a3, b as b2, C as C2, c, d as d2, e, f, g, h, i, j, k, l, m, n, o, p as p2 } from "./CkInput.vue_vue_type_script_setup_true_lang-C5C09u0z.js";
|
|
6
5
|
export {
|
|
7
|
-
|
|
8
|
-
C as ChatService,
|
|
6
|
+
C as CHAT_KIT_LABELS,
|
|
9
7
|
_ as CkAgentMessage,
|
|
10
8
|
a3 as CkConversation,
|
|
11
9
|
b2 as CkConversationLayout,
|
|
12
|
-
|
|
10
|
+
C2 as CkCopyMessageActionButton,
|
|
13
11
|
c as CkFeedbackButton,
|
|
14
12
|
d2 as CkHistory,
|
|
15
13
|
e as CkHistoryItem,
|
package/dist/services/index.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export { ChatService, type IChatService } from './ChatService';
|
|
2
|
-
export { MockChatService } from './MockChatService';
|
|
3
1
|
export type { ChatConfig, ChatMessage, ChatResponse, ConversationSummary, HttpClient, RawConversation, RawConversationList, StreamEvent, } from './types';
|
|
4
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,WAAW,GACZ,MAAM,SAAS,CAAC"}
|
package/dist/services/index.js
CHANGED
|
@@ -1,37 +1 @@
|
|
|
1
|
-
|
|
2
|
-
class MockChatService extends ChatService {
|
|
3
|
-
constructor() {
|
|
4
|
-
super({});
|
|
5
|
-
}
|
|
6
|
-
async sendMessage(message) {
|
|
7
|
-
const replyText = `Mock reply: ${message.content}`;
|
|
8
|
-
return {
|
|
9
|
-
message: this.createBaseMessage({
|
|
10
|
-
role: "assistant",
|
|
11
|
-
content: replyText
|
|
12
|
-
}),
|
|
13
|
-
metadata: { mock: true }
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
async getHistory() {
|
|
17
|
-
return [];
|
|
18
|
-
}
|
|
19
|
-
async streamMessage(message, onEvent) {
|
|
20
|
-
const response = await this.sendMessage(message);
|
|
21
|
-
onEvent({ type: "chunk", data: response.message.content });
|
|
22
|
-
onEvent({ type: "done" });
|
|
23
|
-
}
|
|
24
|
-
createBaseMessage(input) {
|
|
25
|
-
return {
|
|
26
|
-
id: crypto.randomUUID(),
|
|
27
|
-
role: input.role,
|
|
28
|
-
content: input.content,
|
|
29
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
30
|
-
status: "completed"
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
export {
|
|
35
|
-
ChatService,
|
|
36
|
-
MockChatService
|
|
37
|
-
};
|
|
1
|
+
|
package/dist/services/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseMessage, MessageRole } from '../types';
|
|
1
|
+
import type { BaseMessage, MessageContent, MessageRole } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Minimal HTTP transport contract that chat-kit composables depend on.
|
|
4
4
|
*
|
|
@@ -58,7 +58,7 @@ export interface RawConversationList {
|
|
|
58
58
|
export interface ChatMessage {
|
|
59
59
|
id: string;
|
|
60
60
|
role: MessageRole;
|
|
61
|
-
content:
|
|
61
|
+
content: MessageContent[];
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
64
|
* Response from the chat service
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/services/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/services/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,CAAC,EACH,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAC7C,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -3,10 +3,55 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export type MessageRole = 'user' | 'assistant' | 'system';
|
|
5
5
|
export type MessageStatus = 'started' | 'sending' | 'completed' | 'error';
|
|
6
|
+
/**
|
|
7
|
+
* Lifecycle of a single content item inside a message — used to indicate
|
|
8
|
+
* whether a streamed text part is still being produced, has finished, or was
|
|
9
|
+
* cut short. Tool content does not carry a status (it represents a single
|
|
10
|
+
* fired tool call) so it always omits this field.
|
|
11
|
+
*/
|
|
12
|
+
export type MessageContentStatus = 'in_progress' | 'completed' | 'incomplete';
|
|
13
|
+
/**
|
|
14
|
+
* Plain text part of a message. Multiple text parts can appear in a single
|
|
15
|
+
* assistant turn, interleaved with reasoning and tool calls.
|
|
16
|
+
*/
|
|
17
|
+
export interface MessageTextContent {
|
|
18
|
+
type: 'text';
|
|
19
|
+
id: string;
|
|
20
|
+
text: string;
|
|
21
|
+
status: MessageContentStatus;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Tool invocation triggered by the assistant during its turn. Only the tool
|
|
25
|
+
* name is rendered; arguments and results are intentionally outside the
|
|
26
|
+
* UI-facing model and should be tracked by the host application.
|
|
27
|
+
*/
|
|
28
|
+
export interface MessageToolContent {
|
|
29
|
+
type: 'tool';
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Chain-of-thought / reasoning segment produced by the assistant. `text` is
|
|
35
|
+
* the (optional) human-readable trail — populated when the underlying
|
|
36
|
+
* transport exposes reasoning content (`REASONING_MESSAGE_CONTENT` deltas
|
|
37
|
+
* during a live run, `reasoning_text` parts when re-hydrating from the
|
|
38
|
+
* Responses API). Surfaces that only need a "Thinking…" / "Thought"
|
|
39
|
+
* indicator can ignore it and rely on `status` alone.
|
|
40
|
+
*/
|
|
41
|
+
export interface MessageReasoningContent {
|
|
42
|
+
type: 'reasoning';
|
|
43
|
+
id: string;
|
|
44
|
+
text?: string;
|
|
45
|
+
status: MessageContentStatus;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Discriminated union of all content parts that can appear inside a message.
|
|
49
|
+
*/
|
|
50
|
+
export type MessageContent = MessageTextContent | MessageToolContent | MessageReasoningContent;
|
|
6
51
|
export interface BaseMessage {
|
|
7
52
|
id: string;
|
|
8
53
|
role: MessageRole;
|
|
9
|
-
content:
|
|
54
|
+
content: MessageContent[];
|
|
10
55
|
createdAt: Date;
|
|
11
56
|
status: MessageStatus;
|
|
12
57
|
siblingIds?: string[];
|
|
@@ -18,7 +63,7 @@ export interface TreeMessage {
|
|
|
18
63
|
childrenIds: string[];
|
|
19
64
|
activeChildIndex: number;
|
|
20
65
|
role: MessageRole;
|
|
21
|
-
content:
|
|
66
|
+
content: MessageContent[];
|
|
22
67
|
status: MessageStatus;
|
|
23
68
|
createdAt: Date;
|
|
24
69
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,CAAC;AAE9E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,kBAAkB,GAClB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { g as generateId } from "./generateId-BHf0W4Rq.js";
|
|
1
2
|
import { reactive, ref, computed, readonly } from "vue";
|
|
2
3
|
function createEmptyTree() {
|
|
3
4
|
return { nodes: {}, rootId: null };
|
|
@@ -77,9 +78,6 @@ function navigate(tree, targetId) {
|
|
|
77
78
|
if (idx === -1) return;
|
|
78
79
|
parent.activeChildIndex = idx;
|
|
79
80
|
}
|
|
80
|
-
function generateId() {
|
|
81
|
-
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : String(Date.now());
|
|
82
|
-
}
|
|
83
81
|
const useMessageHandler = () => {
|
|
84
82
|
const tree = reactive(createEmptyTree());
|
|
85
83
|
const currentUserMessage = ref("");
|
|
@@ -98,13 +96,16 @@ const useMessageHandler = () => {
|
|
|
98
96
|
const id = generateId();
|
|
99
97
|
const text = currentUserMessage.value.trim();
|
|
100
98
|
if (!text) throw new Error("No message");
|
|
99
|
+
const content = [
|
|
100
|
+
{ type: "text", id: generateId(), text, status: "completed" }
|
|
101
|
+
];
|
|
101
102
|
const node = {
|
|
102
103
|
id,
|
|
103
104
|
parentId: currentLeafId.value,
|
|
104
105
|
childrenIds: [],
|
|
105
106
|
activeChildIndex: 0,
|
|
106
107
|
role: "user",
|
|
107
|
-
content
|
|
108
|
+
content,
|
|
108
109
|
status: "completed",
|
|
109
110
|
createdAt: /* @__PURE__ */ new Date()
|
|
110
111
|
};
|
|
@@ -180,6 +181,5 @@ const useMessageHandler = () => {
|
|
|
180
181
|
};
|
|
181
182
|
};
|
|
182
183
|
export {
|
|
183
|
-
generateId as g,
|
|
184
184
|
useMessageHandler as u
|
|
185
185
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aleph-alpha/chat-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Chat kit package for building chat interfaces with Vue",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,6 +26,14 @@
|
|
|
26
26
|
"./handlers": {
|
|
27
27
|
"import": "./dist/handlers/index.js",
|
|
28
28
|
"types": "./dist/handlers/index.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./adapters": {
|
|
31
|
+
"import": "./dist/adapters/index.js",
|
|
32
|
+
"types": "./dist/adapters/index.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./adapters/responses-api": {
|
|
35
|
+
"import": "./dist/adapters/responses-api.js",
|
|
36
|
+
"types": "./dist/adapters/responses-api.d.ts"
|
|
29
37
|
}
|
|
30
38
|
},
|
|
31
39
|
"files": [
|
|
@@ -64,7 +72,7 @@
|
|
|
64
72
|
"remark-parse": "^11.0.0",
|
|
65
73
|
"remark-rehype": "^11.1.2",
|
|
66
74
|
"unified": "^11.0.5",
|
|
67
|
-
"@aleph-alpha/ui-library": "2.4.
|
|
75
|
+
"@aleph-alpha/ui-library": "2.4.3"
|
|
68
76
|
},
|
|
69
77
|
"devDependencies": {
|
|
70
78
|
"@storybook/addon-a11y": "10.2.17",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format adapters for chat-kit.
|
|
3
|
+
*
|
|
4
|
+
* Adapters convert a transport-specific transcript shape into chat-kit's
|
|
5
|
+
* domain model (`ConversationTree`). They are intentionally optional — the
|
|
6
|
+
* message handler only knows about `setTree(tree)`, so apps that use a
|
|
7
|
+
* different format can write their own adapter (or skip the concept
|
|
8
|
+
* entirely and build trees by hand).
|
|
9
|
+
*
|
|
10
|
+
* Currently shipped:
|
|
11
|
+
* - `responses-api` — OpenAI-style `GET /conversations/{id}/responses`.
|
|
12
|
+
*
|
|
13
|
+
* Re-exported via `@aleph-alpha/chat-kit/adapters`. Use the more granular
|
|
14
|
+
* `@aleph-alpha/chat-kit/adapters/responses-api` subpath when you only
|
|
15
|
+
* need a specific adapter.
|
|
16
|
+
*/
|
|
17
|
+
export * from './responses-api';
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { useMessageHandler } from '../handlers/useMessageHandler';
|
|
2
|
+
import type { MessageContent, MessageTextContent } from '../types';
|
|
3
|
+
import {
|
|
4
|
+
responsesToTree,
|
|
5
|
+
type ResponseOutputMessage,
|
|
6
|
+
type ResponseOutputReasoning,
|
|
7
|
+
type ResponseOutputTool,
|
|
8
|
+
type StoredResponse,
|
|
9
|
+
} from './responses-api';
|
|
10
|
+
|
|
11
|
+
function getText(content: MessageContent[]): string {
|
|
12
|
+
return content
|
|
13
|
+
.filter((c): c is MessageTextContent => c.type === 'text')
|
|
14
|
+
.map((c) => c.text)
|
|
15
|
+
.join('');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function makeResponse(
|
|
19
|
+
overrides: Partial<StoredResponse> & { id: string },
|
|
20
|
+
): StoredResponse {
|
|
21
|
+
return {
|
|
22
|
+
object: 'response',
|
|
23
|
+
created_at: 1_777_456_700,
|
|
24
|
+
completed_at: 1_777_456_701,
|
|
25
|
+
status: 'completed',
|
|
26
|
+
model: 'pharia-1',
|
|
27
|
+
output: [],
|
|
28
|
+
request_input: [],
|
|
29
|
+
...overrides,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function userInput(text: string): Record<string, unknown> {
|
|
34
|
+
return { role: 'user', content: text };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function assistantOutput(id: string, text: string): ResponseOutputMessage {
|
|
38
|
+
return {
|
|
39
|
+
type: 'message',
|
|
40
|
+
id,
|
|
41
|
+
status: 'completed',
|
|
42
|
+
role: 'assistant',
|
|
43
|
+
content: [{ type: 'output_text', text }],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function reasoningOutput(id: string, text: string): ResponseOutputReasoning {
|
|
48
|
+
return {
|
|
49
|
+
type: 'reasoning',
|
|
50
|
+
id,
|
|
51
|
+
status: 'completed',
|
|
52
|
+
role: null,
|
|
53
|
+
content: [{ type: 'reasoning_text', text }],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function toolOutput(
|
|
58
|
+
overrides: Partial<ResponseOutputTool> & { id: string },
|
|
59
|
+
): ResponseOutputTool {
|
|
60
|
+
return {
|
|
61
|
+
type: 'mcp_list_tools',
|
|
62
|
+
server_label: 'firecrawl',
|
|
63
|
+
tools: [],
|
|
64
|
+
...overrides,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe('responsesToTree', () => {
|
|
69
|
+
it('returns an empty tree for an empty response list', () => {
|
|
70
|
+
expect(responsesToTree([])).toEqual({ nodes: {}, rootId: null });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('builds a linear tree from a single response with one user input and one assistant output', () => {
|
|
74
|
+
const tree = responsesToTree([
|
|
75
|
+
makeResponse({
|
|
76
|
+
id: 'r1',
|
|
77
|
+
request_input: [userInput('Hello')],
|
|
78
|
+
output: [assistantOutput('msg-a1', 'Hi there')],
|
|
79
|
+
}),
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
const ids = Object.keys(tree.nodes);
|
|
83
|
+
expect(ids).toHaveLength(2);
|
|
84
|
+
|
|
85
|
+
const userNode = tree.nodes['user-r1-0']!;
|
|
86
|
+
expect(userNode.role).toBe('user');
|
|
87
|
+
expect(userNode.parentId).toBeNull();
|
|
88
|
+
expect(getText(userNode.content)).toBe('Hello');
|
|
89
|
+
|
|
90
|
+
const assistantNode = tree.nodes['msg-a1']!;
|
|
91
|
+
expect(assistantNode.role).toBe('assistant');
|
|
92
|
+
expect(assistantNode.parentId).toBe('user-r1-0');
|
|
93
|
+
expect(getText(assistantNode.content)).toBe('Hi there');
|
|
94
|
+
|
|
95
|
+
expect(tree.rootId).toBe('user-r1-0');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('chains a follow-up response under the previous assistant message via previous_response_id', () => {
|
|
99
|
+
const tree = responsesToTree([
|
|
100
|
+
makeResponse({
|
|
101
|
+
id: 'r1',
|
|
102
|
+
request_input: [userInput('First')],
|
|
103
|
+
output: [assistantOutput('msg-a1', 'A1')],
|
|
104
|
+
}),
|
|
105
|
+
makeResponse({
|
|
106
|
+
id: 'r2',
|
|
107
|
+
previous_response_id: 'r1',
|
|
108
|
+
request_input: [userInput('Second')],
|
|
109
|
+
output: [assistantOutput('msg-a2', 'A2')],
|
|
110
|
+
}),
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
expect(tree.nodes['user-r2-0']!.parentId).toBe('msg-a1');
|
|
114
|
+
expect(tree.nodes['msg-a2']!.parentId).toBe('user-r2-0');
|
|
115
|
+
expect(tree.rootId).toBe('user-r1-0');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('treats sibling responses (same previous_response_id) as branches and surfaces the latest as active', () => {
|
|
119
|
+
const tree = responsesToTree([
|
|
120
|
+
makeResponse({
|
|
121
|
+
id: 'r1',
|
|
122
|
+
request_input: [userInput('Hello')],
|
|
123
|
+
output: [assistantOutput('msg-a1', 'Hi')],
|
|
124
|
+
}),
|
|
125
|
+
makeResponse({
|
|
126
|
+
id: 'r2',
|
|
127
|
+
previous_response_id: 'r1',
|
|
128
|
+
request_input: [userInput('Branch A')],
|
|
129
|
+
output: [assistantOutput('msg-a2', 'A')],
|
|
130
|
+
}),
|
|
131
|
+
makeResponse({
|
|
132
|
+
id: 'r3',
|
|
133
|
+
previous_response_id: 'r1',
|
|
134
|
+
request_input: [userInput('Branch B')],
|
|
135
|
+
output: [assistantOutput('msg-a3', 'B')],
|
|
136
|
+
}),
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
const a1 = tree.nodes['msg-a1']!;
|
|
140
|
+
expect(a1.childrenIds).toEqual(['user-r2-0', 'user-r3-0']);
|
|
141
|
+
expect(a1.activeChildIndex).toBe(1);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('skips responses with no visible content but keeps descendants attached to the parent tail', () => {
|
|
145
|
+
const tree = responsesToTree([
|
|
146
|
+
makeResponse({
|
|
147
|
+
id: 'r1',
|
|
148
|
+
request_input: [userInput('Hello')],
|
|
149
|
+
output: [assistantOutput('msg-a1', 'Hi')],
|
|
150
|
+
}),
|
|
151
|
+
makeResponse({
|
|
152
|
+
id: 'r2',
|
|
153
|
+
previous_response_id: 'r1',
|
|
154
|
+
request_input: [],
|
|
155
|
+
output: [],
|
|
156
|
+
}),
|
|
157
|
+
makeResponse({
|
|
158
|
+
id: 'r3',
|
|
159
|
+
previous_response_id: 'r2',
|
|
160
|
+
request_input: [userInput('Follow-up')],
|
|
161
|
+
output: [assistantOutput('msg-a3', 'Reply')],
|
|
162
|
+
}),
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
expect(tree.nodes['user-r3-0']!.parentId).toBe('msg-a1');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('extracts text from string content as well as output_text / text parts', () => {
|
|
169
|
+
const tree = responsesToTree([
|
|
170
|
+
makeResponse({
|
|
171
|
+
id: 'r1',
|
|
172
|
+
request_input: [userInput('plain string')],
|
|
173
|
+
output: [
|
|
174
|
+
{
|
|
175
|
+
type: 'message',
|
|
176
|
+
id: 'msg-mixed',
|
|
177
|
+
status: 'completed',
|
|
178
|
+
role: 'assistant',
|
|
179
|
+
content: [
|
|
180
|
+
{ type: 'output_text', text: 'first ' },
|
|
181
|
+
{ type: 'reasoning_summary', text: 'should-be-ignored' },
|
|
182
|
+
{ type: 'text', text: 'second' },
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
}),
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
expect(getText(tree.nodes['user-r1-0']!.content)).toBe('plain string');
|
|
190
|
+
expect(getText(tree.nodes['msg-mixed']!.content)).toBe('first second');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('merges reasoning, tool calls, and message text into a single assistant node, preserving wire order', () => {
|
|
194
|
+
const tree = responsesToTree([
|
|
195
|
+
makeResponse({
|
|
196
|
+
id: 'r1',
|
|
197
|
+
request_input: [userInput('Hello')],
|
|
198
|
+
output: [
|
|
199
|
+
toolOutput({
|
|
200
|
+
id: 'tool-list',
|
|
201
|
+
type: 'mcp_list_tools',
|
|
202
|
+
server_label: 'firecrawl',
|
|
203
|
+
tools: [{ name: 'firecrawl_scrape' }],
|
|
204
|
+
}),
|
|
205
|
+
reasoningOutput('rs-1', 'thinking out loud'),
|
|
206
|
+
toolOutput({
|
|
207
|
+
id: 'tool-call-1',
|
|
208
|
+
type: 'mcp_call',
|
|
209
|
+
name: 'firecrawl_scrape',
|
|
210
|
+
}),
|
|
211
|
+
assistantOutput('msg-a1', 'Hi'),
|
|
212
|
+
],
|
|
213
|
+
}),
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
// Reasoning and tool items don't get their own tree nodes; they are
|
|
217
|
+
// rolled up onto the assistant message.
|
|
218
|
+
expect(tree.nodes['tool-list']).toBeUndefined();
|
|
219
|
+
expect(tree.nodes['rs-1']).toBeUndefined();
|
|
220
|
+
expect(tree.nodes['tool-call-1']).toBeUndefined();
|
|
221
|
+
|
|
222
|
+
const assistant = tree.nodes['msg-a1']!;
|
|
223
|
+
expect(assistant).toBeDefined();
|
|
224
|
+
// mcp_list_tools is informational and skipped; reasoning + mcp_call +
|
|
225
|
+
// text remain in their original order.
|
|
226
|
+
expect(assistant.content).toHaveLength(3);
|
|
227
|
+
expect(assistant.content[0]).toMatchObject({
|
|
228
|
+
type: 'reasoning',
|
|
229
|
+
id: 'rs-1',
|
|
230
|
+
text: 'thinking out loud',
|
|
231
|
+
status: 'completed',
|
|
232
|
+
});
|
|
233
|
+
expect(assistant.content[1]).toMatchObject({
|
|
234
|
+
type: 'tool',
|
|
235
|
+
id: 'tool-call-1',
|
|
236
|
+
name: 'firecrawl_scrape',
|
|
237
|
+
});
|
|
238
|
+
expect(assistant.content[2]).toMatchObject({
|
|
239
|
+
type: 'text',
|
|
240
|
+
id: 'msg-a1',
|
|
241
|
+
text: 'Hi',
|
|
242
|
+
status: 'completed',
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('also picks up function_call / tool_call outputs as tool parts', () => {
|
|
247
|
+
const tree = responsesToTree([
|
|
248
|
+
makeResponse({
|
|
249
|
+
id: 'r1',
|
|
250
|
+
request_input: [userInput('Hello')],
|
|
251
|
+
output: [
|
|
252
|
+
toolOutput({ id: 'fn-1', type: 'function_call', name: 'lookup' }),
|
|
253
|
+
toolOutput({ id: 'tc-1', type: 'tool_call', name: 'shell' }),
|
|
254
|
+
assistantOutput('msg-a1', 'Done'),
|
|
255
|
+
],
|
|
256
|
+
}),
|
|
257
|
+
]);
|
|
258
|
+
|
|
259
|
+
const assistant = tree.nodes['msg-a1']!;
|
|
260
|
+
expect(assistant.content).toHaveLength(3);
|
|
261
|
+
expect(assistant.content[0]).toMatchObject({
|
|
262
|
+
type: 'tool',
|
|
263
|
+
id: 'fn-1',
|
|
264
|
+
name: 'lookup',
|
|
265
|
+
});
|
|
266
|
+
expect(assistant.content[1]).toMatchObject({
|
|
267
|
+
type: 'tool',
|
|
268
|
+
id: 'tc-1',
|
|
269
|
+
name: 'shell',
|
|
270
|
+
});
|
|
271
|
+
expect(assistant.content[2].type).toBe('text');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('creates an assistant node from reasoning / tool outputs even when no message output is present', () => {
|
|
275
|
+
const tree = responsesToTree([
|
|
276
|
+
makeResponse({
|
|
277
|
+
id: 'r1',
|
|
278
|
+
request_input: [userInput('Hello')],
|
|
279
|
+
output: [
|
|
280
|
+
reasoningOutput('rs-1', 'planning'),
|
|
281
|
+
toolOutput({ id: 'tool-1', type: 'mcp_call', name: 'search' }),
|
|
282
|
+
],
|
|
283
|
+
}),
|
|
284
|
+
]);
|
|
285
|
+
|
|
286
|
+
const assistant = tree.nodes['assistant-r1']!;
|
|
287
|
+
expect(assistant).toBeDefined();
|
|
288
|
+
expect(assistant.role).toBe('assistant');
|
|
289
|
+
expect(assistant.content).toHaveLength(2);
|
|
290
|
+
expect(assistant.content[0]).toMatchObject({
|
|
291
|
+
type: 'reasoning',
|
|
292
|
+
text: 'planning',
|
|
293
|
+
});
|
|
294
|
+
expect(assistant.content[1].type).toBe('tool');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('omits reasoning text when no reasoning_text parts are present', () => {
|
|
298
|
+
const tree = responsesToTree([
|
|
299
|
+
makeResponse({
|
|
300
|
+
id: 'r1',
|
|
301
|
+
request_input: [userInput('Hello')],
|
|
302
|
+
output: [
|
|
303
|
+
{
|
|
304
|
+
type: 'reasoning',
|
|
305
|
+
id: 'rs-empty',
|
|
306
|
+
status: 'completed',
|
|
307
|
+
role: null,
|
|
308
|
+
content: [],
|
|
309
|
+
},
|
|
310
|
+
assistantOutput('msg-a1', 'Hi'),
|
|
311
|
+
],
|
|
312
|
+
}),
|
|
313
|
+
]);
|
|
314
|
+
|
|
315
|
+
const reasoning = tree.nodes['msg-a1']!.content[0];
|
|
316
|
+
expect(reasoning.type).toBe('reasoning');
|
|
317
|
+
if (reasoning.type === 'reasoning') {
|
|
318
|
+
expect(reasoning.text).toBeUndefined();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('chains the next response under a reasoning-only assistant turn', () => {
|
|
323
|
+
const tree = responsesToTree([
|
|
324
|
+
makeResponse({
|
|
325
|
+
id: 'r1',
|
|
326
|
+
request_input: [userInput('Hello')],
|
|
327
|
+
output: [reasoningOutput('rs-1', 'planning')],
|
|
328
|
+
}),
|
|
329
|
+
makeResponse({
|
|
330
|
+
id: 'r2',
|
|
331
|
+
previous_response_id: 'r1',
|
|
332
|
+
request_input: [userInput('Follow-up')],
|
|
333
|
+
output: [assistantOutput('msg-a2', 'Reply')],
|
|
334
|
+
}),
|
|
335
|
+
]);
|
|
336
|
+
|
|
337
|
+
expect(tree.nodes['rs-1']).toBeUndefined();
|
|
338
|
+
expect(tree.nodes['assistant-r1']).toBeDefined();
|
|
339
|
+
expect(tree.nodes['user-r2-0']!.parentId).toBe('assistant-r1');
|
|
340
|
+
expect(tree.nodes['msg-a2']!.parentId).toBe('user-r2-0');
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe('responsesToTree + useMessageHandler.setTree', () => {
|
|
345
|
+
it('hydrates a handler from a Responses-API list via the documented adapter pattern', () => {
|
|
346
|
+
const handler = useMessageHandler();
|
|
347
|
+
handler.setTree(
|
|
348
|
+
responsesToTree([
|
|
349
|
+
makeResponse({
|
|
350
|
+
id: 'r1',
|
|
351
|
+
request_input: [userInput('Hello')],
|
|
352
|
+
output: [assistantOutput('msg-a1', 'Hi')],
|
|
353
|
+
}),
|
|
354
|
+
]),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
expect(handler.messages.value).toHaveLength(2);
|
|
358
|
+
expect(handler.messages.value.map((m) => m.role)).toEqual([
|
|
359
|
+
'user',
|
|
360
|
+
'assistant',
|
|
361
|
+
]);
|
|
362
|
+
expect(getText(handler.messages.value[0].content)).toBe('Hello');
|
|
363
|
+
expect(getText(handler.messages.value[1].content)).toBe('Hi');
|
|
364
|
+
});
|
|
365
|
+
});
|