@mitodl/smoot-design 0.0.0-a2e4e1e → 0.0.0-cf541fb
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 +15509 -16022
- package/dist/bundles/remoteTutorDrawer.umd.js +79 -52
- package/dist/cjs/ai.d.ts +3 -3
- package/dist/cjs/ai.js +5 -1
- package/dist/cjs/bundles/RemoteTutorDrawer/FlashcardsScreen.js +1 -1
- package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +1 -2
- package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +2 -2
- package/dist/cjs/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +1 -0
- package/dist/cjs/components/AiChat/AiChat.d.ts +3 -3
- package/dist/cjs/components/AiChat/AiChat.js +20 -41
- package/dist/cjs/components/AiChat/AiChatContext.d.ts +22 -0
- package/dist/cjs/components/AiChat/AiChatContext.js +105 -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/types.d.ts +15 -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/esm/ai.d.ts +3 -3
- package/dist/esm/ai.js +2 -1
- package/dist/esm/bundles/RemoteTutorDrawer/FlashcardsScreen.js +1 -1
- package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.d.ts +1 -2
- package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.js +2 -2
- package/dist/esm/bundles/RemoteTutorDrawer/RemoteTutorDrawer.stories.js +1 -0
- package/dist/esm/components/AiChat/AiChat.d.ts +3 -3
- package/dist/esm/components/AiChat/AiChat.js +20 -42
- package/dist/esm/components/AiChat/AiChatContext.d.ts +22 -0
- package/dist/esm/components/AiChat/AiChatContext.js +101 -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/types.d.ts +15 -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/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
package/dist/cjs/ai.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { AiChat } from "./components/AiChat/AiChat";
|
|
2
|
-
export
|
|
3
|
-
export type { AiChatMessage } from "./components/AiChat/types";
|
|
1
|
+
export { AiChat, AiChatDisplay } from "./components/AiChat/AiChat";
|
|
2
|
+
export { AiChatProvider, useAiChat } from "./components/AiChat/AiChatContext";
|
|
3
|
+
export type { AiChatMessage, AiChatContextProps, AiChatDisplayProps, AiChatProps, } from "./components/AiChat/types";
|
package/dist/cjs/ai.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AiChat = void 0;
|
|
3
|
+
exports.useAiChat = exports.AiChatProvider = exports.AiChatDisplay = exports.AiChat = void 0;
|
|
4
4
|
var AiChat_1 = require("./components/AiChat/AiChat");
|
|
5
5
|
Object.defineProperty(exports, "AiChat", { enumerable: true, get: function () { return AiChat_1.AiChat; } });
|
|
6
|
+
Object.defineProperty(exports, "AiChatDisplay", { enumerable: true, get: function () { return AiChat_1.AiChatDisplay; } });
|
|
7
|
+
var AiChatContext_1 = require("./components/AiChat/AiChatContext");
|
|
8
|
+
Object.defineProperty(exports, "AiChatProvider", { enumerable: true, get: function () { return AiChatContext_1.AiChatProvider; } });
|
|
9
|
+
Object.defineProperty(exports, "useAiChat", { enumerable: true, get: function () { return AiChatContext_1.useAiChat; } });
|
|
@@ -38,7 +38,7 @@ const Flashcard = React.forwardRef(({ content, "aria-label": ariaLabel }, ref) =
|
|
|
38
38
|
setScreen(screen === 0 ? 1 : 0);
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
|
-
return (React.createElement(FlashcardContainer, { ref: ref, onClick: () => setScreen(screen === 0 ? 1 : 0), onKeyDown: handleKeyDown, tabIndex: 0, "aria-label": ariaLabel },
|
|
41
|
+
return (React.createElement(FlashcardContainer, { ref: ref, onClick: () => setScreen(screen === 0 ? 1 : 0), onKeyDown: handleKeyDown, tabIndex: 0, "aria-label": ariaLabel, role: "button" },
|
|
42
42
|
React.createElement(Typography_1.default, { variant: "h5" }, screen === 0 ? `Q: ${content.question}` : `Answer: ${content.answer}`)));
|
|
43
43
|
});
|
|
44
44
|
Flashcard.displayName = "Flashcard";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
|
-
import { AiChatMessage } from "../../components/AiChat/types";
|
|
3
|
-
import type { AiChatProps } from "../../components/AiChat/AiChat";
|
|
2
|
+
import type { AiChatProps, AiChatMessage } from "../../components/AiChat/types";
|
|
4
3
|
type RemoteTutorDrawerInitMessage = {
|
|
5
4
|
type: "smoot-design::tutor-drawer-open";
|
|
6
5
|
payload: {
|
|
@@ -248,11 +248,11 @@ const RemoteTutorDrawer = ({ messageOrigin, transformBody = identity, className,
|
|
|
248
248
|
md: "0 32px",
|
|
249
249
|
},
|
|
250
250
|
},
|
|
251
|
-
}, anchor: "right", open: open, onClose: () => setOpen(false) },
|
|
251
|
+
}, anchor: "right", open: open, onClose: () => setOpen(false), role: "dialog", "aria-modal": "true" },
|
|
252
252
|
React.createElement(Header, null,
|
|
253
253
|
React.createElement(Title, null,
|
|
254
254
|
payload.title ? React.createElement(react_2.RiSparkling2Line, null) : null,
|
|
255
|
-
React.createElement(Typography_1.default, { variant: "body1", component: "
|
|
255
|
+
React.createElement(Typography_1.default, { variant: "body1", component: "h1" }, ((_b = payload.title) === null || _b === void 0 ? void 0 : _b.includes("AskTIM")) ? (React.createElement(React.Fragment, null,
|
|
256
256
|
"Ask",
|
|
257
257
|
React.createElement("strong", null, "TIM"),
|
|
258
258
|
payload.title.replace("AskTIM", ""))) : (payload.title))),
|
|
@@ -54,6 +54,7 @@ const IFrame = ({ payload }) => {
|
|
|
54
54
|
};
|
|
55
55
|
const meta = {
|
|
56
56
|
title: "smoot-design/AI/RemoteTutorDrawer",
|
|
57
|
+
component: RemoteTutorDrawer_1.RemoteTutorDrawer,
|
|
57
58
|
render: ({ target }, { parameters: { payload } }) => (React.createElement(React.Fragment, null,
|
|
58
59
|
React.createElement(IFrame, { payload: payload }),
|
|
59
60
|
React.createElement(RemoteTutorDrawer_1.RemoteTutorDrawer, { target: target, messageOrigin: "http://localhost:6006" }))),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FC } from "react";
|
|
2
|
-
import type { AiChatProps } from "./types";
|
|
2
|
+
import type { AiChatDisplayProps, AiChatProps } from "./types";
|
|
3
|
+
declare const AiChatDisplay: FC<AiChatDisplayProps>;
|
|
3
4
|
declare const AiChat: FC<AiChatProps>;
|
|
4
|
-
export { AiChat };
|
|
5
|
-
export type { AiChatProps };
|
|
5
|
+
export { AiChatDisplay, AiChat };
|
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.AiChat = void 0;
|
|
14
|
+
exports.AiChat = exports.AiChatDisplay = void 0;
|
|
15
15
|
const React = require("react");
|
|
16
16
|
const react_1 = require("react");
|
|
17
17
|
const styled_1 = require("@emotion/styled");
|
|
@@ -19,15 +19,15 @@ const Typography_1 = require("@mui/material/Typography");
|
|
|
19
19
|
const classnames_1 = require("classnames");
|
|
20
20
|
const react_2 = require("@remixicon/react");
|
|
21
21
|
const Input_1 = require("../Input/Input");
|
|
22
|
-
const EntryScreen_1 = require("./EntryScreen");
|
|
23
22
|
const react_markdown_1 = require("react-markdown");
|
|
24
23
|
const ScrollSnap_1 = require("../ScrollSnap/ScrollSnap");
|
|
25
24
|
const SrAnnouncer_1 = require("../SrAnnouncer/SrAnnouncer");
|
|
26
25
|
const VisuallyHidden_1 = require("../VisuallyHidden/VisuallyHidden");
|
|
27
26
|
const Alert_1 = require("../Alert/Alert");
|
|
28
27
|
const ChatTitle_1 = require("./ChatTitle");
|
|
29
|
-
const
|
|
28
|
+
const AiChatContext_1 = require("./AiChatContext");
|
|
30
29
|
const useScrollSnap_1 = require("../ScrollSnap/useScrollSnap");
|
|
30
|
+
const EntryScreen_1 = require("./EntryScreen");
|
|
31
31
|
const classes = {
|
|
32
32
|
root: "MitAiChat--root",
|
|
33
33
|
title: "MitAiChat--title",
|
|
@@ -65,6 +65,9 @@ const MessagesContainer = (0, styled_1.default)(ScrollSnap_1.ScrollSnap)(({ exte
|
|
|
65
65
|
padding: "14px 0",
|
|
66
66
|
overflow: externalScroll ? "visible" : "auto",
|
|
67
67
|
gap: "16px",
|
|
68
|
+
[`> .${classes.messageRowAssistant}:first-child`]: {
|
|
69
|
+
marginTop: "16px",
|
|
70
|
+
},
|
|
68
71
|
}));
|
|
69
72
|
const MessageRow = styled_1.default.div({
|
|
70
73
|
display: "flex",
|
|
@@ -78,7 +81,7 @@ const MessageRow = styled_1.default.div({
|
|
|
78
81
|
},
|
|
79
82
|
position: "relative",
|
|
80
83
|
});
|
|
81
|
-
const Message = styled_1.default.div(({ theme }) => (Object.assign(Object.assign({ color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.white
|
|
84
|
+
const Message = styled_1.default.div(({ theme }) => (Object.assign(Object.assign({ color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.white }, theme.typography.body2), { "p:first-of-type": {
|
|
82
85
|
marginTop: 0,
|
|
83
86
|
}, "p:last-of-type": {
|
|
84
87
|
marginBottom: 0,
|
|
@@ -94,9 +97,8 @@ const Message = styled_1.default.div(({ theme }) => (Object.assign(Object.assign
|
|
|
94
97
|
}, a: {
|
|
95
98
|
color: theme.custom.colors.red,
|
|
96
99
|
fontWeight: "normal",
|
|
97
|
-
}, borderRadius: "12px", [`.${classes.
|
|
98
|
-
padding: "12px 16px
|
|
99
|
-
}, [`.${classes.messageRowUser} &`]: {
|
|
100
|
+
}, borderRadius: "12px", [`.${classes.messageRowUser} &`]: {
|
|
101
|
+
padding: "12px 16px",
|
|
100
102
|
borderRadius: "8px 0px 8px 8px",
|
|
101
103
|
backgroundColor: theme.custom.colors.lightGray1,
|
|
102
104
|
} })));
|
|
@@ -131,47 +133,27 @@ const Disclaimer = (0, styled_1.default)(Typography_1.default)(({ theme }) => ({
|
|
|
131
133
|
marginTop: "16px",
|
|
132
134
|
textAlign: "center",
|
|
133
135
|
}));
|
|
134
|
-
const
|
|
136
|
+
const AiChatDisplay = (_a) => {
|
|
135
137
|
var _b, _c;
|
|
136
|
-
var {
|
|
138
|
+
var { conversationStarters, askTimTitle, entryScreenEnabled = true, entryScreenTitle, srLoadingMessages, placeholder = "", className, scrollElement, ref } = _a, others = __rest(_a, ["conversationStarters", "askTimTitle", "entryScreenEnabled", "entryScreenTitle", "srLoadingMessages", "placeholder", "className", "scrollElement", "ref"]) // Could contain data attributes
|
|
137
139
|
;
|
|
138
140
|
const containerRef = (0, react_1.useRef)(null);
|
|
139
141
|
const messagesContainerRef = (0, react_1.useRef)(null);
|
|
140
|
-
const [showEntryScreen, setShowEntryScreen] = (0, react_1.useState)(entryScreenEnabled);
|
|
141
142
|
const chatScreenRef = (0, react_1.useRef)(null);
|
|
142
|
-
const [initialMessages, setInitialMessages] = (0, react_1.useState)();
|
|
143
143
|
const promptInputRef = (0, react_1.useRef)(null);
|
|
144
|
-
const { messages
|
|
145
|
-
initialMessages,
|
|
146
|
-
id: chatId,
|
|
147
|
-
});
|
|
144
|
+
const { messages, input, handleInputChange, handleSubmit, append, isLoading, stop, error, initialMessages, } = (0, AiChatContext_1.useAiChat)();
|
|
148
145
|
(0, useScrollSnap_1.useScrollSnap)({
|
|
149
146
|
scrollElement: scrollElement || messagesContainerRef.current,
|
|
150
147
|
contentElement: scrollElement ? messagesContainerRef.current : null,
|
|
151
148
|
threshold: 200,
|
|
152
149
|
});
|
|
153
|
-
(0, react_1.
|
|
154
|
-
if (_initialMessages) {
|
|
155
|
-
const prefix = Math.random().toString().slice(2);
|
|
156
|
-
setInitialMessages(_initialMessages.map((m, i) => (Object.assign(Object.assign({}, m), { id: `initial-${prefix}-${i}` }))));
|
|
157
|
-
}
|
|
158
|
-
}, [_initialMessages]);
|
|
150
|
+
const [showEntryScreen, setShowEntryScreen] = (0, react_1.useState)(entryScreenEnabled);
|
|
159
151
|
(0, react_1.useEffect)(() => {
|
|
160
152
|
var _a, _b;
|
|
161
153
|
if (!showEntryScreen) {
|
|
162
154
|
(_b = (_a = promptInputRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("input")) === null || _b === void 0 ? void 0 : _b.focus();
|
|
163
155
|
}
|
|
164
156
|
}, [showEntryScreen]);
|
|
165
|
-
const messages = (0, react_1.useMemo)(() => {
|
|
166
|
-
const initial = initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.map((m) => m.id);
|
|
167
|
-
return unparsed.map((m) => {
|
|
168
|
-
if (m.role === "assistant" && !(initial === null || initial === void 0 ? void 0 : initial.includes(m.id))) {
|
|
169
|
-
const content = parseContent ? parseContent(m.content) : m.content;
|
|
170
|
-
return Object.assign(Object.assign({}, m), { content });
|
|
171
|
-
}
|
|
172
|
-
return m;
|
|
173
|
-
});
|
|
174
|
-
}, [parseContent, unparsed, initialMessages]);
|
|
175
157
|
const showStarters = messages.length === ((initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.length) || 0);
|
|
176
158
|
const waiting = !showStarters && !error && ((_b = messages[messages.length - 1]) === null || _b === void 0 ? void 0 : _b.role) === "user";
|
|
177
159
|
const stoppable = isLoading && ((_c = messages[messages.length - 1]) === null || _c === void 0 ? void 0 : _c.role) !== "user";
|
|
@@ -184,16 +166,7 @@ const AiChat = (_a) => {
|
|
|
184
166
|
};
|
|
185
167
|
const lastMsg = messages[messages.length - 1];
|
|
186
168
|
const externalScroll = !!scrollElement;
|
|
187
|
-
return (React.createElement(Container, { className: className, ref: containerRef,
|
|
188
|
-
/**
|
|
189
|
-
* Changing the `useChat` chatId seems to persist some state between
|
|
190
|
-
* hook calls. This can cause strange effects like loading API responses
|
|
191
|
-
* for previous chatId into new chatId.
|
|
192
|
-
*
|
|
193
|
-
* To avoid this, let's change the key, this will force React to make a new component
|
|
194
|
-
* not sharing any of the old state.
|
|
195
|
-
*/
|
|
196
|
-
key: chatId }, showEntryScreen ? (React.createElement(EntryScreen_1.EntryScreen, { className: classes.entryScreenContainer, title: entryScreenTitle, conversationStarters: conversationStarters, onPromptSubmit: (prompt) => {
|
|
169
|
+
return (React.createElement(Container, { className: className, ref: containerRef }, showEntryScreen ? (React.createElement(EntryScreen_1.EntryScreen, { className: classes.entryScreenContainer, title: entryScreenTitle, conversationStarters: conversationStarters, onPromptSubmit: (prompt) => {
|
|
197
170
|
if (prompt.trim() === "") {
|
|
198
171
|
return;
|
|
199
172
|
}
|
|
@@ -244,4 +217,10 @@ const AiChat = (_a) => {
|
|
|
244
217
|
React.createElement(Disclaimer, { variant: "body3" }, "AI-generated content may be incorrect.")),
|
|
245
218
|
React.createElement(SrAnnouncer_1.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 : "" }))))));
|
|
246
219
|
};
|
|
220
|
+
exports.AiChatDisplay = AiChatDisplay;
|
|
221
|
+
const AiChat = (_a) => {
|
|
222
|
+
var { requestOpts, initialMessages, chatId, parseContent } = _a, displayProps = __rest(_a, ["requestOpts", "initialMessages", "chatId", "parseContent"]);
|
|
223
|
+
return (React.createElement(AiChatContext_1.AiChatProvider, { requestOpts: requestOpts, chatId: chatId, initialMessages: initialMessages, parseContent: parseContent },
|
|
224
|
+
React.createElement(AiChatDisplay, Object.assign({}, displayProps))));
|
|
225
|
+
};
|
|
247
226
|
exports.AiChat = AiChat;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { UseChatHelpers } from "@ai-sdk/react";
|
|
3
|
+
import type { AiChatMessage, AiChatContextProps } from "./types";
|
|
4
|
+
type AiChatContextResult = UseChatHelpers & {
|
|
5
|
+
initialMessages: AiChatMessage[] | null;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Provides AiChatContext to its children. Within this provider, you can consume
|
|
9
|
+
* the AiChatContext using the `useAiChat` hook.
|
|
10
|
+
*/
|
|
11
|
+
declare const AiChatProvider: React.FC<AiChatContextProps>;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the AiChatContext.
|
|
14
|
+
*
|
|
15
|
+
* This is largely a wrapper around `useChat` from `@ai-sdk/react`, but it also provides
|
|
16
|
+
* the initial messages and a custom fetcher.
|
|
17
|
+
*
|
|
18
|
+
* Using a context avoids this issue https://github.com/vercel/ai/issues/3266
|
|
19
|
+
* since the caller no longer needs to provide the initial messages.
|
|
20
|
+
*/
|
|
21
|
+
declare const useAiChat: () => AiChatContextResult;
|
|
22
|
+
export { useAiChat, AiChatProvider };
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
14
|
+
t[p] = s[p];
|
|
15
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
16
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
17
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
18
|
+
t[p[i]] = s[p[i]];
|
|
19
|
+
}
|
|
20
|
+
return t;
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.AiChatProvider = exports.useAiChat = void 0;
|
|
24
|
+
const React = require("react");
|
|
25
|
+
const react_1 = require("@ai-sdk/react");
|
|
26
|
+
const react_2 = require("react");
|
|
27
|
+
const identity = (x) => x;
|
|
28
|
+
const getFetcher = (requestOpts) => (url, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
29
|
+
var _a, _b;
|
|
30
|
+
if (typeof (opts === null || opts === void 0 ? void 0 : opts.body) !== "string") {
|
|
31
|
+
console.error("Unexpected body type.");
|
|
32
|
+
return window.fetch(url, opts);
|
|
33
|
+
}
|
|
34
|
+
const messages = JSON.parse(opts === null || opts === void 0 ? void 0 : opts.body).messages;
|
|
35
|
+
const transformBody = (_a = requestOpts.transformBody) !== null && _a !== void 0 ? _a : identity;
|
|
36
|
+
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) });
|
|
37
|
+
return fetch(url, options);
|
|
38
|
+
});
|
|
39
|
+
const AiChatContext = (0, react_2.createContext)(null);
|
|
40
|
+
/**
|
|
41
|
+
* Provides AiChatContext to its children. Within this provider, you can consume
|
|
42
|
+
* the AiChatContext using the `useAiChat` hook.
|
|
43
|
+
*/
|
|
44
|
+
const AiChatProvider = ({ initialMessages: _initialMessages, requestOpts, chatId, parseContent, children, }) => {
|
|
45
|
+
const initialMessages = (0, react_2.useMemo)(() => {
|
|
46
|
+
var _a;
|
|
47
|
+
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 : []);
|
|
48
|
+
}, [_initialMessages]);
|
|
49
|
+
const fetcher = (0, react_2.useMemo)(() => getFetcher(requestOpts), [requestOpts]);
|
|
50
|
+
const _a = (0, react_1.useChat)({
|
|
51
|
+
api: requestOpts.apiUrl,
|
|
52
|
+
streamProtocol: "text",
|
|
53
|
+
fetch: fetcher,
|
|
54
|
+
onFinish: (message) => {
|
|
55
|
+
var _a;
|
|
56
|
+
if (!requestOpts.onFinish)
|
|
57
|
+
return;
|
|
58
|
+
if (message.role === "assistant" || message.role === "user") {
|
|
59
|
+
(_a = requestOpts.onFinish) === null || _a === void 0 ? void 0 : _a.call(requestOpts, message);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.info("Unexpected message role.", message);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
initialMessages,
|
|
66
|
+
id: chatId,
|
|
67
|
+
}), { messages: unparsed } = _a, others = __rest(_a, ["messages"]);
|
|
68
|
+
const messages = (0, react_2.useMemo)(() => {
|
|
69
|
+
const initial = initialMessages === null || initialMessages === void 0 ? void 0 : initialMessages.map((m) => m.id);
|
|
70
|
+
return unparsed.map((m) => {
|
|
71
|
+
if (m.role === "assistant" && !(initial === null || initial === void 0 ? void 0 : initial.includes(m.id))) {
|
|
72
|
+
const content = parseContent ? parseContent(m.content) : m.content;
|
|
73
|
+
return Object.assign(Object.assign({}, m), { content });
|
|
74
|
+
}
|
|
75
|
+
return m;
|
|
76
|
+
});
|
|
77
|
+
}, [parseContent, unparsed, initialMessages]);
|
|
78
|
+
return (React.createElement(AiChatContext.Provider
|
|
79
|
+
/**
|
|
80
|
+
* Ensure that child state is reset when chatId changes.
|
|
81
|
+
*/
|
|
82
|
+
, {
|
|
83
|
+
/**
|
|
84
|
+
* Ensure that child state is reset when chatId changes.
|
|
85
|
+
*/
|
|
86
|
+
key: chatId, value: Object.assign({ initialMessages, messages }, others) }, children));
|
|
87
|
+
};
|
|
88
|
+
exports.AiChatProvider = AiChatProvider;
|
|
89
|
+
/**
|
|
90
|
+
* Returns the AiChatContext.
|
|
91
|
+
*
|
|
92
|
+
* This is largely a wrapper around `useChat` from `@ai-sdk/react`, but it also provides
|
|
93
|
+
* the initial messages and a custom fetcher.
|
|
94
|
+
*
|
|
95
|
+
* Using a context avoids this issue https://github.com/vercel/ai/issues/3266
|
|
96
|
+
* since the caller no longer needs to provide the initial messages.
|
|
97
|
+
*/
|
|
98
|
+
const useAiChat = () => {
|
|
99
|
+
const context = React.useContext(AiChatContext);
|
|
100
|
+
if (!context) {
|
|
101
|
+
throw new Error("useAiChatContext must be used within an AiChatProvider");
|
|
102
|
+
}
|
|
103
|
+
return context;
|
|
104
|
+
};
|
|
105
|
+
exports.useAiChat = useAiChat;
|
|
@@ -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,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StreamingResponses = void 0;
|
|
4
|
+
const React = require("react");
|
|
5
|
+
const AiChat_1 = require("./AiChat");
|
|
6
|
+
const AiChatContext_1 = require("./AiChatContext");
|
|
7
|
+
const styled_1 = require("@emotion/styled");
|
|
8
|
+
const api_1 = require("./test-utils/api");
|
|
9
|
+
const Typography_1 = require("@mui/material/Typography");
|
|
10
|
+
const TEST_API_STREAMING = "http://localhost:4567/streaming";
|
|
11
|
+
const INITIAL_MESSAGES = [
|
|
12
|
+
{
|
|
13
|
+
content: "Hi! What are you interested in learning about?",
|
|
14
|
+
role: "assistant",
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
const STARTERS = [
|
|
18
|
+
{ content: "I'm interested in quantum computing" },
|
|
19
|
+
{ content: "I want to understand global warming. " },
|
|
20
|
+
{ content: "I am curious about AI applications for business" },
|
|
21
|
+
];
|
|
22
|
+
const Container = styled_1.default.div({
|
|
23
|
+
width: "100%",
|
|
24
|
+
height: "400px",
|
|
25
|
+
position: "relative",
|
|
26
|
+
});
|
|
27
|
+
const MessageCounter = () => {
|
|
28
|
+
const { messages } = (0, AiChatContext_1.useAiChat)();
|
|
29
|
+
return (React.createElement(Typography_1.default, { variant: "subtitle1" },
|
|
30
|
+
"Message count: ",
|
|
31
|
+
messages.length,
|
|
32
|
+
" (Provided by ",
|
|
33
|
+
React.createElement("code", null, "AiChatContext"),
|
|
34
|
+
")"));
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* AiChatProvider provides state and functions for managing chat. The higher-level
|
|
38
|
+
* `AiChat` component is a wrapper around this provider and the `AiChatDisplay`,
|
|
39
|
+
* roughly.
|
|
40
|
+
*
|
|
41
|
+
* If you need to access chat state outside of the chat display, you can use
|
|
42
|
+
* `AiChatProvider` directly.
|
|
43
|
+
*/
|
|
44
|
+
const meta = {
|
|
45
|
+
title: "smoot-design/AI/AiChatContext",
|
|
46
|
+
component: AiChatContext_1.AiChatProvider,
|
|
47
|
+
parameters: {
|
|
48
|
+
msw: { handlers: api_1.handlers },
|
|
49
|
+
},
|
|
50
|
+
render: (args) => {
|
|
51
|
+
return (React.createElement(AiChatContext_1.AiChatProvider, Object.assign({}, args),
|
|
52
|
+
React.createElement(MessageCounter, null),
|
|
53
|
+
React.createElement(Container, null,
|
|
54
|
+
React.createElement(AiChat_1.AiChatDisplay, { entryScreenEnabled: false, conversationStarters: STARTERS, placeholder: "Type your message here", askTimTitle: "Ask TIM" }))));
|
|
55
|
+
},
|
|
56
|
+
decorators: (Story) => {
|
|
57
|
+
return (React.createElement(Container, null,
|
|
58
|
+
React.createElement(Story, null)));
|
|
59
|
+
},
|
|
60
|
+
args: {
|
|
61
|
+
requestOpts: { apiUrl: TEST_API_STREAMING },
|
|
62
|
+
initialMessages: INITIAL_MESSAGES,
|
|
63
|
+
},
|
|
64
|
+
argTypes: {
|
|
65
|
+
initialMessages: {
|
|
66
|
+
control: { type: "object", disable: true },
|
|
67
|
+
},
|
|
68
|
+
requestOpts: {
|
|
69
|
+
control: { type: "object", disable: true },
|
|
70
|
+
table: { readonly: true }, // See above
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
exports.default = meta;
|
|
75
|
+
exports.StreamingResponses = {};
|
|
@@ -20,12 +20,24 @@ type RequestOpts = {
|
|
|
20
20
|
fetchOpts?: RequestInit;
|
|
21
21
|
onFinish?: (message: AiChatMessage) => void;
|
|
22
22
|
};
|
|
23
|
-
type
|
|
23
|
+
type AiChatContextProps = {
|
|
24
24
|
/**
|
|
25
25
|
* Changing the `chatId` will reset the chat. Changing the `chatId` to a
|
|
26
26
|
* previously used value will restore the session state.
|
|
27
27
|
*/
|
|
28
28
|
chatId?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Options for making requests to the AI service.
|
|
31
|
+
*/
|
|
32
|
+
requestOpts: RequestOpts;
|
|
33
|
+
parseContent?: (content: unknown) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Initial messages to display on the chat. If not provided, the entry screen title will be used as the initial message.
|
|
36
|
+
*/
|
|
37
|
+
initialMessages?: Omit<AiChatMessage, "id">[];
|
|
38
|
+
children?: React.ReactNode;
|
|
39
|
+
};
|
|
40
|
+
type AiChatDisplayProps = {
|
|
29
41
|
/**
|
|
30
42
|
* If provided, renders the "AskTIM" title motif followed by the text.
|
|
31
43
|
*/
|
|
@@ -44,21 +56,12 @@ type AiChatProps = {
|
|
|
44
56
|
* Title to display on the entry screen, also the initial assistant message if not overridden by `initialMessages`.
|
|
45
57
|
*/
|
|
46
58
|
entryScreenTitle?: string;
|
|
47
|
-
/**
|
|
48
|
-
* Initial messages to display on the chat. If not provided, the entry screen title will be used as the initial message.
|
|
49
|
-
*/
|
|
50
|
-
initialMessages?: Omit<AiChatMessage, "id">[];
|
|
51
59
|
/**
|
|
52
60
|
* Prompt suggestions for the user, clickable on the entry screen or in the chat if the entry screen is not enabled.
|
|
53
61
|
*/
|
|
54
62
|
conversationStarters?: {
|
|
55
63
|
content: string;
|
|
56
64
|
}[];
|
|
57
|
-
/**
|
|
58
|
-
* Options for making requests to the AI service.
|
|
59
|
-
*/
|
|
60
|
-
requestOpts: RequestOpts;
|
|
61
|
-
parseContent?: (content: unknown) => string;
|
|
62
65
|
/**
|
|
63
66
|
* A message to display while the component is in a loading state.
|
|
64
67
|
*
|
|
@@ -79,4 +82,5 @@ type AiChatProps = {
|
|
|
79
82
|
*/
|
|
80
83
|
scrollElement?: HTMLElement | null;
|
|
81
84
|
} & RefAttributes<HTMLDivElement>;
|
|
82
|
-
|
|
85
|
+
type AiChatProps = AiChatContextProps & AiChatDisplayProps;
|
|
86
|
+
export type { RequestOpts, AiChatMessage, AiChatContextProps, AiChatDisplayProps, AiChatProps, };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// Some of these are based on (compatible, but simplified / restricted) versions of ai/react types.
|
|
2
|
+
// Some of these are based on (compatible, but simplified / restricted) versions of @ai-sdk/react types.
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UseChatOptions } from "ai/react";
|
|
1
|
+
import { UseChatOptions } from "@ai-sdk/react";
|
|
2
2
|
import type { RequestOpts } from "./types";
|
|
3
3
|
declare const useAiChat: (requestOpts: RequestOpts, opts: UseChatOptions) => import("@ai-sdk/react").UseChatHelpers & {
|
|
4
4
|
addToolResult: ({ toolCallId, result, }: {
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.useAiChat = void 0;
|
|
13
|
-
const react_1 = require("ai/react");
|
|
13
|
+
const react_1 = require("@ai-sdk/react");
|
|
14
14
|
const react_2 = require("react");
|
|
15
15
|
const identity = (x) => x;
|
|
16
16
|
const getFetcher = (requestOpts) => (url, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -29,6 +29,6 @@ const LinkAdapter = React.forwardRef(function LinkAdapter(_a, ref) {
|
|
|
29
29
|
var { Component } = _a, props = __rest(_a, ["Component"]);
|
|
30
30
|
const theme = (0, react_1.useTheme)();
|
|
31
31
|
const LinkComponent = Component !== null && Component !== void 0 ? Component : theme.custom.LinkAdapter;
|
|
32
|
-
return React.createElement(PlainLink, Object.assign({ as: LinkComponent, ref: ref }, props));
|
|
32
|
+
return (React.createElement(PlainLink, Object.assign({ as: LinkComponent, ref: ref }, props), props.children));
|
|
33
33
|
});
|
|
34
34
|
exports.LinkAdapter = LinkAdapter;
|
|
@@ -77,7 +77,7 @@ const TabButtonInner = React.forwardRef(
|
|
|
77
77
|
TabButtonInner.displayName = "TabButtonInner";
|
|
78
78
|
const TabLinkInner = React.forwardRef((props, ref) => {
|
|
79
79
|
const { className } = props, others = __rest(props, ["className"]);
|
|
80
|
-
return React.createElement(TabLinkStyled, Object.assign({}, defaultTabButtonProps, others, { ref: ref }));
|
|
80
|
+
return (React.createElement(TabLinkStyled, Object.assign({}, defaultTabButtonProps, others, { ref: ref }), props.children));
|
|
81
81
|
});
|
|
82
82
|
TabLinkInner.displayName = "TabLinkInner";
|
|
83
83
|
/**
|
package/dist/esm/ai.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { AiChat } from "./components/AiChat/AiChat";
|
|
2
|
-
export
|
|
3
|
-
export type { AiChatMessage } from "./components/AiChat/types";
|
|
1
|
+
export { AiChat, AiChatDisplay } from "./components/AiChat/AiChat";
|
|
2
|
+
export { AiChatProvider, useAiChat } from "./components/AiChat/AiChatContext";
|
|
3
|
+
export type { AiChatMessage, AiChatContextProps, AiChatDisplayProps, AiChatProps, } from "./components/AiChat/types";
|
package/dist/esm/ai.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { AiChat } from "./components/AiChat/AiChat";
|
|
1
|
+
export { AiChat, AiChatDisplay } from "./components/AiChat/AiChat";
|
|
2
|
+
export { AiChatProvider, useAiChat } from "./components/AiChat/AiChatContext";
|
|
@@ -35,7 +35,7 @@ const Flashcard = React.forwardRef(({ content, "aria-label": ariaLabel }, ref) =
|
|
|
35
35
|
setScreen(screen === 0 ? 1 : 0);
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
return (React.createElement(FlashcardContainer, { ref: ref, onClick: () => setScreen(screen === 0 ? 1 : 0), onKeyDown: handleKeyDown, tabIndex: 0, "aria-label": ariaLabel },
|
|
38
|
+
return (React.createElement(FlashcardContainer, { ref: ref, onClick: () => setScreen(screen === 0 ? 1 : 0), onKeyDown: handleKeyDown, tabIndex: 0, "aria-label": ariaLabel, role: "button" },
|
|
39
39
|
React.createElement(Typography, { variant: "h5" }, screen === 0 ? `Q: ${content.question}` : `Answer: ${content.answer}`)));
|
|
40
40
|
});
|
|
41
41
|
Flashcard.displayName = "Flashcard";
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { FC } from "react";
|
|
2
|
-
import { AiChatMessage } from "../../components/AiChat/types";
|
|
3
|
-
import type { AiChatProps } from "../../components/AiChat/AiChat";
|
|
2
|
+
import type { AiChatProps, AiChatMessage } from "../../components/AiChat/types";
|
|
4
3
|
type RemoteTutorDrawerInitMessage = {
|
|
5
4
|
type: "smoot-design::tutor-drawer-open";
|
|
6
5
|
payload: {
|
|
@@ -245,11 +245,11 @@ const RemoteTutorDrawer = ({ messageOrigin, transformBody = identity, className,
|
|
|
245
245
|
md: "0 32px",
|
|
246
246
|
},
|
|
247
247
|
},
|
|
248
|
-
}, anchor: "right", open: open, onClose: () => setOpen(false) },
|
|
248
|
+
}, anchor: "right", open: open, onClose: () => setOpen(false), role: "dialog", "aria-modal": "true" },
|
|
249
249
|
React.createElement(Header, null,
|
|
250
250
|
React.createElement(Title, null,
|
|
251
251
|
payload.title ? React.createElement(RiSparkling2Line, null) : null,
|
|
252
|
-
React.createElement(Typography, { variant: "body1", component: "
|
|
252
|
+
React.createElement(Typography, { variant: "body1", component: "h1" }, ((_b = payload.title) === null || _b === void 0 ? void 0 : _b.includes("AskTIM")) ? (React.createElement(React.Fragment, null,
|
|
253
253
|
"Ask",
|
|
254
254
|
React.createElement("strong", null, "TIM"),
|
|
255
255
|
payload.title.replace("AskTIM", ""))) : (payload.title))),
|
|
@@ -51,6 +51,7 @@ const IFrame = ({ payload }) => {
|
|
|
51
51
|
};
|
|
52
52
|
const meta = {
|
|
53
53
|
title: "smoot-design/AI/RemoteTutorDrawer",
|
|
54
|
+
component: RemoteTutorDrawer,
|
|
54
55
|
render: ({ target }, { parameters: { payload } }) => (React.createElement(React.Fragment, null,
|
|
55
56
|
React.createElement(IFrame, { payload: payload }),
|
|
56
57
|
React.createElement(RemoteTutorDrawer, { target: target, messageOrigin: "http://localhost:6006" }))),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FC } from "react";
|
|
2
|
-
import type { AiChatProps } from "./types";
|
|
2
|
+
import type { AiChatDisplayProps, AiChatProps } from "./types";
|
|
3
|
+
declare const AiChatDisplay: FC<AiChatDisplayProps>;
|
|
3
4
|
declare const AiChat: FC<AiChatProps>;
|
|
4
|
-
export { AiChat };
|
|
5
|
-
export type { AiChatProps };
|
|
5
|
+
export { AiChatDisplay, AiChat };
|