matt HOFFNER commited on
Commit
ed9ee96
Β·
1 Parent(s): 8b233d8
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
- export { default, getServerSideProps } from './api/home';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }