@mitodl/smoot-design 0.0.0-a2e4e1e → 0.0.0-bafccd3
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/README.md +4 -0
- package/dist/bundles/{remoteTutorDrawer.es.js → aiDrawerManager.es.js} +17612 -17240
- package/dist/bundles/aiDrawerManager.es.js.map +1 -0
- package/dist/bundles/aiDrawerManager.umd.js +245 -0
- package/dist/bundles/aiDrawerManager.umd.js.map +1 -0
- package/dist/cjs/VERSION.d.ts +12 -0
- package/dist/cjs/VERSION.js +15 -0
- package/dist/cjs/ai.d.ts +3 -3
- package/dist/cjs/ai.js +5 -1
- package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.d.ts → AiDrawer/AiDrawer.d.ts} +11 -13
- package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.js → AiDrawer/AiDrawer.js} +27 -41
- package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts → AiDrawer/AiDrawer.stories.d.ts} +4 -3
- package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.js → AiDrawer/AiDrawer.stories.js} +32 -51
- package/dist/cjs/bundles/AiDrawer/AiDrawerManager.d.ts +12 -0
- package/dist/cjs/bundles/AiDrawer/AiDrawerManager.js +51 -0
- package/dist/cjs/bundles/AiDrawer/AiDrawerManager.stories.d.ts +6 -0
- package/dist/cjs/bundles/AiDrawer/AiDrawerManager.stories.js +267 -0
- package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.test.js → AiDrawer/AiDrawerManager.test.js} +22 -18
- package/dist/cjs/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.js +1 -1
- package/dist/cjs/bundles/aiDrawerManager.d.ts +6 -0
- package/dist/cjs/bundles/aiDrawerManager.js +44 -0
- package/dist/cjs/components/AiChat/AiChat.d.ts +3 -3
- package/dist/cjs/components/AiChat/AiChat.js +65 -53
- package/dist/cjs/components/AiChat/AiChat.stories.d.ts +0 -4
- package/dist/cjs/components/AiChat/AiChat.stories.js +5 -54
- package/dist/cjs/components/AiChat/AiChatContext.d.ts +26 -0
- package/dist/cjs/components/AiChat/AiChatContext.js +106 -0
- package/dist/cjs/components/AiChat/AiChatContext.stories.d.ts +14 -0
- package/dist/cjs/components/AiChat/AiChatContext.stories.js +75 -0
- package/dist/cjs/components/AiChat/AiChatMarkdown.stories.d.ts +15 -0
- package/dist/cjs/components/AiChat/AiChatMarkdown.stories.js +282 -0
- package/dist/cjs/components/AiChat/Markdown.d.ts +7 -0
- package/dist/cjs/components/AiChat/Markdown.js +14 -0
- package/dist/cjs/components/AiChat/test-utils/api.js +40 -1
- package/dist/cjs/components/AiChat/types.d.ts +25 -11
- package/dist/cjs/components/AiChat/types.js +1 -1
- package/dist/cjs/components/AiChat/utils.d.ts +1 -1
- package/dist/cjs/components/AiChat/utils.js +1 -1
- package/dist/cjs/components/LinkAdapter/LinkAdapter.js +1 -1
- package/dist/cjs/components/TabButtons/TabButtonList.js +1 -1
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/utils/retryingFetch.d.ts +19 -0
- package/dist/cjs/utils/retryingFetch.js +98 -0
- package/dist/cjs/utils/retryingFetch.test.js +48 -0
- package/dist/esm/VERSION.d.ts +12 -0
- package/dist/esm/VERSION.js +12 -0
- package/dist/esm/ai.d.ts +3 -3
- package/dist/esm/ai.js +2 -1
- package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.d.ts → AiDrawer/AiDrawer.d.ts} +11 -13
- package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.js → AiDrawer/AiDrawer.js} +26 -40
- package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.d.ts → AiDrawer/AiDrawer.stories.d.ts} +4 -3
- package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.stories.js → AiDrawer/AiDrawer.stories.js} +31 -50
- package/dist/esm/bundles/AiDrawer/AiDrawerManager.d.ts +12 -0
- package/dist/esm/bundles/AiDrawer/AiDrawerManager.js +48 -0
- package/dist/esm/bundles/AiDrawer/AiDrawerManager.stories.d.ts +6 -0
- package/dist/esm/bundles/AiDrawer/AiDrawerManager.stories.js +264 -0
- package/dist/esm/bundles/AiDrawer/AiDrawerManager.test.d.ts +1 -0
- package/dist/esm/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.test.js → AiDrawer/AiDrawerManager.test.js} +22 -18
- package/dist/esm/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.js +1 -1
- package/dist/esm/bundles/aiDrawerManager.d.ts +6 -0
- package/dist/esm/bundles/aiDrawerManager.js +41 -0
- package/dist/esm/components/AiChat/AiChat.d.ts +3 -3
- package/dist/esm/components/AiChat/AiChat.js +65 -54
- package/dist/esm/components/AiChat/AiChat.stories.d.ts +0 -4
- package/dist/esm/components/AiChat/AiChat.stories.js +4 -53
- package/dist/esm/components/AiChat/AiChatContext.d.ts +26 -0
- package/dist/esm/components/AiChat/AiChatContext.js +102 -0
- package/dist/esm/components/AiChat/AiChatContext.stories.d.ts +14 -0
- package/dist/esm/components/AiChat/AiChatContext.stories.js +72 -0
- package/dist/esm/components/AiChat/AiChatMarkdown.stories.d.ts +15 -0
- package/dist/esm/components/AiChat/AiChatMarkdown.stories.js +279 -0
- package/dist/esm/components/AiChat/Markdown.d.ts +7 -0
- package/dist/esm/components/AiChat/Markdown.js +12 -0
- package/dist/esm/components/AiChat/test-utils/api.js +40 -1
- package/dist/esm/components/AiChat/types.d.ts +25 -11
- package/dist/esm/components/AiChat/types.js +1 -1
- package/dist/esm/components/AiChat/utils.d.ts +1 -1
- package/dist/esm/components/AiChat/utils.js +1 -1
- package/dist/esm/components/LinkAdapter/LinkAdapter.js +1 -1
- package/dist/esm/components/TabButtons/TabButtonList.js +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/utils/retryingFetch.d.ts +19 -0
- package/dist/esm/utils/retryingFetch.js +96 -0
- package/dist/esm/utils/retryingFetch.test.d.ts +1 -0
- package/dist/esm/utils/retryingFetch.test.js +46 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -7
- package/dist/bundles/remoteTutorDrawer.umd.js +0 -207
- package/dist/cjs/bundles/remoteTutorDrawer.d.ts +0 -7
- package/dist/cjs/bundles/remoteTutorDrawer.js +0 -40
- package/dist/esm/bundles/remoteTutorDrawer.d.ts +0 -7
- package/dist/esm/bundles/remoteTutorDrawer.js +0 -37
- /package/dist/cjs/bundles/{RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts → AiDrawer/AiDrawerManager.test.d.ts} +0 -0
- /package/dist/cjs/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.d.ts +0 -0
- /package/dist/{esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.test.d.ts → cjs/utils/retryingFetch.test.d.ts} +0 -0
- /package/dist/esm/bundles/{RemoteTutorDrawer → AiDrawer}/FlashcardsScreen.d.ts +0 -0
|
@@ -10,21 +10,21 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import * as React from "react";
|
|
13
|
-
import { useEffect, useRef, useState
|
|
13
|
+
import { useEffect, useRef, useState } from "react";
|
|
14
14
|
import styled from "@emotion/styled";
|
|
15
15
|
import Typography from "@mui/material/Typography";
|
|
16
16
|
import classNames from "classnames";
|
|
17
17
|
import { RiSendPlaneFill, RiStopFill, RiMoreFill } from "@remixicon/react";
|
|
18
18
|
import { Input, AdornmentButton } from "../Input/Input";
|
|
19
19
|
import { EntryScreen } from "./EntryScreen";
|
|
20
|
-
import Markdown from "react-markdown";
|
|
21
20
|
import { ScrollSnap } from "../ScrollSnap/ScrollSnap";
|
|
22
21
|
import { SrAnnouncer } from "../SrAnnouncer/SrAnnouncer";
|
|
23
22
|
import { VisuallyHidden } from "../VisuallyHidden/VisuallyHidden";
|
|
24
23
|
import { Alert } from "../Alert/Alert";
|
|
25
24
|
import { ChatTitle } from "./ChatTitle";
|
|
26
|
-
import { useAiChat } from "./
|
|
25
|
+
import { AiChatProvider, useAiChat } from "./AiChatContext";
|
|
27
26
|
import { useScrollSnap } from "../ScrollSnap/useScrollSnap";
|
|
27
|
+
import Markdown from "./Markdown";
|
|
28
28
|
const classes = {
|
|
29
29
|
root: "MitAiChat--root",
|
|
30
30
|
title: "MitAiChat--title",
|
|
@@ -62,6 +62,9 @@ const MessagesContainer = styled(ScrollSnap)(({ externalScroll }) => ({
|
|
|
62
62
|
padding: "14px 0",
|
|
63
63
|
overflow: externalScroll ? "visible" : "auto",
|
|
64
64
|
gap: "16px",
|
|
65
|
+
[`> .${classes.messageRowAssistant}:first-child`]: {
|
|
66
|
+
marginTop: "16px",
|
|
67
|
+
},
|
|
65
68
|
}));
|
|
66
69
|
const MessageRow = styled.div({
|
|
67
70
|
display: "flex",
|
|
@@ -75,25 +78,45 @@ const MessageRow = styled.div({
|
|
|
75
78
|
},
|
|
76
79
|
position: "relative",
|
|
77
80
|
});
|
|
78
|
-
const Message = styled.div(({ theme }) => (Object.assign(Object.assign({ color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.white
|
|
81
|
+
const Message = styled.div(({ theme }) => (Object.assign(Object.assign({ color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.white }, theme.typography.body2), { "p:first-of-type": {
|
|
79
82
|
marginTop: 0,
|
|
80
83
|
}, "p:last-of-type": {
|
|
81
84
|
marginBottom: 0,
|
|
82
|
-
}, "ol,
|
|
85
|
+
}, "ol,ul": {
|
|
83
86
|
paddingInlineStart: "16px",
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
margin: "12px 0 12px 4px",
|
|
88
|
+
}, "ol > li, ul > li": {
|
|
89
|
+
margin: "12px 0",
|
|
90
|
+
"ol,ul": {
|
|
91
|
+
margin: "12px 0 12px 4px",
|
|
87
92
|
},
|
|
88
93
|
li: {
|
|
89
|
-
margin: "
|
|
94
|
+
margin: "6px 0",
|
|
95
|
+
},
|
|
96
|
+
}, ul: {
|
|
97
|
+
paddingInlineStart: 0,
|
|
98
|
+
"> li": {
|
|
99
|
+
listStyleType: "none",
|
|
100
|
+
position: "relative",
|
|
101
|
+
"&::before": {
|
|
102
|
+
content: '"–"',
|
|
103
|
+
position: "absolute",
|
|
104
|
+
left: 0,
|
|
105
|
+
color: "#888",
|
|
106
|
+
marginRight: "8px",
|
|
107
|
+
},
|
|
108
|
+
paddingLeft: "16px",
|
|
109
|
+
},
|
|
110
|
+
}, "ol + ul": {
|
|
111
|
+
marginLeft: "24px",
|
|
112
|
+
li: {
|
|
113
|
+
margin: "6px 0",
|
|
90
114
|
},
|
|
91
115
|
}, a: {
|
|
92
116
|
color: theme.custom.colors.red,
|
|
93
117
|
fontWeight: "normal",
|
|
94
|
-
}, borderRadius: "12px", [`.${classes.
|
|
95
|
-
padding: "12px 16px
|
|
96
|
-
}, [`.${classes.messageRowUser} &`]: {
|
|
118
|
+
}, borderRadius: "12px", [`.${classes.messageRowUser} &`]: {
|
|
119
|
+
padding: "12px 16px",
|
|
97
120
|
borderRadius: "8px 0px 8px 8px",
|
|
98
121
|
backgroundColor: theme.custom.colors.lightGray1,
|
|
99
122
|
} })));
|
|
@@ -128,47 +151,32 @@ const Disclaimer = styled(Typography)(({ theme }) => ({
|
|
|
128
151
|
marginTop: "16px",
|
|
129
152
|
textAlign: "center",
|
|
130
153
|
}));
|
|
131
|
-
const
|
|
154
|
+
const AiChatDisplay = (_a) => {
|
|
132
155
|
var _b, _c;
|
|
133
|
-
var {
|
|
156
|
+
var { conversationStarters, askTimTitle, entryScreenEnabled = true, entryScreenTitle, srLoadingMessages, placeholder = "", className, scrollElement, ref, useMathJax = false } = _a, others = __rest(_a, ["conversationStarters", "askTimTitle", "entryScreenEnabled", "entryScreenTitle", "srLoadingMessages", "placeholder", "className", "scrollElement", "ref", "useMathJax"]) // Could contain data attributes
|
|
134
157
|
;
|
|
135
158
|
const containerRef = useRef(null);
|
|
136
159
|
const messagesContainerRef = useRef(null);
|
|
137
|
-
const [showEntryScreen, setShowEntryScreen] = useState(entryScreenEnabled);
|
|
138
160
|
const chatScreenRef = useRef(null);
|
|
139
|
-
const [initialMessages, setInitialMessages] = useState();
|
|
140
161
|
const promptInputRef = useRef(null);
|
|
141
|
-
const { messages
|
|
142
|
-
initialMessages,
|
|
143
|
-
id: chatId,
|
|
144
|
-
});
|
|
162
|
+
const { messages, input, handleInputChange, handleSubmit, append, isLoading, stop, error, initialMessages, status, } = useAiChat();
|
|
145
163
|
useScrollSnap({
|
|
146
164
|
scrollElement: scrollElement || messagesContainerRef.current,
|
|
147
165
|
contentElement: scrollElement ? messagesContainerRef.current : null,
|
|
148
166
|
threshold: 200,
|
|
149
167
|
});
|
|
150
|
-
|
|
151
|
-
if (_initialMessages) {
|
|
152
|
-
const prefix = Math.random().toString().slice(2);
|
|
153
|
-
setInitialMessages(_initialMessages.map((m, i) => (Object.assign(Object.assign({}, m), { id: `initial-${prefix}-${i}` }))));
|
|
154
|
-
}
|
|
155
|
-
}, [_initialMessages]);
|
|
168
|
+
const [showEntryScreen, setShowEntryScreen] = useState(entryScreenEnabled);
|
|
156
169
|
useEffect(() => {
|
|
157
170
|
var _a, _b;
|
|
158
171
|
if (!showEntryScreen) {
|
|
159
172
|
(_b = (_a = promptInputRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("input")) === null || _b === void 0 ? void 0 : _b.focus();
|
|
160
173
|
}
|
|
161
174
|
}, [showEntryScreen]);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return Object.assign(Object.assign({}, m), { content });
|
|
168
|
-
}
|
|
169
|
-
return m;
|
|
170
|
-
});
|
|
171
|
-
}, [parseContent, unparsed, initialMessages]);
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (messages.some((m) => m.role === "user" || ["submitted", "streaming"].includes(status))) {
|
|
177
|
+
setShowEntryScreen(false);
|
|
178
|
+
}
|
|
179
|
+
}, [messages, status]);
|
|
172
180
|
const showStarters = messages.length === ((initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.length) || 0);
|
|
173
181
|
const waiting = !showStarters && !error && ((_b = messages[messages.length - 1]) === null || _b === void 0 ? void 0 : _b.role) === "user";
|
|
174
182
|
const stoppable = isLoading && ((_c = messages[messages.length - 1]) === null || _c === void 0 ? void 0 : _c.role) !== "user";
|
|
@@ -181,16 +189,7 @@ const AiChat = (_a) => {
|
|
|
181
189
|
};
|
|
182
190
|
const lastMsg = messages[messages.length - 1];
|
|
183
191
|
const externalScroll = !!scrollElement;
|
|
184
|
-
return (React.createElement(Container, { className: className, ref: containerRef,
|
|
185
|
-
/**
|
|
186
|
-
* Changing the `useChat` chatId seems to persist some state between
|
|
187
|
-
* hook calls. This can cause strange effects like loading API responses
|
|
188
|
-
* for previous chatId into new chatId.
|
|
189
|
-
*
|
|
190
|
-
* To avoid this, let's change the key, this will force React to make a new component
|
|
191
|
-
* not sharing any of the old state.
|
|
192
|
-
*/
|
|
193
|
-
key: chatId }, showEntryScreen ? (React.createElement(EntryScreen, { className: classes.entryScreenContainer, title: entryScreenTitle, conversationStarters: conversationStarters, onPromptSubmit: (prompt) => {
|
|
192
|
+
return (React.createElement(Container, { className: className, ref: containerRef }, showEntryScreen ? (React.createElement(EntryScreen, { className: classes.entryScreenContainer, title: entryScreenTitle, conversationStarters: conversationStarters, onPromptSubmit: (prompt) => {
|
|
194
193
|
if (prompt.trim() === "") {
|
|
195
194
|
return;
|
|
196
195
|
}
|
|
@@ -200,13 +199,20 @@ const AiChat = (_a) => {
|
|
|
200
199
|
React.createElement(ChatContainer, Object.assign({ className: classNames(className, classes.root), externalScroll: externalScroll }, others),
|
|
201
200
|
React.createElement(ChatTitle, { askTimTitle: askTimTitle, externalScroll: externalScroll, className: classNames(className, classes.title) }),
|
|
202
201
|
React.createElement(MessagesContainer, { className: classes.messagesContainer, externalScroll: externalScroll, ref: messagesContainerRef },
|
|
203
|
-
messages.map((m) =>
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
messages.map((m, i) => {
|
|
203
|
+
// Our Markdown+Mathjax has issues when rendering streaming display math
|
|
204
|
+
// Force a re-render of the last (streaming) message when it's done loading.
|
|
205
|
+
const key = i === messages.length - 1 && isLoading
|
|
206
|
+
? `isLoading-${m.id}`
|
|
207
|
+
: m.id;
|
|
208
|
+
return (React.createElement(MessageRow, { key: key, "data-chat-role": m.role, className: classNames(classes.messageRow, {
|
|
209
|
+
[classes.messageRowUser]: m.role === "user",
|
|
210
|
+
[classes.messageRowAssistant]: m.role === "assistant",
|
|
211
|
+
}) },
|
|
212
|
+
React.createElement(Message, { className: classes.message },
|
|
213
|
+
React.createElement(VisuallyHidden, { as: m.role === "user" ? "h5" : "h6" }, m.role === "user" ? "You said: " : "Assistant said: "),
|
|
214
|
+
React.createElement(Markdown, { enableMathjax: useMathJax }, m.content))));
|
|
215
|
+
}),
|
|
210
216
|
showStarters ? (React.createElement(StarterContainer, null, conversationStarters === null || conversationStarters === void 0 ? void 0 : conversationStarters.map((m) => (React.createElement(Starter, { className: classes.conversationStarter, key: m.content, onClick: () => {
|
|
211
217
|
scrollToBottom();
|
|
212
218
|
append({ role: "user", content: m.content });
|
|
@@ -241,4 +247,9 @@ const AiChat = (_a) => {
|
|
|
241
247
|
React.createElement(Disclaimer, { variant: "body3" }, "AI-generated content may be incorrect.")),
|
|
242
248
|
React.createElement(SrAnnouncer, { isLoading: isLoading, loadingMessages: srLoadingMessages, message: (lastMsg === null || lastMsg === void 0 ? void 0 : lastMsg.role) === "assistant" ? lastMsg === null || lastMsg === void 0 ? void 0 : lastMsg.content : "" }))))));
|
|
243
249
|
};
|
|
244
|
-
|
|
250
|
+
const AiChat = (_a) => {
|
|
251
|
+
var { requestOpts, initialMessages, chatId, parseContent } = _a, displayProps = __rest(_a, ["requestOpts", "initialMessages", "chatId", "parseContent"]);
|
|
252
|
+
return (React.createElement(AiChatProvider, { requestOpts: requestOpts, chatId: chatId, initialMessages: initialMessages, parseContent: parseContent },
|
|
253
|
+
React.createElement(AiChatDisplay, Object.assign({}, displayProps))));
|
|
254
|
+
};
|
|
255
|
+
export { AiChatDisplay, AiChat };
|
|
@@ -9,10 +9,6 @@ export declare const StreamingResponses: Story;
|
|
|
9
9
|
* to text via `parseContent`.
|
|
10
10
|
*/
|
|
11
11
|
export declare const JsonResponses: Story;
|
|
12
|
-
/**
|
|
13
|
-
* This story shows the component's builtin markdown styling.
|
|
14
|
-
*/
|
|
15
|
-
export declare const MarkdownStyling: Story;
|
|
16
12
|
/**
|
|
17
13
|
* Where a scrollable container exists in the including component, it can be passed in
|
|
18
14
|
* for the AiChat component to use in place of its own height constrained message container
|
|
@@ -3,6 +3,7 @@ import { AiChat } from "./AiChat";
|
|
|
3
3
|
import styled from "@emotion/styled";
|
|
4
4
|
import { handlers } from "./test-utils/api";
|
|
5
5
|
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
import { MathJaxContext } from "better-react-mathjax";
|
|
6
7
|
const TEST_API_STREAMING = "http://localhost:4567/streaming";
|
|
7
8
|
const TEST_API_JSON = "http://localhost:4567/json";
|
|
8
9
|
const INITIAL_MESSAGES = [
|
|
@@ -36,8 +37,9 @@ const meta = {
|
|
|
36
37
|
},
|
|
37
38
|
render: (args) => React.createElement(AiChat, Object.assign({}, args)),
|
|
38
39
|
decorators: (Story, context) => {
|
|
39
|
-
return (React.createElement(
|
|
40
|
-
React.createElement(
|
|
40
|
+
return (React.createElement(MathJaxContext, null,
|
|
41
|
+
React.createElement(Container, null,
|
|
42
|
+
React.createElement(Story, { key: String(context.args.entryScreenEnabled) }))));
|
|
41
43
|
},
|
|
42
44
|
args: {
|
|
43
45
|
entryScreenTitle: "What do you want to learn from MIT?",
|
|
@@ -74,57 +76,6 @@ export const JsonResponses = {
|
|
|
74
76
|
},
|
|
75
77
|
},
|
|
76
78
|
};
|
|
77
|
-
const DEMO_MARKDOWN = `This shows default markdown styling. Here's are some lists:
|
|
78
|
-
- Item 1
|
|
79
|
-
- Item 2 - an unordered list (bullets)
|
|
80
|
-
- Point A
|
|
81
|
-
- Point B
|
|
82
|
-
- Item 3 - an ordered list (numbers)
|
|
83
|
-
1. Item 3.1
|
|
84
|
-
2. Item 3.2
|
|
85
|
-
3. Item 3.3
|
|
86
|
-
|
|
87
|
-
Sometimes, unordered lists are nested within ordered lists:
|
|
88
|
-
1. Item 1
|
|
89
|
-
- Item 1.1
|
|
90
|
-
- Item 1.2
|
|
91
|
-
2. Item 2
|
|
92
|
-
- Item 2.1
|
|
93
|
-
- Item 2.2
|
|
94
|
-
|
|
95
|
-
Sometimes, ordered lists are nested within unordered lists:
|
|
96
|
-
- Item 1
|
|
97
|
-
1. Item 1.1
|
|
98
|
-
2. Item 1.2
|
|
99
|
-
- Item 2
|
|
100
|
-
1. Item 2.1
|
|
101
|
-
2. Item 2.2
|
|
102
|
-
|
|
103
|
-
Here is a longer paragraph and **bold text** and *italic text*. Lorem ipsum dolor sit amet, consectetur adipiscing elit
|
|
104
|
-
sed do eiusmod tempor [incididunt](https://mit.edu) ut labore et dolore magna aliqua. Ut enim ad minim veniam.
|
|
105
|
-
|
|
106
|
-
And some inline code, \`\`<inline></inline>\`\` and code block:
|
|
107
|
-
\`\`\`
|
|
108
|
-
def f(x):
|
|
109
|
-
print(x)
|
|
110
|
-
\`\`\`
|
|
111
|
-
`;
|
|
112
|
-
/**
|
|
113
|
-
* This story shows the component's builtin markdown styling.
|
|
114
|
-
*/
|
|
115
|
-
export const MarkdownStyling = {
|
|
116
|
-
args: {
|
|
117
|
-
requestOpts: { apiUrl: TEST_API_STREAMING },
|
|
118
|
-
entryScreenEnabled: false,
|
|
119
|
-
conversationStarters: [],
|
|
120
|
-
initialMessages: [
|
|
121
|
-
{
|
|
122
|
-
role: "assistant",
|
|
123
|
-
content: DEMO_MARKDOWN,
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
},
|
|
127
|
-
};
|
|
128
79
|
const ScrollComponent = (args) => {
|
|
129
80
|
const ref = useRef(null);
|
|
130
81
|
const [scrollElement, setScrollElement] = useState(null);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { UseChatHelpers } from "@ai-sdk/react";
|
|
3
|
+
import type { AiChatMessage, AiChatContextProps } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* All of `@ai-sdk/react`'s [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat)
|
|
6
|
+
* results, plus the initial messages.
|
|
7
|
+
*/
|
|
8
|
+
type AiChatContextResult = UseChatHelpers & {
|
|
9
|
+
initialMessages: AiChatMessage[] | null;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Provides AiChatContext to its children. Within this provider, you can consume
|
|
13
|
+
* the AiChatContext using the `useAiChat` hook.
|
|
14
|
+
*/
|
|
15
|
+
declare const AiChatProvider: React.FC<AiChatContextProps>;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the AiChatContext, which includes all results from `@ai-sdk/react`'s
|
|
18
|
+
* [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) hook as
|
|
19
|
+
* well as the initial messages.
|
|
20
|
+
*
|
|
21
|
+
* In addition to customizing the fetcher, using a context allows us to avoid
|
|
22
|
+
* this issue https://github.com/vercel/ai/issues/3266 since the caller no
|
|
23
|
+
* longer needs to provide the initial messages.
|
|
24
|
+
*/
|
|
25
|
+
declare const useAiChat: () => AiChatContextResult;
|
|
26
|
+
export { useAiChat, AiChatProvider };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
11
|
+
var t = {};
|
|
12
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
13
|
+
t[p] = s[p];
|
|
14
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
15
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
16
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
17
|
+
t[p[i]] = s[p[i]];
|
|
18
|
+
}
|
|
19
|
+
return t;
|
|
20
|
+
};
|
|
21
|
+
import * as React from "react";
|
|
22
|
+
import { useChat } from "@ai-sdk/react";
|
|
23
|
+
import { useMemo, createContext } from "react";
|
|
24
|
+
import retryingFetch from "../../utils/retryingFetch";
|
|
25
|
+
const identity = (x) => x;
|
|
26
|
+
const getFetcher = (requestOpts) => (url, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
if (typeof (opts === null || opts === void 0 ? void 0 : opts.body) !== "string") {
|
|
29
|
+
console.error("Unexpected body type.");
|
|
30
|
+
return retryingFetch(url, opts);
|
|
31
|
+
}
|
|
32
|
+
const messages = JSON.parse(opts === null || opts === void 0 ? void 0 : opts.body).messages;
|
|
33
|
+
const transformBody = (_a = requestOpts.transformBody) !== null && _a !== void 0 ? _a : identity;
|
|
34
|
+
const options = Object.assign(Object.assign(Object.assign(Object.assign({}, opts), { body: JSON.stringify(transformBody(messages)) }), requestOpts.fetchOpts), { headers: Object.assign(Object.assign(Object.assign({}, opts === null || opts === void 0 ? void 0 : opts.headers), { "Content-Type": "application/json" }), (_b = requestOpts.fetchOpts) === null || _b === void 0 ? void 0 : _b.headers) });
|
|
35
|
+
return retryingFetch(url, options);
|
|
36
|
+
});
|
|
37
|
+
const AiChatContext = createContext(null);
|
|
38
|
+
/**
|
|
39
|
+
* Provides AiChatContext to its children. Within this provider, you can consume
|
|
40
|
+
* the AiChatContext using the `useAiChat` hook.
|
|
41
|
+
*/
|
|
42
|
+
const AiChatProvider = ({ initialMessages: _initialMessages, requestOpts, chatId, parseContent, children, }) => {
|
|
43
|
+
const initialMessages = useMemo(() => {
|
|
44
|
+
var _a;
|
|
45
|
+
return ((_a = _initialMessages === null || _initialMessages === void 0 ? void 0 : _initialMessages.map((message, i) => (Object.assign(Object.assign({}, message), { id: `initial-${i}` })))) !== null && _a !== void 0 ? _a : []);
|
|
46
|
+
}, [_initialMessages]);
|
|
47
|
+
const fetcher = useMemo(() => getFetcher(requestOpts), [requestOpts]);
|
|
48
|
+
const _a = useChat({
|
|
49
|
+
api: requestOpts.apiUrl,
|
|
50
|
+
streamProtocol: "text",
|
|
51
|
+
fetch: fetcher,
|
|
52
|
+
onFinish: (message) => {
|
|
53
|
+
var _a;
|
|
54
|
+
if (!requestOpts.onFinish)
|
|
55
|
+
return;
|
|
56
|
+
if (message.role === "assistant" || message.role === "user") {
|
|
57
|
+
(_a = requestOpts.onFinish) === null || _a === void 0 ? void 0 : _a.call(requestOpts, message);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.info("Unexpected message role.", message);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
initialMessages,
|
|
64
|
+
id: chatId,
|
|
65
|
+
}), { messages: unparsed } = _a, others = __rest(_a, ["messages"]);
|
|
66
|
+
const messages = useMemo(() => {
|
|
67
|
+
const initial = initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.map((m) => m.id);
|
|
68
|
+
return unparsed.map((m) => {
|
|
69
|
+
if (m.role === "assistant" && !(initial === null || initial === void 0 ? void 0 : initial.includes(m.id))) {
|
|
70
|
+
const content = parseContent ? parseContent(m.content) : m.content;
|
|
71
|
+
return Object.assign(Object.assign({}, m), { content });
|
|
72
|
+
}
|
|
73
|
+
return m;
|
|
74
|
+
});
|
|
75
|
+
}, [parseContent, unparsed, initialMessages]);
|
|
76
|
+
return (React.createElement(AiChatContext.Provider
|
|
77
|
+
/**
|
|
78
|
+
* Ensure that child state is reset when chatId changes.
|
|
79
|
+
*/
|
|
80
|
+
, {
|
|
81
|
+
/**
|
|
82
|
+
* Ensure that child state is reset when chatId changes.
|
|
83
|
+
*/
|
|
84
|
+
key: chatId, value: Object.assign({ initialMessages, messages }, others) }, children));
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Returns the AiChatContext, which includes all results from `@ai-sdk/react`'s
|
|
88
|
+
* [`useChat`](https://ai-sdk.dev/docs/reference/ai-sdk-ui/use-chat) hook as
|
|
89
|
+
* well as the initial messages.
|
|
90
|
+
*
|
|
91
|
+
* In addition to customizing the fetcher, using a context allows us to avoid
|
|
92
|
+
* this issue https://github.com/vercel/ai/issues/3266 since the caller no
|
|
93
|
+
* longer needs to provide the initial messages.
|
|
94
|
+
*/
|
|
95
|
+
const useAiChat = () => {
|
|
96
|
+
const context = React.useContext(AiChatContext);
|
|
97
|
+
if (!context) {
|
|
98
|
+
throw new Error("useAiChatContext must be used within an AiChatProvider");
|
|
99
|
+
}
|
|
100
|
+
return context;
|
|
101
|
+
};
|
|
102
|
+
export { useAiChat, AiChatProvider };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { AiChatProvider } from "./AiChatContext";
|
|
3
|
+
/**
|
|
4
|
+
* AiChatProvider provides state and functions for managing chat. The higher-level
|
|
5
|
+
* `AiChat` component is a wrapper around this provider and the `AiChatDisplay`,
|
|
6
|
+
* roughly.
|
|
7
|
+
*
|
|
8
|
+
* If you need to access chat state outside of the chat display, you can use
|
|
9
|
+
* `AiChatProvider` directly.
|
|
10
|
+
*/
|
|
11
|
+
declare const meta: Meta<typeof AiChatProvider>;
|
|
12
|
+
export default meta;
|
|
13
|
+
type Story = StoryObj<typeof AiChatProvider>;
|
|
14
|
+
export declare const StreamingResponses: Story;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { AiChatDisplay } from "./AiChat";
|
|
3
|
+
import { AiChatProvider, useAiChat } from "./AiChatContext";
|
|
4
|
+
import styled from "@emotion/styled";
|
|
5
|
+
import { handlers } from "./test-utils/api";
|
|
6
|
+
import Typography from "@mui/material/Typography";
|
|
7
|
+
const TEST_API_STREAMING = "http://localhost:4567/streaming";
|
|
8
|
+
const INITIAL_MESSAGES = [
|
|
9
|
+
{
|
|
10
|
+
content: "Hi! What are you interested in learning about?",
|
|
11
|
+
role: "assistant",
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
const STARTERS = [
|
|
15
|
+
{ content: "I'm interested in quantum computing" },
|
|
16
|
+
{ content: "I want to understand global warming. " },
|
|
17
|
+
{ content: "I am curious about AI applications for business" },
|
|
18
|
+
];
|
|
19
|
+
const Container = styled.div({
|
|
20
|
+
width: "100%",
|
|
21
|
+
height: "400px",
|
|
22
|
+
position: "relative",
|
|
23
|
+
});
|
|
24
|
+
const MessageCounter = () => {
|
|
25
|
+
const { messages } = useAiChat();
|
|
26
|
+
return (React.createElement(Typography, { variant: "subtitle1" },
|
|
27
|
+
"Message count: ",
|
|
28
|
+
messages.length,
|
|
29
|
+
" (Provided by ",
|
|
30
|
+
React.createElement("code", null, "AiChatContext"),
|
|
31
|
+
")"));
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* AiChatProvider provides state and functions for managing chat. The higher-level
|
|
35
|
+
* `AiChat` component is a wrapper around this provider and the `AiChatDisplay`,
|
|
36
|
+
* roughly.
|
|
37
|
+
*
|
|
38
|
+
* If you need to access chat state outside of the chat display, you can use
|
|
39
|
+
* `AiChatProvider` directly.
|
|
40
|
+
*/
|
|
41
|
+
const meta = {
|
|
42
|
+
title: "smoot-design/AI/AiChatContext",
|
|
43
|
+
component: AiChatProvider,
|
|
44
|
+
parameters: {
|
|
45
|
+
msw: { handlers },
|
|
46
|
+
},
|
|
47
|
+
render: (args) => {
|
|
48
|
+
return (React.createElement(AiChatProvider, Object.assign({}, args),
|
|
49
|
+
React.createElement(MessageCounter, null),
|
|
50
|
+
React.createElement(Container, null,
|
|
51
|
+
React.createElement(AiChatDisplay, { entryScreenEnabled: false, conversationStarters: STARTERS, placeholder: "Type your message here", askTimTitle: "Ask TIM" }))));
|
|
52
|
+
},
|
|
53
|
+
decorators: (Story) => {
|
|
54
|
+
return (React.createElement(Container, null,
|
|
55
|
+
React.createElement(Story, null)));
|
|
56
|
+
},
|
|
57
|
+
args: {
|
|
58
|
+
requestOpts: { apiUrl: TEST_API_STREAMING },
|
|
59
|
+
initialMessages: INITIAL_MESSAGES,
|
|
60
|
+
},
|
|
61
|
+
argTypes: {
|
|
62
|
+
initialMessages: {
|
|
63
|
+
control: { type: "object", disable: true },
|
|
64
|
+
},
|
|
65
|
+
requestOpts: {
|
|
66
|
+
control: { type: "object", disable: true },
|
|
67
|
+
table: { readonly: true }, // See above
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
export default meta;
|
|
72
|
+
export const StreamingResponses = {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { AiChat } from "./AiChat";
|
|
3
|
+
declare const meta: Meta<typeof AiChat>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof AiChat>;
|
|
6
|
+
export declare const Typical: Story;
|
|
7
|
+
export declare const Textual: Story;
|
|
8
|
+
export declare const Math: Story;
|
|
9
|
+
export declare const SimpleOrderedList: Story;
|
|
10
|
+
export declare const SimpleUnorderedList: Story;
|
|
11
|
+
export declare const NestedOrderedList: Story;
|
|
12
|
+
export declare const NestedUnorderedList: Story;
|
|
13
|
+
export declare const NestedOrderedUnorderedList: Story;
|
|
14
|
+
export declare const NestedUnorderedOrderedList: Story;
|
|
15
|
+
export declare const UnexpectedList: Story;
|