Spaces:
Runtime error
Runtime error
matt HOFFNER
commited on
Commit
Β·
ed9ee96
1
Parent(s):
8b233d8
llm hooks
Browse files- src/components/ChatV2.tsx +30 -0
- src/components/ChatWindow.jsx +116 -0
- src/components/Loader.jsx +79 -0
- src/pages/index.tsx +15 -1
src/components/ChatV2.tsx
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client";
|
2 |
+
import { useState } from "react";
|
3 |
+
import ChatWindow from "./ChatWindow";
|
4 |
+
|
5 |
+
|
6 |
+
export default function Chat() {
|
7 |
+
const [screenName, setScreenName] = useState("endlessbox5");
|
8 |
+
const [stopStrings, setStopStrings] = useState(["user:", "assistant:"]);
|
9 |
+
const [maxTokens, setMaxTokens] = useState(100);
|
10 |
+
const [soundLevel, setSoundLevel] = useState(0.2);
|
11 |
+
const [showConversationList, setShowConversationList] = useState(false);
|
12 |
+
const [showOptions, setShowOptions] = useState(false);
|
13 |
+
|
14 |
+
|
15 |
+
return (
|
16 |
+
<div>
|
17 |
+
<div className="flex justify-center m-3 gap-2">
|
18 |
+
<div className="sm:w-[500px] w-full">
|
19 |
+
<ChatWindow
|
20 |
+
screenName={screenName}
|
21 |
+
assistantScreenName={"SmartestChild"}
|
22 |
+
maxTokens={maxTokens}
|
23 |
+
stopStrings={stopStrings}
|
24 |
+
soundLevel={soundLevel}
|
25 |
+
/>
|
26 |
+
</div>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
);
|
30 |
+
}
|
src/components/ChatWindow.jsx
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import useLLM from "@react-llm/headless";
|
2 |
+
import Image from "next/image";
|
3 |
+
import { SetStateAction, useCallback, useEffect, useState } from "react";
|
4 |
+
|
5 |
+
import Loader from "./Loader";
|
6 |
+
|
7 |
+
function ChatWindow({
|
8 |
+
stopStrings,
|
9 |
+
maxTokens,
|
10 |
+
}) {
|
11 |
+
const { loadingStatus, send, isGenerating, setOnMessage } = useLLM();
|
12 |
+
const [userInput, setUserInput] = useState("");
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
const handleChange = (event) => {
|
17 |
+
setUserInput(event.target.value);
|
18 |
+
};
|
19 |
+
|
20 |
+
const isReady = loadingStatus.progress === 1;
|
21 |
+
|
22 |
+
const handleSubmit = useCallback(() => {
|
23 |
+
if (isGenerating || !isReady) {
|
24 |
+
return;
|
25 |
+
}
|
26 |
+
|
27 |
+
send(userInput, maxTokens, stopStrings);
|
28 |
+
setUserInput("");
|
29 |
+
}, [
|
30 |
+
userInput,
|
31 |
+
send,
|
32 |
+
isGenerating,
|
33 |
+
isReady,
|
34 |
+
maxTokens,
|
35 |
+
stopStrings
|
36 |
+
]);
|
37 |
+
|
38 |
+
useEffect(() => {
|
39 |
+
const handleKeyPress = (event) => {
|
40 |
+
if (event.key === "Enter") {
|
41 |
+
event.preventDefault();
|
42 |
+
handleSubmit();
|
43 |
+
}
|
44 |
+
};
|
45 |
+
window.addEventListener("keydown", handleKeyPress);
|
46 |
+
|
47 |
+
return () => {
|
48 |
+
window.removeEventListener("keydown", handleKeyPress);
|
49 |
+
};
|
50 |
+
}, [handleSubmit]);
|
51 |
+
|
52 |
+
return (
|
53 |
+
<div className="window sm:w-[500px] w-full">
|
54 |
+
|
55 |
+
<div className="window-content w-full">
|
56 |
+
<div className="flex flex-col w-full">
|
57 |
+
{/* <Separator /> */}
|
58 |
+
<div className="h-4" />
|
59 |
+
{isReady && (
|
60 |
+
<div>
|
61 |
+
<form onSubmit={handleSubmit}>
|
62 |
+
<div className="flex">
|
63 |
+
<input
|
64 |
+
value={userInput}
|
65 |
+
placeholder="Say something..."
|
66 |
+
onChange={handleChange}
|
67 |
+
/>
|
68 |
+
</div>
|
69 |
+
</form>
|
70 |
+
<div className="h-4">
|
71 |
+
{isGenerating && (
|
72 |
+
<span>is typing...</span>
|
73 |
+
)}
|
74 |
+
</div>
|
75 |
+
<div className="flex justify-start m-2">
|
76 |
+
<div>
|
77 |
+
<button
|
78 |
+
onClick={handleSubmit}
|
79 |
+
className="submit"
|
80 |
+
style={{ height: "65px", width: "65px" }}
|
81 |
+
>
|
82 |
+
<Image
|
83 |
+
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAiCAYAAABIiGl0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyNpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDYuMC1jMDAyIDc5LjE2NDQ2MCwgMjAyMC8wNS8xMi0xNjowNDoxNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIDIxLjIgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjU3MUQ1NTRFMTdFQTExRUM5MDA1OTZFMzQ1Q0E2MTMxIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjU3MUQ1NTRGMTdFQTExRUM5MDA1OTZFMzQ1Q0E2MTMxIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NTcxRDU1NEMxN0VBMTFFQzkwMDU5NkUzNDVDQTYxMzEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NTcxRDU1NEQxN0VBMTFFQzkwMDU5NkUzNDVDQTYxMzEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz53k5gpAAACU0lEQVR42sRWAZKDIAyETn9k3wRv0jfBm7xsIEwuAtZee43DDKhhk+wadPu+u6sjpbQvy7Kv67q/4o/xklMIgUEBjjkC+TgwAB251WxbAFezv9MmLsaIzaZGmTkC8Nu2odTwwVruO9x/PB4IgN872+9yiTFgwrO2K9k/JSQBtEDCtTV5d8b9gT8BwRyOKMoSSERr+MWtmA3GZl8rdAD2jD4wcJ9ddgTK65xpFTPz2tY0wDG4HezB71jub+6CLXRhE55XYUFoMIirZwDEQABaxJwxOe2yoVYx7sEhpMCgW9yG2ZGiWxCTCvKeXLGZsMARD+IY/Ooh6hYTlYuqsa7i6nI/5BhVQPmQrXBb612+21i+Z8yttUoZLehqDoHRDChTh+FyAWwbb7lwmguHAnBWevhUofnbiZqKA2W3PcqIPjJwWIqwsBFArEakm1ngFuS0H8uVCi/qOz68rzpW41zrQDWbfucSUAhqNZeIrAcuQKpjteai2uuxc4kjK5a6VAOrqtbgs3aos9fHp33/mGVaf4PWtscBpdDuj1qhPlDMp+e6wBxlvQAgoBKpBMfgqczPfgAk6+khISUWHnsOwiGp/fT0efpYlE3PfmX++q/11On0Sbu5L9nXgO/tfPR+V/9h/p0gsrfe924eYM5r/dIoKH2/F7B9Xr9rfyg1QOEsn5cNarBZ95n163LcycJLZDooE/nwj/mSuGYltKfY21QNIMtrL/LKv7PVeBlYAHu8qGeaz9O0rd8hCVM6vfAT7nqV8WY+qpzvAf+b+VHZ3t1ErP0IMAAwT6EpM/krMwAAAABJRU5ErkJggg=="
|
84 |
+
alt="Send Message"
|
85 |
+
style={{
|
86 |
+
filter:
|
87 |
+
!isReady || isGenerating
|
88 |
+
? "grayscale(100%)"
|
89 |
+
: undefined,
|
90 |
+
}}
|
91 |
+
width="40"
|
92 |
+
height="40"
|
93 |
+
/>
|
94 |
+
</button>
|
95 |
+
<div
|
96 |
+
className="w-full h-1 mt-2"
|
97 |
+
style={{
|
98 |
+
backgroundColor:
|
99 |
+
!isReady || isGenerating ? "red" : "green",
|
100 |
+
width: "100%",
|
101 |
+
height: "5px",
|
102 |
+
marginTop: "2px",
|
103 |
+
}}
|
104 |
+
></div>
|
105 |
+
</div>
|
106 |
+
</div>
|
107 |
+
</div>
|
108 |
+
)}
|
109 |
+
{!isReady && <Loader />}
|
110 |
+
</div>
|
111 |
+
</div>
|
112 |
+
</div>
|
113 |
+
);
|
114 |
+
}
|
115 |
+
|
116 |
+
export default ChatWindow;
|
src/components/Loader.jsx
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import useLLM from "@react-llm/headless";
|
2 |
+
|
3 |
+
const Loader = () => {
|
4 |
+
const { loadingStatus, isReady, init, gpuDevice } = useLLM();
|
5 |
+
if (isReady) return null;
|
6 |
+
if (loadingStatus.progress === 1) return null;
|
7 |
+
|
8 |
+
if (gpuDevice.unsupportedReason) {
|
9 |
+
return (
|
10 |
+
<div style={{ fontSize: "20px" }}>
|
11 |
+
<p style={{ color: "red" }}>Sorry, unsupported!</p>
|
12 |
+
<p> Reason: {gpuDevice.unsupportedReason}</p>
|
13 |
+
<p>
|
14 |
+
<a href={"http://github.com/react-llm"}>react-llm</a> runs models in
|
15 |
+
the browser with WebGPU and only works in Google Chrome v113 and above
|
16 |
+
on Desktop with supported GPUs.
|
17 |
+
</p>
|
18 |
+
</div>
|
19 |
+
);
|
20 |
+
}
|
21 |
+
|
22 |
+
if (loadingStatus.progress == 0) {
|
23 |
+
return (
|
24 |
+
<div style={{ padding: "10px", width: "100%" }}>
|
25 |
+
<div
|
26 |
+
style={{
|
27 |
+
display: "flex",
|
28 |
+
alignItems: "center",
|
29 |
+
flexDirection: "column",
|
30 |
+
gap: "10px",
|
31 |
+
}}
|
32 |
+
>
|
33 |
+
<div className="lg:hidden">
|
34 |
+
<p className="p-1">
|
35 |
+
web-llm-embed
|
36 |
+
</p>
|
37 |
+
<p className="p-1">
|
38 |
+
No data is sent to the server. Conversations are cached in local
|
39 |
+
storage.
|
40 |
+
</p>
|
41 |
+
<p className="p-1">
|
42 |
+
WebGPU is only supported in Desktop Google Chrome 113
|
43 |
+
</p>
|
44 |
+
<p className="p-1">
|
45 |
+
Powered by Apache TVM and MLC Relax Runtime.
|
46 |
+
</p>
|
47 |
+
<p className="p-1">
|
48 |
+
Large language model is <a href="https://www.together.xyz/blog/redpajama-3b-updates">RedPajama-INCITE-3B</a>
|
49 |
+
</p>
|
50 |
+
<p className="p-1">
|
51 |
+
Embeddings are from<a href="https://github.com/xenova/transformers.js">transformers.js</a>
|
52 |
+
</p>
|
53 |
+
</div>
|
54 |
+
<div>
|
55 |
+
This will download the model and may take a few minutes. After the
|
56 |
+
first time, it will be cached.
|
57 |
+
</div>
|
58 |
+
|
59 |
+
<button
|
60 |
+
style={{ padding: "10px" }}
|
61 |
+
size="lg"
|
62 |
+
primary
|
63 |
+
onClick={() => init()}
|
64 |
+
>
|
65 |
+
Load Model
|
66 |
+
</button>
|
67 |
+
</div>
|
68 |
+
</div>
|
69 |
+
);
|
70 |
+
}
|
71 |
+
|
72 |
+
return (
|
73 |
+
<div style={{ width: "100%", margin: "10px" }}>
|
74 |
+
Loading {loadingStatus.progress * 100}%
|
75 |
+
</div>
|
76 |
+
);
|
77 |
+
};
|
78 |
+
|
79 |
+
export default Loader;
|
src/pages/index.tsx
CHANGED
@@ -1 +1,15 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client";
|
2 |
+
import Chat from "@/components/ChatV2";
|
3 |
+
import { ModelProvider } from "@react-llm/headless";
|
4 |
+
|
5 |
+
export default function Home() {
|
6 |
+
return (
|
7 |
+
<ModelProvider
|
8 |
+
config={{
|
9 |
+
persistToLocalStorage: true,
|
10 |
+
}}
|
11 |
+
>
|
12 |
+
<Chat />
|
13 |
+
</ModelProvider>
|
14 |
+
);
|
15 |
+
}
|