Skip to content

Commit 39cd4d6

Browse files
committed
Use Together SDK
1 parent 1faa464 commit 39cd4d6

7 files changed

Lines changed: 378 additions & 260 deletions

File tree

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

app/(main)/page.tsx

Lines changed: 71 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ import { ArrowUpOnSquareIcon } from "@heroicons/react/24/outline";
1111
import * as Select from "@radix-ui/react-select";
1212
import * as Switch from "@radix-ui/react-switch";
1313
import * as Tooltip from "@radix-ui/react-tooltip";
14-
import {
15-
createParser,
16-
ParsedEvent,
17-
ReconnectInterval,
18-
} from "eventsource-parser";
1914
import { AnimatePresence, motion } from "framer-motion";
2015
import { FormEvent, useEffect, useState } from "react";
2116
import { toast, Toaster } from "sonner";
@@ -26,6 +21,24 @@ export default function Home() {
2621
let [status, setStatus] = useState<
2722
"initial" | "creating" | "created" | "updating" | "updated"
2823
>("initial");
24+
let [prompt, setPrompt] = useState("");
25+
let models = [
26+
{
27+
label: "Llama 3.1 405B",
28+
value: "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
29+
},
30+
{
31+
label: "Llama 3.1 70B",
32+
value: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
33+
},
34+
{
35+
label: "Gemma 2 27B",
36+
value: "google/gemma-2-27b-it",
37+
},
38+
];
39+
let [model, setModel] = useState(models[0].value);
40+
let [shadcn, setShadcn] = useState(false);
41+
let [modification, setModification] = useState("");
2942
let [generatedCode, setGeneratedCode] = useState("");
3043
let [initialAppConfig, setInitialAppConfig] = useState({
3144
model: "",
@@ -39,7 +52,7 @@ export default function Home() {
3952

4053
let loading = status === "creating" || status === "updating";
4154

42-
async function generateCode(e: FormEvent<HTMLFormElement>) {
55+
async function createApp(e: FormEvent<HTMLFormElement>) {
4356
e.preventDefault();
4457

4558
if (status !== "initial") {
@@ -49,134 +62,70 @@ export default function Home() {
4962
setStatus("creating");
5063
setGeneratedCode("");
5164

52-
let formData = new FormData(e.currentTarget);
53-
let model = formData.get("model");
54-
let prompt = formData.get("prompt");
55-
let shadcn = !!formData.get("shadcn");
56-
if (typeof prompt !== "string" || typeof model !== "string") {
57-
return;
58-
}
59-
let newMessages = [{ role: "user", content: prompt }];
60-
61-
const chatRes = await fetch("/api/generateCode", {
65+
let res = await fetch("/api/generateCode", {
6266
method: "POST",
6367
headers: {
6468
"Content-Type": "application/json",
6569
},
6670
body: JSON.stringify({
67-
messages: newMessages,
6871
model,
6972
shadcn,
73+
messages: [{ role: "user", content: prompt }],
7074
}),
7175
});
72-
if (!chatRes.ok) {
73-
throw new Error(chatRes.statusText);
74-
}
7576

76-
// This data is a ReadableStream
77-
const data = chatRes.body;
78-
if (!data) {
79-
return;
77+
if (!res.ok) {
78+
throw new Error(res.statusText);
8079
}
81-
const onParse = (event: ParsedEvent | ReconnectInterval) => {
82-
if (event.type === "event") {
83-
const data = event.data;
84-
try {
85-
const text = JSON.parse(data).text ?? "";
86-
setGeneratedCode((prev) => prev + text);
87-
} catch (e) {
88-
console.error(e);
89-
}
90-
}
91-
};
92-
93-
// https://web.dev/streams/#the-getreader-and-read-methods
94-
const reader = data.getReader();
95-
const decoder = new TextDecoder();
96-
const parser = createParser(onParse);
97-
let done = false;
98-
99-
while (!done) {
100-
const { value, done: doneReading } = await reader.read();
101-
done = doneReading;
102-
const chunkValue = decoder.decode(value);
103-
parser.feed(chunkValue);
80+
81+
if (!res.body) {
82+
throw new Error("No response body");
10483
}
10584

106-
newMessages = [
107-
...newMessages,
108-
{ role: "assistant", content: generatedCode },
109-
];
85+
for await (let chunk of readStream(res.body)) {
86+
setGeneratedCode((prev) => prev + chunk);
87+
}
11088

89+
setMessages([{ role: "user", content: prompt }]);
11190
setInitialAppConfig({ model, shadcn });
112-
setMessages(newMessages);
11391
setStatus("created");
11492
}
11593

116-
async function modifyCode(e: FormEvent<HTMLFormElement>) {
94+
async function updateApp(e: FormEvent<HTMLFormElement>) {
11795
e.preventDefault();
11896

11997
setStatus("updating");
12098

121-
let formData = new FormData(e.currentTarget);
122-
let prompt = formData.get("prompt");
123-
if (typeof prompt !== "string") {
124-
return;
125-
}
126-
let newMessages = [...messages, { role: "user", content: prompt }];
99+
let codeMessage = { role: "assistant", content: generatedCode };
100+
let modificationMessage = { role: "user", content: modification };
127101

128102
setGeneratedCode("");
129-
const chatRes = await fetch("/api/generateCode", {
103+
104+
const res = await fetch("/api/generateCode", {
130105
method: "POST",
131106
headers: {
132107
"Content-Type": "application/json",
133108
},
134109
body: JSON.stringify({
135-
messages: newMessages,
110+
messages: [...messages, codeMessage, modificationMessage],
136111
model: initialAppConfig.model,
137112
shadcn: initialAppConfig.shadcn,
138113
}),
139114
});
140-
if (!chatRes.ok) {
141-
throw new Error(chatRes.statusText);
142-
}
143115

144-
// This data is a ReadableStream
145-
const data = chatRes.body;
146-
if (!data) {
147-
return;
116+
if (!res.ok) {
117+
throw new Error(res.statusText);
148118
}
149-
const onParse = (event: ParsedEvent | ReconnectInterval) => {
150-
if (event.type === "event") {
151-
const data = event.data;
152-
try {
153-
const text = JSON.parse(data).text ?? "";
154-
setGeneratedCode((prev) => prev + text);
155-
} catch (e) {
156-
console.error(e);
157-
}
158-
}
159-
};
160-
161-
// https://web.dev/streams/#the-getreader-and-read-methods
162-
const reader = data.getReader();
163-
const decoder = new TextDecoder();
164-
const parser = createParser(onParse);
165-
let done = false;
166-
167-
while (!done) {
168-
const { value, done: doneReading } = await reader.read();
169-
done = doneReading;
170-
const chunkValue = decoder.decode(value);
171-
parser.feed(chunkValue);
119+
120+
if (!res.body) {
121+
throw new Error("No response body");
172122
}
173123

174-
newMessages = [
175-
...newMessages,
176-
{ role: "assistant", content: generatedCode },
177-
];
124+
for await (let chunk of readStream(res.body)) {
125+
setGeneratedCode((prev) => prev + chunk);
126+
}
178127

179-
setMessages(newMessages);
128+
setMessages((m) => [...m, codeMessage, modificationMessage]);
180129
setStatus("updated");
181130
}
182131

@@ -208,14 +157,16 @@ export default function Home() {
208157
<br /> into an <span className="text-blue-600">app</span>
209158
</h1>
210159

211-
<form className="w-full max-w-xl" onSubmit={generateCode}>
160+
<form className="w-full max-w-xl" onSubmit={createApp}>
212161
<fieldset disabled={loading} className="disabled:opacity-75">
213162
<div className="relative mt-5">
214163
<div className="absolute -inset-2 rounded-[32px] bg-gray-300/50" />
215164
<div className="relative flex rounded-3xl bg-white shadow-sm">
216165
<div className="relative flex flex-grow items-stretch focus-within:z-10">
217166
<input
218167
required
168+
value={prompt}
169+
onChange={(e) => setPrompt(e.target.value)}
219170
name="prompt"
220171
className="w-full rounded-l-3xl bg-transparent px-6 py-5 text-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-500"
221172
placeholder="Build me a calculator app..."
@@ -239,8 +190,9 @@ export default function Home() {
239190
<p className="text-gray-500 sm:text-xs">Model:</p>
240191
<Select.Root
241192
name="model"
242-
defaultValue="meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo"
243193
disabled={loading}
194+
value={model}
195+
onValueChange={(value) => setModel(value)}
244196
>
245197
<Select.Trigger className="group flex w-60 max-w-xs items-center rounded-2xl border-[6px] border-gray-300 bg-white px-4 py-2 text-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-500">
246198
<Select.Value />
@@ -251,22 +203,7 @@ export default function Home() {
251203
<Select.Portal>
252204
<Select.Content className="overflow-hidden rounded-md bg-white shadow-lg">
253205
<Select.Viewport className="p-2">
254-
{[
255-
{
256-
label: "Llama 3.1 405B",
257-
value:
258-
"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
259-
},
260-
{
261-
label: "Llama 3.1 70B",
262-
value:
263-
"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
264-
},
265-
{
266-
label: "Gemma 2 27B",
267-
value: "google/gemma-2-27b-it",
268-
},
269-
].map((model) => (
206+
{models.map((model) => (
270207
<Select.Item
271208
key={model.value}
272209
value={model.value}
@@ -299,6 +236,8 @@ export default function Home() {
299236
className="group flex w-20 max-w-xs items-center rounded-2xl border-[6px] border-gray-300 bg-white p-1.5 text-sm shadow-inner transition focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-500 data-[state=checked]:bg-blue-500"
300237
id="shadcn"
301238
name="shadcn"
239+
checked={shadcn}
240+
onCheckedChange={(value) => setShadcn(value)}
302241
>
303242
<Switch.Thumb className="size-7 rounded-lg bg-gray-200 shadow-[0_1px_2px] shadow-gray-400 transition data-[state=checked]:translate-x-7 data-[state=checked]:bg-white data-[state=checked]:shadow-gray-600" />
304243
</Switch.Root>
@@ -323,14 +262,16 @@ export default function Home() {
323262
ref={ref}
324263
>
325264
<div className="mt-5 flex gap-4">
326-
<form className="w-full" onSubmit={modifyCode}>
265+
<form className="w-full" onSubmit={updateApp}>
327266
<fieldset disabled={loading} className="group">
328267
<div className="relative">
329268
<div className="relative flex rounded-3xl bg-white shadow-sm group-disabled:bg-gray-50">
330269
<div className="relative flex flex-grow items-stretch focus-within:z-10">
331270
<input
332271
required
333-
name="prompt"
272+
name="modification"
273+
value={modification}
274+
onChange={(e) => setModification(e.target.value)}
334275
className="w-full rounded-l-3xl bg-transparent px-6 py-5 text-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-blue-500 disabled:cursor-not-allowed"
335276
placeholder="Make changes to your app here"
336277
/>
@@ -455,3 +396,17 @@ async function minDelay<T>(promise: Promise<T>, ms: number) {
455396

456397
return p;
457398
}
399+
400+
async function* readStream(response: ReadableStream) {
401+
let reader = response.pipeThrough(new TextDecoderStream()).getReader();
402+
let done = false;
403+
404+
while (!done) {
405+
let { value, done: streamDone } = await reader.read();
406+
done = streamDone;
407+
408+
if (value) yield value;
409+
}
410+
411+
reader.releaseLock();
412+
}

0 commit comments

Comments
 (0)