File size: 7,501 Bytes
ed9ee96
 
ed45bdf
 
b801bb1
ed9ee96
5d239ba
f373356
 
c79b0f3
 
 
 
ed9ee96
 
 
 
 
70ff588
5d239ba
ed45bdf
ed9ee96
 
 
 
 
 
 
70ff588
 
 
 
81c1854
ed9ee96
 
 
 
5d239ba
 
1300e36
f373356
1300e36
f373356
 
 
 
 
70ff588
f373356
81c1854
 
 
 
 
 
70ff588
81c1854
 
 
f373356
 
 
81c1854
 
 
f1b879c
81c1854
ed9ee96
 
 
 
 
 
 
5d239ba
 
ed9ee96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f1b879c
1300e36
f1b879c
 
 
 
 
5d239ba
f1b879c
ed9ee96
 
 
 
 
ed45bdf
22ff301
 
ed45bdf
ed9ee96
 
 
 
 
 
c79b0f3
ed9ee96
 
 
c79b0f3
 
 
ed9ee96
 
 
 
 
 
 
 
b801bb1
ed9ee96
 
b801bb1
c79b0f3
ed9ee96
 
f16ab24
ed9ee96
 
 
 
 
 
 
 
 
 
 
 
 
c79b0f3
 
5d239ba
c79b0f3
b801bb1
 
ed9ee96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import useLLM from "@react-llm/headless";
import Image from "next/image";
import { useCallback, useEffect, useState } from "react";
import MessageList from './MessageList';
import {FileLoader} from './FileLoader';
import Loader from "./Loader";
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
import { XenovaTransformersEmbeddings } from '../embed/hf';
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import {
  Button,
  TextInput,
} from "react95";

function ChatWindow({
  stopStrings,
  maxTokens,
}) {
  const { loadingStatus, send, isGenerating, deleteMessages } = useLLM();
  const [fileText, setFileText] = useState();
  const [userInput, setUserInput] = useState("");  

  const handleChange = (event) => {
    setUserInput(event.target.value);
  };

  const isReady = loadingStatus.progress === 1;

  const handleClear = () => {
    deleteMessages();
  }

  const handleSubmit = useCallback(async () => {
    if (isGenerating || !isReady) {
      return;
    }
    
    if (fileText) {
      const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000 });
      const docs = await textSplitter.createDocuments([fileText]);
      let qaPrompt;
      try {
        const vectorStore = await MemoryVectorStore.fromTexts(
          [...docs.map(doc => doc.pageContent)],
          [...docs.map((v, k) => k)],
          new XenovaTransformersEmbeddings()
        )
        const queryResult = await vectorStore.similaritySearch(userInput, 2);
        qaPrompt = 
      `You are an AI assistant providing helpful advice. You are given the following extracted parts of a long document and a question. Provide a conversational answer based on the context provided.
        You should only provide hyperlinks that reference the context below. Do NOT make up hyperlinks.
        If you can't find the answer in the context below, just say "Hmm, I'm not sure." Don't try to make up an answer.
        If the question is not related to the context, politely respond that you are tuned to only answer questions that are related to the context.
        Question: ${userInput}
        =========
        ${queryResult.map(result => result.pageContent).join('')}
        =========
        Answer:
        `
      } catch (err) {
        console.log(err);
      }      
      send(qaPrompt, maxTokens, stopStrings);
    } else {
      send(userInput, maxTokens, stopStrings);
    }
    
    setUserInput("");
  }, [
    userInput,
    send,
    isGenerating,
    isReady,
    maxTokens,
    stopStrings,
    fileText
  ]);

  useEffect(() => {
    const handleKeyPress = (event) => {
      if (event.key === "Enter") {
        event.preventDefault();
        handleSubmit();
      }
    };
    window.addEventListener("keydown", handleKeyPress);

    return () => {
      window.removeEventListener("keydown", handleKeyPress);
    };
  }, [handleSubmit]);

  const loadFile = async () => {
    console.log('file loaded');
  }

  useEffect(() => {
    loadFile();

  }, [fileText])

  return (
    <div className="window sm:w-[500px] w-full">
      
      <div className="window-content w-full">
        <div className="flex flex-col w-full">
          <MessageList
            screenName={"me"}
            assistantScreenName={"vicuna"}
          />
          {/* <Separator /> */}
          <div className="h-4" />
          {isReady && (
            <div>
              <form onSubmit={handleSubmit}>
                <div className="flex">
                   <TextInput
                    value={userInput}
                    placeholder="Say something..."
                    onChange={handleChange}
                    fullWidth
                    multiline
                    rows={3}
                  />
                </div>
              </form>
              <div className="h-4">
                {isGenerating && (
                  <span>is typing...</span>
                )}
              </div>
              
              <div className="flex justify-start m-2">
                <div>
                  <div>
                  <Button
                    onClick={handleSubmit}
                    className="submit"
                    style={{ height: "65px", width: "65px", float: "right" }}
                  >
                    <Image
                      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=="
                      alt="Send Message"
                      style={{
                        filter:
                          !isReady || isGenerating
                            ? "grayscale(100%)"
                            : undefined,
                      }}
                      width="40"
                      height="40"
                    />
                  </Button>
                  
                  <FileLoader setFileText={setFileText} />
                  <Button onClick={handleClear}>Clear</Button>
                  </div>
                  
                  <div
                    className="w-full h-1 mt-2"
                    style={{
                      backgroundColor:
                        !isReady || isGenerating ? "red" : "green",
                      width: "100%",
                      height: "5px",
                      marginTop: "2px",
                    }}
                  ></div>
                </div>
              </div>
            </div>
          )}
          {!isReady && <Loader />}
        </div>
      </div>
    </div>
  );
}

export default ChatWindow;