@tulip-systems/ai 0.11.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/LICENSE +662 -0
- package/dist/agents/lib/definitions.server.d.mts +11 -0
- package/dist/agents/lib/definitions.server.mjs +14 -0
- package/dist/agents/lib/delegation.server.d.mts +15 -0
- package/dist/agents/lib/delegation.server.mjs +28 -0
- package/dist/agents/lib/instructions.server.d.mts +14 -0
- package/dist/agents/lib/instructions.server.mjs +26 -0
- package/dist/agents/lib/types.d.mts +26 -0
- package/dist/agents/server.d.mts +5 -0
- package/dist/agents/server.mjs +5 -0
- package/dist/chat/client.d.mts +10 -0
- package/dist/chat/client.mjs +12 -0
- package/dist/chat/components/agent-activity.client.d.mts +14 -0
- package/dist/chat/components/agent-activity.client.mjs +26 -0
- package/dist/chat/components/assistant-panel.client.d.mts +40 -0
- package/dist/chat/components/assistant-panel.client.mjs +53 -0
- package/dist/chat/components/chat-composer.client.d.mts +160 -0
- package/dist/chat/components/chat-composer.client.mjs +266 -0
- package/dist/chat/components/chat-message.client.d.mts +92 -0
- package/dist/chat/components/chat-message.client.mjs +121 -0
- package/dist/chat/components/chat-thread.client.d.mts +130 -0
- package/dist/chat/components/chat-thread.client.mjs +164 -0
- package/dist/chat/components/message-part.client.d.mts +23 -0
- package/dist/chat/components/message-part.client.mjs +38 -0
- package/dist/chat/components/starter-prompts.client.d.mts +25 -0
- package/dist/chat/components/starter-prompts.client.mjs +31 -0
- package/dist/chat/components/tool-call-card.client.d.mts +29 -0
- package/dist/chat/components/tool-call-card.client.mjs +60 -0
- package/dist/chat/lib/classes.mjs +7 -0
- package/dist/chat/lib/parts.d.mts +10 -0
- package/dist/chat/lib/parts.mjs +25 -0
- package/dist/chat/lib/stream.server.d.mts +24 -0
- package/dist/chat/lib/stream.server.mjs +34 -0
- package/dist/chat/lib/types.d.mts +21 -0
- package/dist/chat/server.d.mts +3 -0
- package/dist/chat/server.mjs +3 -0
- package/dist/chat.d.mts +3 -0
- package/dist/chat.mjs +3 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/mcp/lib/content.d.mts +66 -0
- package/dist/mcp/lib/content.d.mts.map +1 -0
- package/dist/mcp/lib/content.mjs +115 -0
- package/dist/mcp/lib/content.mjs.map +1 -0
- package/dist/mcp/lib/tool-result.server.d.mts +12 -0
- package/dist/mcp/lib/tool-result.server.d.mts.map +1 -0
- package/dist/mcp/lib/tool-result.server.mjs +10 -0
- package/dist/mcp/lib/tool-result.server.mjs.map +1 -0
- package/dist/mcp/lib/types.d.mts +27 -0
- package/dist/mcp/server.d.mts +3 -0
- package/dist/mcp/server.mjs +3 -0
- package/dist/mcp/types.d.mts +27 -0
- package/dist/mcp/types.d.mts.map +1 -0
- package/dist/server/mcp-content.d.mts +44 -0
- package/dist/server/mcp-content.d.mts.map +1 -0
- package/dist/server/mcp-content.mjs +71 -0
- package/dist/server/mcp-content.mjs.map +1 -0
- package/dist/server/tools.d.mts +61 -0
- package/dist/server/tools.d.mts.map +1 -0
- package/dist/server/tools.mjs +47 -0
- package/dist/server/tools.mjs.map +1 -0
- package/dist/server.d.mts +3 -0
- package/dist/server.mjs +4 -0
- package/dist/tools/client.d.mts +3 -0
- package/dist/tools/client.mjs +5 -0
- package/dist/tools/lib/ai-sdk.server.d.mts +12 -0
- package/dist/tools/lib/ai-sdk.server.d.mts.map +1 -0
- package/dist/tools/lib/ai-sdk.server.mjs +38 -0
- package/dist/tools/lib/ai-sdk.server.mjs.map +1 -0
- package/dist/tools/lib/define-tool.server.d.mts +9 -0
- package/dist/tools/lib/define-tool.server.d.mts.map +1 -0
- package/dist/tools/lib/define-tool.server.mjs +13 -0
- package/dist/tools/lib/define-tool.server.mjs.map +1 -0
- package/dist/tools/lib/definitions.client.d.mts +20 -0
- package/dist/tools/lib/definitions.client.mjs +21 -0
- package/dist/tools/lib/definitions.d.mts +16 -0
- package/dist/tools/lib/definitions.d.mts.map +1 -0
- package/dist/tools/lib/definitions.mjs +20 -0
- package/dist/tools/lib/definitions.mjs.map +1 -0
- package/dist/tools/lib/resolvers.d.mts +41 -0
- package/dist/tools/lib/resolvers.d.mts.map +1 -0
- package/dist/tools/lib/resolvers.mjs +43 -0
- package/dist/tools/lib/resolvers.mjs.map +1 -0
- package/dist/tools/lib/types.client.d.mts +13 -0
- package/dist/tools/lib/types.d.mts +33 -0
- package/dist/tools/resolvers/agent.server.d.mts +27 -0
- package/dist/tools/resolvers/agent.server.d.mts.map +1 -0
- package/dist/tools/resolvers/agent.server.mjs +44 -0
- package/dist/tools/resolvers/agent.server.mjs.map +1 -0
- package/dist/tools/resolvers/mcp.server.d.mts +31 -0
- package/dist/tools/resolvers/mcp.server.d.mts.map +1 -0
- package/dist/tools/resolvers/mcp.server.mjs +20 -0
- package/dist/tools/resolvers/mcp.server.mjs.map +1 -0
- package/dist/tools/server.d.mts +5 -0
- package/dist/tools/server.mjs +5 -0
- package/dist/tools/types.d.mts +33 -0
- package/dist/tools/types.d.mts.map +1 -0
- package/package.json +61 -0
- package/src/agents/entry.server.ts +7 -0
- package/src/agents/lib/definitions.server.ts +14 -0
- package/src/agents/lib/delegation.server.ts +33 -0
- package/src/agents/lib/instructions.server.ts +37 -0
- package/src/agents/lib/types.ts +32 -0
- package/src/chat/components/agent-activity.client.tsx +21 -0
- package/src/chat/components/chat-composer.client.tsx +379 -0
- package/src/chat/components/chat-message.client.tsx +166 -0
- package/src/chat/components/chat-thread.client.tsx +242 -0
- package/src/chat/components/message-part.client.tsx +59 -0
- package/src/chat/components/starter-prompts.client.tsx +49 -0
- package/src/chat/components/tool-call-card.client.tsx +78 -0
- package/src/chat/entry.client.ts +18 -0
- package/src/chat/entry.server.ts +5 -0
- package/src/chat/entry.ts +5 -0
- package/src/chat/lib/parts.ts +33 -0
- package/src/chat/lib/stream.server.ts +40 -0
- package/src/chat/lib/types.ts +20 -0
- package/src/entry.ts +24 -0
- package/src/mcp/entry.server.ts +2 -0
- package/src/mcp/lib/content.ts +131 -0
- package/src/mcp/lib/types.ts +28 -0
- package/src/styles.css +1 -0
- package/src/tools/entry.client.ts +7 -0
- package/src/tools/entry.server.ts +11 -0
- package/src/tools/lib/definitions.client.ts +21 -0
- package/src/tools/lib/definitions.ts +24 -0
- package/src/tools/lib/types.client.ts +11 -0
- package/src/tools/lib/types.ts +44 -0
- package/src/tools/resolvers/agent.server.ts +78 -0
- package/src/tools/resolvers/mcp.server.ts +40 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@tulip-systems/core/lib";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Button, Textarea } from "@tulip-systems/core/components";
|
|
6
|
+
import { ArrowUp, UploadIcon } from "lucide-react";
|
|
7
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
|
8
|
+
|
|
9
|
+
//#region src/chat/components/chat-composer.client.tsx
|
|
10
|
+
const ChatComposerContext = createContext({});
|
|
11
|
+
/**
|
|
12
|
+
* Provides the root composer shell and shared state for all chat composer slots.
|
|
13
|
+
* Use it as the controlled boundary for the current draft value and submit handler.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <ChatComposer value={input} onValueChange={setInput} onSubmit={sendMessage}>
|
|
18
|
+
* <ChatComposerInput placeholder="Ask something..." />
|
|
19
|
+
* <ChatComposerToolbar>
|
|
20
|
+
* <ChatComposerActions>
|
|
21
|
+
* <ChatComposerAttachmentButton />
|
|
22
|
+
* </ChatComposerActions>
|
|
23
|
+
* <ChatComposerSubmit />
|
|
24
|
+
* </ChatComposerToolbar>
|
|
25
|
+
* </ChatComposer>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
function ChatComposer({ value, onValueChange, onSubmit, disabled = false, status = "ready", statusLabel, className, children, ...props }) {
|
|
29
|
+
const textareaRef = useRef(null);
|
|
30
|
+
const isDisabled = disabled;
|
|
31
|
+
const isSubmitDisabled = disabled || status !== "ready";
|
|
32
|
+
const canSubmit = !isSubmitDisabled && value.trim().length > 0;
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
adjustTextareaHeight(textareaRef.current);
|
|
35
|
+
});
|
|
36
|
+
const submit = useCallback(() => {
|
|
37
|
+
const trimmedValue = value.trim();
|
|
38
|
+
if (!trimmedValue || isSubmitDisabled) return;
|
|
39
|
+
onSubmit(trimmedValue);
|
|
40
|
+
resetTextareaHeight(textareaRef.current);
|
|
41
|
+
}, [
|
|
42
|
+
value,
|
|
43
|
+
isSubmitDisabled,
|
|
44
|
+
onSubmit
|
|
45
|
+
]);
|
|
46
|
+
const contextValue = useMemo(() => ({
|
|
47
|
+
value,
|
|
48
|
+
onValueChange,
|
|
49
|
+
submit,
|
|
50
|
+
textareaRef,
|
|
51
|
+
disabled,
|
|
52
|
+
canSubmit,
|
|
53
|
+
status,
|
|
54
|
+
statusLabel
|
|
55
|
+
}), [
|
|
56
|
+
value,
|
|
57
|
+
onValueChange,
|
|
58
|
+
submit,
|
|
59
|
+
disabled,
|
|
60
|
+
canSubmit,
|
|
61
|
+
status,
|
|
62
|
+
statusLabel
|
|
63
|
+
]);
|
|
64
|
+
return /* @__PURE__ */ jsx(ChatComposerContext.Provider, {
|
|
65
|
+
value: contextValue,
|
|
66
|
+
children: /* @__PURE__ */ jsx("form", {
|
|
67
|
+
className: cn("pointer-events-auto mx-auto w-full max-w-3xl", className),
|
|
68
|
+
...props,
|
|
69
|
+
children: /* @__PURE__ */ jsx("fieldset", {
|
|
70
|
+
"data-disabled": isDisabled ? true : void 0,
|
|
71
|
+
className: "relative flex min-h-28 w-full min-w-0 flex-col rounded-xl border border-input/70 bg-background/80 shadow-[0_12px_40px_rgba(0,0,0,0.08)] outline-none backdrop-blur-xl transition-colors focus-within:border-primary/35 focus-within:ring-3 focus-within:ring-ring/50 data-[disabled=true]:opacity-70",
|
|
72
|
+
children
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Renders the auto-growing textarea connected to the nearest `ChatComposer`.
|
|
79
|
+
* Pressing Enter submits the composer, while Shift+Enter inserts a new line.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```tsx
|
|
83
|
+
* <ChatComposerInput placeholder="Ask something..." rows={3} />
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
function ChatComposerInput({ rows = 3, className, onKeyDown, ...props }) {
|
|
87
|
+
const { value, onValueChange, submit, textareaRef, disabled } = useContext(ChatComposerContext);
|
|
88
|
+
return /* @__PURE__ */ jsx(Textarea, {
|
|
89
|
+
ref: textareaRef,
|
|
90
|
+
value,
|
|
91
|
+
onChange: (event) => {
|
|
92
|
+
onValueChange(event.target.value);
|
|
93
|
+
adjustTextareaHeight(event.target);
|
|
94
|
+
},
|
|
95
|
+
onKeyDown: (event) => {
|
|
96
|
+
onKeyDown?.(event);
|
|
97
|
+
if (event.defaultPrevented) return;
|
|
98
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
submit();
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
disabled,
|
|
104
|
+
rows,
|
|
105
|
+
className: cn("max-h-[calc(75dvh)] min-h-20 flex-1 resize-none overflow-hidden rounded-none border-0 bg-transparent px-3 pt-3 pb-0 text-base leading-6 shadow-none ring-0 placeholder:text-muted-foreground/70 focus-visible:ring-0 disabled:bg-transparent dark:bg-transparent dark:disabled:bg-transparent", className),
|
|
106
|
+
...props
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Renders the bottom toolbar area for composer actions and the submit button.
|
|
111
|
+
* Place `ChatComposerActions` on the left and `ChatComposerSubmit` on the right.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```tsx
|
|
115
|
+
* <ChatComposerToolbar>
|
|
116
|
+
* <ChatComposerActions>
|
|
117
|
+
* <ChatComposerAttachmentButton />
|
|
118
|
+
* </ChatComposerActions>
|
|
119
|
+
* <ChatComposerSubmit />
|
|
120
|
+
* </ChatComposerToolbar>
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
function ChatComposerToolbar({ className, ...props }) {
|
|
124
|
+
return /* @__PURE__ */ jsx("div", {
|
|
125
|
+
className: cn("flex w-full items-center justify-between gap-2 px-2.5 pt-1 pb-2 text-muted-foreground text-sm", className),
|
|
126
|
+
...props
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Groups secondary composer controls such as attachments, model selection, or status.
|
|
131
|
+
* It keeps action controls aligned together inside `ChatComposerToolbar`.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```tsx
|
|
135
|
+
* <ChatComposerActions>
|
|
136
|
+
* <ChatComposerAttachmentButton />
|
|
137
|
+
* <ChatComposerModelSelect>GPT-5.5</ChatComposerModelSelect>
|
|
138
|
+
* <ChatComposerStatus />
|
|
139
|
+
* </ChatComposerActions>
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
function ChatComposerActions({ className, ...props }) {
|
|
143
|
+
return /* @__PURE__ */ jsx("div", {
|
|
144
|
+
className: cn("flex min-w-0 items-center gap-2", className),
|
|
145
|
+
...props
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Renders an attachment action button for opening a file picker or context picker.
|
|
150
|
+
* It uses a file-upload icon and `Context toevoegen` accessible label by default.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```tsx
|
|
154
|
+
* <ChatComposerAttachmentButton onClick={openFilePicker} />
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
function ChatComposerAttachmentButton({ className, disabled, ...props }) {
|
|
158
|
+
const { disabled: composerDisabled } = useContext(ChatComposerContext);
|
|
159
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
160
|
+
"aria-label": "Bestanden toevoegen",
|
|
161
|
+
type: "button",
|
|
162
|
+
size: "icon",
|
|
163
|
+
variant: "outline",
|
|
164
|
+
disabled: composerDisabled || disabled,
|
|
165
|
+
className: cn("size-8 rounded-lg p-0 shadow-none", className),
|
|
166
|
+
...props,
|
|
167
|
+
children: [/* @__PURE__ */ jsx(UploadIcon, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
|
|
168
|
+
className: "sr-only",
|
|
169
|
+
children: "Bestanden toevoegen"
|
|
170
|
+
})]
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Renders a compact model selector trigger inside the composer toolbar.
|
|
175
|
+
* Use it as a simple button or compose it with a popover/select in application code.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```tsx
|
|
179
|
+
* <ChatComposerModelSelect onClick={openModelMenu}>GPT-5.5</ChatComposerModelSelect>
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
function ChatComposerModelSelect({ className, disabled, children, ...props }) {
|
|
183
|
+
const { disabled: composerDisabled } = useContext(ChatComposerContext);
|
|
184
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
185
|
+
type: "button",
|
|
186
|
+
variant: "outline",
|
|
187
|
+
size: "sm",
|
|
188
|
+
disabled: composerDisabled || disabled,
|
|
189
|
+
className: cn("h-8 rounded-lg shadow-none", className),
|
|
190
|
+
...props,
|
|
191
|
+
children: "Select model"
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Displays the current composer status from `ChatComposer.status` and `statusLabel`.
|
|
196
|
+
* It renders nothing when no label or children are provided.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```tsx
|
|
200
|
+
* <ChatComposer status="streaming" statusLabel="Thinking...">
|
|
201
|
+
* <ChatComposerInput />
|
|
202
|
+
* <ChatComposerToolbar>
|
|
203
|
+
* <ChatComposerActions>
|
|
204
|
+
* <ChatComposerStatus />
|
|
205
|
+
* </ChatComposerActions>
|
|
206
|
+
* <ChatComposerSubmit />
|
|
207
|
+
* </ChatComposerToolbar>
|
|
208
|
+
* </ChatComposer>
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
function ChatComposerStatus({ className, children, ...props }) {
|
|
212
|
+
const { status, statusLabel } = useContext(ChatComposerContext);
|
|
213
|
+
const content = children ?? statusLabel;
|
|
214
|
+
if (!content) return null;
|
|
215
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
216
|
+
className: cn("line-clamp-1 flex items-center gap-2 text-muted-foreground text-xs opacity-70 data-[status=ready]:opacity-100", className),
|
|
217
|
+
"data-status": status,
|
|
218
|
+
...props,
|
|
219
|
+
children: [/* @__PURE__ */ jsx("span", { className: cn("size-2 rounded-full", status === "ready" ? "bg-emerald-500" : status === "error" ? "bg-red-500" : "bg-amber-500") }), /* @__PURE__ */ jsx("span", { children: content })]
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Renders the primary submit button for the current composer draft.
|
|
224
|
+
* It uses an arrow-up icon by default and disables itself while the composer cannot submit.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```tsx
|
|
228
|
+
* <ChatComposerSubmit />
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
function ChatComposerSubmit({ className, disabled, onClick, ...props }) {
|
|
232
|
+
const { canSubmit, submit, status } = useContext(ChatComposerContext);
|
|
233
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
234
|
+
type: "button",
|
|
235
|
+
variant: "default",
|
|
236
|
+
size: "icon",
|
|
237
|
+
disabled: !canSubmit || disabled,
|
|
238
|
+
className: cn("size-8 rounded-full p-0 shadow-none", className),
|
|
239
|
+
"aria-label": "Send message",
|
|
240
|
+
onClick: (event) => {
|
|
241
|
+
onClick?.(event);
|
|
242
|
+
if (!event.defaultPrevented) submit();
|
|
243
|
+
},
|
|
244
|
+
...props,
|
|
245
|
+
children: [/* @__PURE__ */ jsx(ArrowUp, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
|
|
246
|
+
className: "sr-only",
|
|
247
|
+
children: status !== "ready" ? "Wait for response" : "Send message"
|
|
248
|
+
})]
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Helpers
|
|
253
|
+
*/
|
|
254
|
+
function adjustTextareaHeight(textarea) {
|
|
255
|
+
if (!textarea) return;
|
|
256
|
+
textarea.style.height = "auto";
|
|
257
|
+
textarea.style.height = `${textarea.scrollHeight + 2}px`;
|
|
258
|
+
}
|
|
259
|
+
function resetTextareaHeight(textarea) {
|
|
260
|
+
if (!textarea) return;
|
|
261
|
+
textarea.style.height = "auto";
|
|
262
|
+
textarea.style.height = "8rem";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
//#endregion
|
|
266
|
+
export { ChatComposer, ChatComposerActions, ChatComposerAttachmentButton, ChatComposerInput, ChatComposerModelSelect, ChatComposerStatus, ChatComposerSubmit, ChatComposerToolbar };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ChatPart } from "../lib/types.mjs";
|
|
2
|
+
import { ToolClientDefinition } from "../../tools/lib/types.client.mjs";
|
|
3
|
+
import { UIMessage } from "ai";
|
|
4
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
5
|
+
import { Message, MessageContent } from "@tulip-systems/core/components";
|
|
6
|
+
import { ComponentProps } from "react";
|
|
7
|
+
|
|
8
|
+
//#region src/chat/components/chat-message.client.d.ts
|
|
9
|
+
type ChatMessageProps = ComponentProps<typeof Message> & {
|
|
10
|
+
message: UIMessage;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Provides the root message container and message context for all chat message slots.
|
|
14
|
+
* Use it with `ChatMessageContent` and `ChatMessageParts` for the default message layout.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <ChatMessage message={message}>
|
|
19
|
+
* <ChatMessageContent>
|
|
20
|
+
* <ChatMessageParts toolRenderers={toolRenderers} />
|
|
21
|
+
* </ChatMessageContent>
|
|
22
|
+
* </ChatMessage>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function ChatMessage({
|
|
26
|
+
message,
|
|
27
|
+
children,
|
|
28
|
+
...props
|
|
29
|
+
}: ChatMessageProps): react_jsx_runtime0.JSX.Element | null;
|
|
30
|
+
/**
|
|
31
|
+
* Renders the content column for a chat message.
|
|
32
|
+
* Place `ChatMessageHeader`, `ChatMessageParts`, and `ChatMessageFooter` inside it.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <ChatMessageContent>
|
|
37
|
+
* <ChatMessageParts toolRenderers={toolRenderers} />
|
|
38
|
+
* </ChatMessageContent>
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
declare function ChatMessageContent({
|
|
42
|
+
className,
|
|
43
|
+
...props
|
|
44
|
+
}: ComponentProps<typeof MessageContent>): react_jsx_runtime0.JSX.Element;
|
|
45
|
+
/**
|
|
46
|
+
* Renders a message header slot.
|
|
47
|
+
* If no children are passed, it renders nothing.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* <ChatMessageHeader>Assistant</ChatMessageHeader>
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function ChatMessageHeader({
|
|
55
|
+
className,
|
|
56
|
+
children,
|
|
57
|
+
...props
|
|
58
|
+
}: ComponentProps<"div">): react_jsx_runtime0.JSX.Element | null;
|
|
59
|
+
/**
|
|
60
|
+
* Renders the visible text and tool parts for the current chat message.
|
|
61
|
+
* Pass `toolRenderers` to customize how known tool calls/results are displayed.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* <ChatMessageParts toolRenderers={toolRenderers} />
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare function ChatMessageParts({
|
|
69
|
+
toolRenderers,
|
|
70
|
+
className,
|
|
71
|
+
parts,
|
|
72
|
+
...props
|
|
73
|
+
}: ComponentProps<"div"> & {
|
|
74
|
+
toolRenderers?: ToolClientDefinition[];
|
|
75
|
+
parts?: ChatPart[];
|
|
76
|
+
}): react_jsx_runtime0.JSX.Element;
|
|
77
|
+
/**
|
|
78
|
+
* Renders a message footer slot.
|
|
79
|
+
* If no children are passed, it renders nothing.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```tsx
|
|
83
|
+
* <ChatMessageFooter>Copied</ChatMessageFooter>
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
declare function ChatMessageFooter({
|
|
87
|
+
className,
|
|
88
|
+
children,
|
|
89
|
+
...props
|
|
90
|
+
}: ComponentProps<"div">): react_jsx_runtime0.JSX.Element | null;
|
|
91
|
+
//#endregion
|
|
92
|
+
export { ChatMessage, ChatMessageContent, ChatMessageFooter, ChatMessageHeader, ChatMessageParts, ChatMessageProps };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { getChatPartKey, isToolPart } from "../lib/parts.mjs";
|
|
4
|
+
import { ChatMessagePart } from "./message-part.client.mjs";
|
|
5
|
+
import { cn } from "@tulip-systems/core/lib";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { Message, MessageContent, MessageFooter, MessageHeader } from "@tulip-systems/core/components";
|
|
8
|
+
import { createContext, useContext, useMemo } from "react";
|
|
9
|
+
|
|
10
|
+
//#region src/chat/components/chat-message.client.tsx
|
|
11
|
+
const ChatMessageContext = createContext({});
|
|
12
|
+
/**
|
|
13
|
+
* Provides the root message container and message context for all chat message slots.
|
|
14
|
+
* Use it with `ChatMessageContent` and `ChatMessageParts` for the default message layout.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <ChatMessage message={message}>
|
|
19
|
+
* <ChatMessageContent>
|
|
20
|
+
* <ChatMessageParts toolRenderers={toolRenderers} />
|
|
21
|
+
* </ChatMessageContent>
|
|
22
|
+
* </ChatMessage>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function ChatMessage({ message, children, ...props }) {
|
|
26
|
+
const isUser = message.role === "user";
|
|
27
|
+
const contextValue = useMemo(() => {
|
|
28
|
+
return {
|
|
29
|
+
message,
|
|
30
|
+
isUser,
|
|
31
|
+
visibleParts: message.parts.filter((part) => part.type === "text" || isToolPart(part))
|
|
32
|
+
};
|
|
33
|
+
}, [message, isUser]);
|
|
34
|
+
if (contextValue.visibleParts.length === 0) return null;
|
|
35
|
+
return /* @__PURE__ */ jsx(ChatMessageContext.Provider, {
|
|
36
|
+
value: contextValue,
|
|
37
|
+
children: /* @__PURE__ */ jsx(Message, {
|
|
38
|
+
align: isUser ? "end" : "start",
|
|
39
|
+
...props,
|
|
40
|
+
children
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Renders the content column for a chat message.
|
|
46
|
+
* Place `ChatMessageHeader`, `ChatMessageParts`, and `ChatMessageFooter` inside it.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```tsx
|
|
50
|
+
* <ChatMessageContent>
|
|
51
|
+
* <ChatMessageParts toolRenderers={toolRenderers} />
|
|
52
|
+
* </ChatMessageContent>
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
function ChatMessageContent({ className, ...props }) {
|
|
56
|
+
const { isUser } = useContext(ChatMessageContext);
|
|
57
|
+
return /* @__PURE__ */ jsx(MessageContent, {
|
|
58
|
+
className: cn(isUser && "ml-auto w-fit max-w-[82%] md:max-w-[68%]", className),
|
|
59
|
+
...props
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Renders a message header slot.
|
|
64
|
+
* If no children are passed, it renders nothing.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```tsx
|
|
68
|
+
* <ChatMessageHeader>Assistant</ChatMessageHeader>
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
function ChatMessageHeader({ className, children, ...props }) {
|
|
72
|
+
const { isUser } = useContext(ChatMessageContext);
|
|
73
|
+
if (!children) return null;
|
|
74
|
+
return /* @__PURE__ */ jsx(MessageHeader, {
|
|
75
|
+
className: cn("px-1", isUser && "justify-end", className),
|
|
76
|
+
...props,
|
|
77
|
+
children
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Renders the visible text and tool parts for the current chat message.
|
|
82
|
+
* Pass `toolRenderers` to customize how known tool calls/results are displayed.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* <ChatMessageParts toolRenderers={toolRenderers} />
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
function ChatMessageParts({ toolRenderers, className, parts, ...props }) {
|
|
90
|
+
const { message, isUser, visibleParts } = useContext(ChatMessageContext);
|
|
91
|
+
const resolvedParts = parts ?? visibleParts;
|
|
92
|
+
return /* @__PURE__ */ jsx("div", {
|
|
93
|
+
className: cn("flex max-w-full flex-col gap-2.5", className),
|
|
94
|
+
...props,
|
|
95
|
+
children: resolvedParts.map((part) => /* @__PURE__ */ jsx(ChatMessagePart, {
|
|
96
|
+
part,
|
|
97
|
+
isUser,
|
|
98
|
+
toolRenderers
|
|
99
|
+
}, getChatPartKey(message.id, part)))
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Renders a message footer slot.
|
|
104
|
+
* If no children are passed, it renders nothing.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* <ChatMessageFooter>Copied</ChatMessageFooter>
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
function ChatMessageFooter({ className, children, ...props }) {
|
|
112
|
+
if (!children) return null;
|
|
113
|
+
return /* @__PURE__ */ jsx(MessageFooter, {
|
|
114
|
+
className: cn("px-1", className),
|
|
115
|
+
...props,
|
|
116
|
+
children
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
//#endregion
|
|
121
|
+
export { ChatMessage, ChatMessageContent, ChatMessageFooter, ChatMessageHeader, ChatMessageParts };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { ToolClientDefinition } from "../../tools/lib/types.client.mjs";
|
|
2
|
+
import { ChatStatus, UIMessage } from "ai";
|
|
3
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
|
+
import { ComponentProps } from "react";
|
|
5
|
+
import { MessageScrollerButton, MessageScrollerContent, MessageScrollerItem } from "@tulip-systems/core/components/client";
|
|
6
|
+
|
|
7
|
+
//#region src/chat/components/chat-thread.client.d.ts
|
|
8
|
+
type ChatThreadProps = ComponentProps<"div"> & {
|
|
9
|
+
status?: ChatStatus;
|
|
10
|
+
autoScroll?: boolean;
|
|
11
|
+
defaultScrollPosition?: "start" | "end" | "last-anchor";
|
|
12
|
+
scrollPreviousItemPeek?: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Provides shared thread state for all chat thread slots.
|
|
16
|
+
* Use it to configure chat status and scroll behavior once.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <ChatThread status={status}>
|
|
21
|
+
* {messages.length === 0 && <EmptyState />}
|
|
22
|
+
*
|
|
23
|
+
* {messages.length > 0 && (
|
|
24
|
+
* <ChatThreadContent>
|
|
25
|
+
* {messages.map((message) => (
|
|
26
|
+
* <ChatThreadMessage key={message.id} message={message} />
|
|
27
|
+
* ))}
|
|
28
|
+
* <ChatThreadActivity />
|
|
29
|
+
* </ChatThreadContent>
|
|
30
|
+
* )}
|
|
31
|
+
* </ChatThread>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
declare function ChatThread({
|
|
35
|
+
status,
|
|
36
|
+
autoScroll,
|
|
37
|
+
defaultScrollPosition,
|
|
38
|
+
scrollPreviousItemPeek,
|
|
39
|
+
className,
|
|
40
|
+
children,
|
|
41
|
+
...props
|
|
42
|
+
}: ChatThreadProps): react_jsx_runtime0.JSX.Element;
|
|
43
|
+
/**
|
|
44
|
+
* Renders the scrollable message area for a chat thread.
|
|
45
|
+
* Place `ChatThreadMessage`, `ChatThreadActivity`, and optional sentinels inside it.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <ChatThreadContent className="px-3">
|
|
50
|
+
* {messages.map((message) => (
|
|
51
|
+
* <ChatThreadMessage key={message.id} message={message} />
|
|
52
|
+
* ))}
|
|
53
|
+
* <ChatThreadActivity />
|
|
54
|
+
* </ChatThreadContent>
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare function ChatThreadContent({
|
|
58
|
+
className,
|
|
59
|
+
children,
|
|
60
|
+
...props
|
|
61
|
+
}: ComponentProps<typeof MessageScrollerContent>): react_jsx_runtime0.JSX.Element;
|
|
62
|
+
/**
|
|
63
|
+
* Renders one AI SDK message using the current `ChatThread` configuration.
|
|
64
|
+
* Use this inside `ChatThreadContent` when mapping over messages.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```tsx
|
|
68
|
+
* {messages.map((message) => (
|
|
69
|
+
* <ChatThreadMessage key={message.id} message={message} toolRenderers={toolRenderers} />
|
|
70
|
+
* ))}
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare function ChatThreadMessage({
|
|
74
|
+
message,
|
|
75
|
+
messageId,
|
|
76
|
+
scrollAnchor,
|
|
77
|
+
toolRenderers,
|
|
78
|
+
...props
|
|
79
|
+
}: Omit<ComponentProps<typeof MessageScrollerItem>, "children" | "messageId" | "scrollAnchor"> & {
|
|
80
|
+
message: UIMessage;
|
|
81
|
+
messageId?: string;
|
|
82
|
+
scrollAnchor?: boolean;
|
|
83
|
+
toolRenderers?: ToolClientDefinition[];
|
|
84
|
+
}): react_jsx_runtime0.JSX.Element;
|
|
85
|
+
/**
|
|
86
|
+
* Renders the assistant activity indicator while the thread status is `submitted` or `streaming`.
|
|
87
|
+
* It returns nothing when the assistant is not responding.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```tsx
|
|
91
|
+
* <ChatThreadActivity />
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
declare function ChatThreadActivity({
|
|
95
|
+
messageId,
|
|
96
|
+
activityClassName,
|
|
97
|
+
...props
|
|
98
|
+
}: Omit<ComponentProps<typeof MessageScrollerItem>, "children" | "messageId"> & {
|
|
99
|
+
messageId?: string;
|
|
100
|
+
activityClassName?: string;
|
|
101
|
+
}): react_jsx_runtime0.JSX.Element | null;
|
|
102
|
+
/**
|
|
103
|
+
* Renders a bottom sentinel item inside the thread scroller.
|
|
104
|
+
* Use it when callers need a stable ref at the end of the message list.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* <ChatThreadBottom ref={bottomRef} />
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare function ChatThreadBottom({
|
|
112
|
+
messageId,
|
|
113
|
+
scrollAnchor,
|
|
114
|
+
...props
|
|
115
|
+
}: ComponentProps<"div"> & {
|
|
116
|
+
messageId?: string;
|
|
117
|
+
scrollAnchor?: boolean;
|
|
118
|
+
}): react_jsx_runtime0.JSX.Element;
|
|
119
|
+
/**
|
|
120
|
+
* Renders the scroll-to-bottom button for `ChatThreadContent`.
|
|
121
|
+
* `ChatThreadContent` includes this button by default.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```tsx
|
|
125
|
+
* <ChatThreadScrollButton />
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
declare function ChatThreadScrollButton(props: ComponentProps<typeof MessageScrollerButton>): react_jsx_runtime0.JSX.Element;
|
|
129
|
+
//#endregion
|
|
130
|
+
export { ChatThread, ChatThreadActivity, ChatThreadBottom, ChatThreadContent, ChatThreadMessage, ChatThreadProps, ChatThreadScrollButton };
|