diff --git a/web/api/chat.py b/web/api/chat.py new file mode 100644 index 0000000..4518077 --- /dev/null +++ b/web/api/chat.py @@ -0,0 +1,32 @@ +# ---------------------------------- web code ---------------------------------- + +import json +import dataclasses +from http.server import BaseHTTPRequestHandler + +@dataclasses.dataclass +class Block: + title: str + author: str + date: str + url: str + tags: str + text: str + +class Encoder(json.JSONEncoder): + def default(self, o): + return dataclasses.asdict(o) if dataclasses.is_dataclass(o) else super().default(o) + +class handler(BaseHTTPRequestHandler): + + def do_POST(self): + + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length) + data = json.loads(post_data) + + self.wfile.write(f"no, you're a {data['query']}".encode()) diff --git a/web/src/pages/index.tsx b/web/src/pages/index.tsx index 5d23515..a3885ca 100644 --- a/web/src/pages/index.tsx +++ b/web/src/pages/index.tsx @@ -10,17 +10,13 @@ type Entry = { const ShowEntry: React.FC<{entry: Entry}> = ({entry}) => { if (entry.role === "user") { - return ( -

{entry.text}

- ); + return (

{entry.text}

); } return (
- { // split into paragraphs on "\n" - entry.text.split("\n").map((paragraph, i) => ( -

{paragraph}

- )) + { // split into paragraphs + entry.text.split("\n").map((paragraph, i) => (

{paragraph}

)) }
); @@ -37,6 +33,40 @@ const Home: NextPage = () => { { role: "demon", text: "No one will mourn your species when it is gone. A hundred year wave of radio and information will ring out across a dead cosmos, reflecting on shores more distant and beautiful than you can possibly conceive. No one is out there to listen."} ]); + const [ query, setQuery ] = useState(""); + const [ loading, setLoading ] = useState(false); + + + const search = async (query: string) => { + + // clear the query box, append to entries + const old_entries = entries; + const new_entries: Entry[] = [...old_entries, {role: "user", text: query}]; + setEntries(new_entries); + setQuery(""); + + setLoading(true); + + const res = await fetch("/api/chat", { + method: "POST", + headers: { "Content-Type": "application/json", }, + body: JSON.stringify({query: query}), + }) + + if (!res.ok) { + setLoading(false); + return "load failure: " + res.status; + } + + const response = res.body!.getReader().read().then(({value}) => { + return new TextDecoder("utf-8").decode(value); + }); + + setEntries([...new_entries, {role: "demon", text: await response}]); + + setLoading(false); + }; + return ( <> @@ -68,103 +98,27 @@ const Home: NextPage = () => { ))} - + { loading ?

loading...

: +
{ // store in a form so that submits + e.preventDefault(); + await search(query); + }}> + + setQuery(e.target.value)} + /> + + + } + ); }; -// Round trip test. If this works, our heavier usecase probably will (famous last words) -// The one real difference is we'll want to send back a series of results as we get -// them back from OpenAI - I think we can just do this with a websocket, which -// shouldn't be too much harder. - -type SemanticEntry = { - title: string; - author: string; - date: string; - url: string; - tags: string; - text: string; -}; - -const ShowSemanticEntry: React.FC<{entry: SemanticEntry}> = ({entry}) => { - return ( -
- - {/* horizontally split first row, title on left, author on right */} -
-

{entry.title}

-

{entry.author} - {entry.date}

-
- -

{entry.text}

- - Read more -
- ); -}; - -const SearchBox: React.FC = () => { - - const [query, setQuery] = useState(""); - const [results, setResults] = useState([]); - const [loading, setLoading] = useState(false); - - const semantic_search = async (query: String) => { - - setLoading(true); - - const res = await fetch("/api/semantic_search", { - method: "POST", - headers: { "Content-Type": "application/json", }, - body: JSON.stringify({query: query}), - }) - - if (!res.ok) { - setLoading(false); - return "load failure: " + res.status; - } - - const data = await res.json(); - setLoading(false); - return data; - }; - - - - return ( - <> -
{ // store in a form so that submits - e.preventDefault(); - setResults(await semantic_search(query)); - }}> - - setQuery(e.target.value)} - /> - - - - { - loading ?

loading...

: - typeof results === "string" ?

{results}

: -
    - {results.map((result, i) => ( -
  • - -
  • - ))} -
- } - - ); -}; - - export default Home;