mirror of
https://github.com/wassname/stampy-chat.git
synced 2026-09-11 12:50:34 +08:00
conversational flow working
This commit is contained in:
@@ -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())
|
||||
+55
-101
@@ -10,17 +10,13 @@ type Entry = {
|
||||
|
||||
const ShowEntry: React.FC<{entry: Entry}> = ({entry}) => {
|
||||
if (entry.role === "user") {
|
||||
return (
|
||||
<p className="border border-gray-300 px-1"> {entry.text} </p>
|
||||
);
|
||||
return ( <p className="border border-gray-300 px-1"> {entry.text} </p>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="my-3">
|
||||
{ // split into paragraphs on "\n"
|
||||
entry.text.split("\n").map((paragraph, i) => (
|
||||
<p key={i}> {paragraph} </p>
|
||||
))
|
||||
{ // split into paragraphs
|
||||
entry.text.split("\n").map((paragraph, i) => ( <p key={i}> {paragraph} </p>))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
@@ -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 (
|
||||
<>
|
||||
<Head>
|
||||
@@ -68,103 +98,27 @@ const Home: NextPage = () => {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<SearchBox />
|
||||
{ loading ? <p>loading...</p> :
|
||||
<form className="flex mb-2" onSubmit={async (e) => { // store in a form so that <enter> submits
|
||||
e.preventDefault();
|
||||
await search(query);
|
||||
}}>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 px-1 flex-1"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
<button className="ml-2" type="submit" disabled={loading}>
|
||||
{loading ? "Loading..." : "Search"}
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// 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 (
|
||||
<div className="my-3">
|
||||
|
||||
{/* horizontally split first row, title on left, author on right */}
|
||||
<div className="flex">
|
||||
<h3 className="text-xl flex-1">{entry.title}</h3>
|
||||
<p className="flex-1 text-right my-0">{entry.author} - {entry.date}</p>
|
||||
</div>
|
||||
|
||||
<p className="text-sm">{entry.text}</p>
|
||||
|
||||
<a href={entry.url}>Read more</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SearchBox: React.FC = () => {
|
||||
|
||||
const [query, setQuery] = useState("");
|
||||
const [results, setResults] = useState<SemanticEntry[] | string>([]);
|
||||
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 (
|
||||
<>
|
||||
<form className="flex mb-2" onSubmit={async (e) => { // store in a form so that <enter> submits
|
||||
e.preventDefault();
|
||||
setResults(await semantic_search(query));
|
||||
}}>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
className="border border-gray-300 px-1 flex-1"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
<button className="ml-2" type="submit" disabled={loading}>
|
||||
{loading ? "Loading..." : "Search"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{
|
||||
loading ? <p>loading...</p> :
|
||||
typeof results === "string" ? <p className="text-red-500">{results}</p> :
|
||||
<ul>
|
||||
{results.map((result, i) => (
|
||||
<li key={i}>
|
||||
<ShowSemanticEntry entry={result} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default Home;
|
||||
|
||||
Reference in New Issue
Block a user