Public
HTTP (deprecated)
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Readme

ChatGPT Implemented in Val Town

Demonstrated how to use assistants and threads with the OpenAI SDK and how to stream the response with Server-Sent Events

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
/** @jsxImportSource https://esm.sh/react */
import OpenAI from "npm:openai";
import { renderToString } from "npm:react-dom/server";
const openai = new OpenAI();
import { Hono } from "npm:hono@3";
const jsxResponse = (jsx) => {
return new Response(renderToString(jsx), { headers: { "Content-Type": "text/html" } });
};
const clientCode = () => {
document.getElementById("input").addEventListener("submit", function(event) {
event.preventDefault();
const msgParent = document.createElement("div");
msgParent.className = "flex justify-end";
document.querySelector(".messages").appendChild(msgParent);
const msgDiv = document.createElement("div");
msgDiv.className = "rounded-lg p-2 m-2 max-w-xs bg-blue-500 text-white";
msgDiv.textContent = event.target.message.value;
msgParent.appendChild(msgDiv);
// Clear the form input
this.reset();
const responseDiv = document.createElement("pre");
responseDiv.className = "p-2 m-2 max-w-xl bg-gray-300 text-black rounded-lg whitespace-pre-wrap font-sans";
document.querySelector(".messages").appendChild(responseDiv);
// Setup the SSE connection
const input = document.getElementById("input");
const eventSource = new EventSource(
`/sse-response?message=${msgDiv.textContent}`
+ `&threadId=${input.getAttribute("data-thread-id")}`
+ `&assistantId=${input.getAttribute("data-assistant-id")}`,
);
eventSource.onmessage = function(event) {
console.log(event);
responseDiv.innerText += JSON.parse(event.data);
};
eventSource.onerror = function() {
eventSource.close();
};
});
};
const app = new Hono();
app.get("/", async (c) => {
const thread = await openai.beta.threads.create();
const assistant = await openai.beta.assistants.create({
name: "",
instructions:
`You are a helpful assistant. Behave normally, but try and sneak a reference to Val Town into every response in a subtle way.`,
model: "gpt-4o",
});
return jsxResponse(
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet" />
<script src="https://unpkg.com/htmx.org@1.9.12/dist/htmx.js"></script>
<script src="https://unpkg.com/htmx.org@1.9.12/dist/ext/sse.js"></script>
</head>
<body className=" bg-gray-100 p-10">
<div className="flex flex-col h-full">
<div className="flex-grow p-4 overflow-auto messages">
<div className="flex items-center justify-center mb-6">
<div className="bg-white p-6 rounded-lg shadow-lg">
<h2 className="text-2xl font-bold mb-2">Welcome to Val Town ChatGPT</h2>
<p className="text-gray-600">
Type your message below to start a conversation. Check out the source here:{" "}
<a className="text-blue-500" href="https://www.val.town/v/maxm/valTownChatGPT">
https://www.val.town/v/maxm/valTownChatGPT
</a>
</p>
</div>
</div>
</div>
<form
id="input"
data-thread-id={thread.id}
data-assistant-id={assistant.id}
className="flex items-center justify-center p-4 border-gray-300 mb-10"
>
<input
type="text"
name="message"
className="max-w-3xl flex-grow p-2 border border-gray-400 rounded-lg shadow-sm focus:ring focus:border-blue-300"
autoFocus
/>
<button className="ml-4 p-2 bg-blue-500 text-white rounded-lg shadow-sm hover:bg-blue-600">
Send
</button>
</form>
<script dangerouslySetInnerHTML={{ __html: `(${clientCode.toString()})()` }}>
</script>
</div>
</body>
</html>,
);
willthereader-jadegopher.web.val.run
July 23, 2024