Skip to content

Commit e7fa128

Browse files
Refactor: Layout and update OpenAI model (#29)
* refactor: update to gpt-4.5 * refactor: layout * refactor: openai streamer to work better with actions array * chore: remove anthropic implementation * fix: layout breakpoint changes mismatch * improve: reduce default sleep after async action * Delete bun.lock
1 parent 135748a commit e7fa128

18 files changed

Lines changed: 846 additions & 930 deletions

File tree

app/api/chat/route.ts

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,15 @@
11
import { Sandbox } from "@e2b/desktop";
2-
import { ComputerModel, SSEEvent, SSEEventType } from "@/types/api";
2+
import { SSEEvent, SSEEventType } from "@/types/api";
33
import {
44
ComputerInteractionStreamerFacade,
55
createStreamingResponse,
66
} from "@/lib/streaming";
77
import { SANDBOX_TIMEOUT_MS } from "@/lib/config";
88
import { OpenAIComputerStreamer } from "@/lib/streaming/openai";
99
import { logError } from "@/lib/logger";
10-
import { ResolutionScaler } from "@/lib/streaming/resolution";
1110

1211
export const maxDuration = 800;
1312

14-
class StreamerFactory {
15-
static getStreamer(
16-
model: ComputerModel,
17-
desktop: Sandbox,
18-
resolution: [number, number]
19-
): ComputerInteractionStreamerFacade {
20-
const resolutionScaler = new ResolutionScaler(desktop, resolution);
21-
22-
switch (model) {
23-
case "anthropic":
24-
// currently not implemented
25-
/* return new AnthropicComputerStreamer(desktop, resolutionScaler); */
26-
case "openai":
27-
default:
28-
return new OpenAIComputerStreamer(desktop, resolutionScaler);
29-
}
30-
}
31-
}
32-
3313
export async function POST(request: Request) {
3414
const abortController = new AbortController();
3515
const { signal } = abortController;
@@ -42,7 +22,6 @@ export async function POST(request: Request) {
4222
messages,
4323
sandboxId,
4424
resolution,
45-
model = "openai",
4625
} = await request.json();
4726

4827
const apiKey = process.env.E2B_API_KEY;
@@ -79,14 +58,11 @@ export async function POST(request: Request) {
7958
desktop.setTimeout(SANDBOX_TIMEOUT_MS);
8059

8160
try {
82-
const streamer = StreamerFactory.getStreamer(
83-
model as ComputerModel,
84-
desktop,
85-
resolution
86-
);
61+
const streamer: ComputerInteractionStreamerFacade =
62+
new OpenAIComputerStreamer(desktop, resolution);
8763

8864
if (!sandboxId && activeSandboxId && vncUrl) {
89-
async function* stream(): AsyncGenerator<SSEEvent<typeof model>> {
65+
async function* stream(): AsyncGenerator<SSEEvent> {
9066
yield {
9167
type: SSEEventType.SANDBOX_CREATED,
9268
sandboxId: activeSandboxId,

app/page.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,10 @@ export default function Home() {
198198
}, [onSandboxCreated]);
199199

200200
return (
201-
<div className="w-full h-dvh overflow-hidden p-2 sm:p-4 md:p-8 md:pb-10">
201+
<div className="w-full flex justify-center items-center h-dvh overflow-hidden p-2 sm:p-4 md:p-8 md:pb-10">
202202
<Frame
203203
classNames={{
204-
wrapper: "w-full h-full",
204+
wrapper: "w-full max-lg:h-full lg:aspect-[18/9] max-w-[2000px]",
205205
frame: "flex flex-col h-full overflow-hidden",
206206
}}
207207
>
@@ -239,7 +239,7 @@ export default function Home() {
239239
</Button>
240240
</div>
241241

242-
<div className="hidden md:flex items-center gap-2">
242+
<div className="hidden lg:flex items-center gap-2">
243243
<ThemeToggle />
244244
<RepoBanner />
245245

@@ -262,14 +262,12 @@ export default function Home() {
262262
}
263263
>
264264
<Timer
265-
className={`h-3 w-3 ${
266-
!isTabVisible ? "text-fg-400" : ""
267-
}`}
265+
className={`h-3 w-3 ${!isTabVisible ? "text-fg-400" : ""
266+
}`}
268267
/>
269268
<span
270-
className={`text-xs font-medium ${
271-
!isTabVisible ? "text-fg-400" : ""
272-
}`}
269+
className={`text-xs font-medium ${!isTabVisible ? "text-fg-400" : ""
270+
}`}
273271
>
274272
{Math.floor(timeRemaining / 60)}:
275273
{(timeRemaining % 60).toString().padStart(2, "0")}
@@ -290,7 +288,7 @@ export default function Home() {
290288
</AnimatePresence>
291289
</div>
292290

293-
<div className="md:hidden flex items-center">
291+
<div className="lg:hidden flex items-center">
294292
<AnimatePresence>
295293
{sandboxId && (
296294
<motion.div
@@ -312,14 +310,12 @@ export default function Home() {
312310
className="px-1.5"
313311
>
314312
<Timer
315-
className={`h-3 w-3 ${
316-
!isTabVisible ? "text-fg-400" : ""
317-
}`}
313+
className={`h-3 w-3 ${!isTabVisible ? "text-fg-400" : ""
314+
}`}
318315
/>
319316
<span
320-
className={`text-xs font-medium ml-1 ${
321-
!isTabVisible ? "text-fg-400" : ""
322-
}`}
317+
className={`text-xs font-medium ml-1 ${!isTabVisible ? "text-fg-400" : ""
318+
}`}
323319
>
324320
{Math.floor(timeRemaining / 60)}:
325321
{(timeRemaining % 60).toString().padStart(2, "0")}
@@ -343,7 +339,7 @@ export default function Home() {
343339
<AnimatePresence>
344340
{mobileMenuOpen && (
345341
<motion.div
346-
className="md:hidden border-b p-2 flex items-center justify-between"
342+
className="lg:hidden border-b p-2 flex items-center justify-between"
347343
initial={{ opacity: 0, height: 0 }}
348344
animate={{ opacity: 1, height: "auto" }}
349345
exit={{ opacity: 0, height: 0 }}
@@ -357,10 +353,10 @@ export default function Home() {
357353
)}
358354
</AnimatePresence>
359355

360-
<div className="flex flex-col md:flex-row flex-1 overflow-hidden">
356+
<div className="flex flex-col lg:flex-row flex-1 overflow-hidden">
361357
<div
362358
ref={iFrameWrapperRef}
363-
className="relative w-full md:flex-1 h-[40vh] md:h-auto overflow-hidden"
359+
className="relative w-full lg:flex-1 h-[40vh] lg:h-auto overflow-hidden"
364360
>
365361
{isLoading || (chatLoading && !sandboxId) ? (
366362
<div className="absolute inset-0 flex flex-col items-center justify-center gap-4">
@@ -411,7 +407,7 @@ export default function Home() {
411407
)}
412408
</div>
413409

414-
<div className="flex-1 flex flex-col relative border-t md:border-t-0 md:border-l overflow-hidden h-[60vh] md:h-auto md:max-w-xl">
410+
<div className="flex-1 flex flex-col relative border-t lg:border-t-0 lg:border-l overflow-hidden h-[60vh] lg:h-auto lg:max-w-xl">
415411
<ChatList className="flex-1" messages={messages} />
416412

417413
{messages.length === 0 && (

components/chat/input.tsx

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
11
"use client";
22

33
import React, { useMemo } from "react";
4-
import { OpenAiLogo } from "@phosphor-icons/react";
54
import { ChevronsRight, StopCircle } from "lucide-react";
65
import { Button } from "@/components/ui/button";
76
import { cn } from "@/lib/utils";
8-
import {
9-
Select,
10-
SelectContent,
11-
SelectGroup,
12-
SelectItem,
13-
SelectLabel,
14-
SelectTrigger,
15-
} from "../ui/select";
16-
import { useChat } from "@/lib/chat-context";
177
import { Input } from "../ui/input";
18-
import { AnthropicLogo } from "../icons";
198
import { motion } from "motion/react";
209

2110
interface ChatInputProps {
@@ -42,34 +31,12 @@ export function ChatInput({
4231
placeholder = "What are we surfing today?",
4332
className,
4433
}: ChatInputProps) {
45-
const { model, setModel } = useChat();
46-
4734
const isInputEmpty = useMemo(() => input.trim() === "", [input]);
4835

4936
return (
5037
<form onSubmit={onSubmit} className={cn(className)}>
5138
<div className="flex items-center">
5239
<div className="relative flex-1 flex items-center gap-2">
53-
{/* CURRENTLY NOT USED */}
54-
{/* <Select value={model} onValueChange={setModel} disabled={disabled}>
55-
<SelectTrigger
56-
className="absolute rounded-lg left-1.5 z-10 inset-y-1.5 border-border-200 w-min aspect-square h-auto flex items-center justify-center hover:bg-bg focus:bg-bg"
57-
withIcon={false}
58-
>
59-
{model == "openai" ? (
60-
<OpenAiLogo className="size-5" />
61-
) : (
62-
<AnthropicLogo className="size-5" />
63-
)}
64-
</SelectTrigger>
65-
<SelectContent>
66-
<SelectGroup>
67-
<SelectLabel>Model</SelectLabel>
68-
<SelectItem value="openai">OpenAI</SelectItem>
69-
<SelectItem value="anthropic">Anthropic</SelectItem>
70-
</SelectGroup>
71-
</SelectContent>
72-
</Select> */}
7340
<Input
7441
placeholder={placeholder}
7542
value={input}

components/chat/message.tsx

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import React from "react";
44
import {
55
ChatMessage as ChatMessageType,
66
ActionChatMessage,
7-
AssistantChatMessage,
87
} from "@/types/chat";
98
import { Card, CardContent } from "@/components/ui/card";
109
import { cn } from "@/lib/utils";
@@ -17,10 +16,8 @@ import {
1716
User,
1817
Info,
1918
} from "lucide-react";
20-
import { useChat } from "@/lib/chat-context";
2119
import { Badge } from "../ui/badge";
2220
import { OpenAiLogo } from "@phosphor-icons/react";
23-
import { AnthropicLogo } from "../icons";
2421

2522
const messageVariants = cva("", {
2623
variants: {
@@ -47,11 +44,15 @@ function ActionMessageDisplay({
4744
message: ActionChatMessage;
4845
className?: string;
4946
}) {
50-
const { action, status } = message;
47+
const { action, repeatCount, status } = message;
5148

52-
const formatAction = (action: any): string => {
49+
const formatAction = (action: any, repeats?: number): string => {
5350
if (!action) return "No action details";
5451

52+
if (action.type === "wait") {
53+
return repeats && repeats > 1 ? `wait x${repeats}` : "wait";
54+
}
55+
5556
try {
5657
return JSON.stringify(action, null, 2);
5758
} catch (e) {
@@ -91,7 +92,7 @@ function ActionMessageDisplay({
9192
</div>
9293

9394
<div className="bg-bg-200 dark:bg-bg-300 p-2 rounded font-mono text-xs tracking-wide text-fg-100 overflow-x-auto mb-3">
94-
<code>{formatAction(action)}</code>
95+
<code>{formatAction(action, repeatCount)}</code>
9596
</div>
9697
</CardContent>
9798
</Card>
@@ -108,8 +109,6 @@ export function ChatMessage({ message, className }: ChatMessageProps) {
108109
const isSystem = role === "system";
109110
const isError = "isError" in message && message.isError;
110111

111-
const { model } = useChat();
112-
113112
if (isSystem) {
114113
return (
115114
<div className={cn("w-full flex justify-center", className)}>
@@ -121,7 +120,7 @@ export function ChatMessage({ message, className }: ChatMessageProps) {
121120
if (isAction) {
122121
return (
123122
<ActionMessageDisplay
124-
message={message as ActionChatMessage<typeof model>}
123+
message={message as ActionChatMessage}
125124
className={className}
126125
/>
127126
);
@@ -130,11 +129,7 @@ export function ChatMessage({ message, className }: ChatMessageProps) {
130129
const getRoleIcon = () => {
131130
if (isUser) return <User className="h-3 w-3" />;
132131
if (isAssistant) {
133-
if ((message as AssistantChatMessage).model === "openai") {
134-
return <OpenAiLogo className="h-3 w-3" />;
135-
} else {
136-
return <AnthropicLogo className="h-3 w-3" />;
137-
}
132+
return <OpenAiLogo className="h-3 w-3" />;
138133
}
139134
return <Info className="h-3 w-3" />;
140135
};

components/icons.tsx

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -188,33 +188,3 @@ export const InvoiceIcon = ({ size = 16 }: { size: number }) => {
188188
</svg>
189189
);
190190
};
191-
192-
export const AnthropicLogo = ({
193-
size = 16,
194-
className,
195-
}: {
196-
size?: number;
197-
className?: string;
198-
}) => {
199-
return (
200-
<svg
201-
width={size}
202-
height={size}
203-
version="1.1"
204-
id="Layer_1"
205-
xmlns="http://www.w3.org/2000/svg"
206-
xmlnsXlink="http://www.w3.org/1999/xlink"
207-
x="0px"
208-
y="0px"
209-
viewBox="0 0 92.2 65"
210-
className={className}
211-
>
212-
<style>{`.st0{fill:currentColor;}`}</style>
213-
<path
214-
className="st0"
215-
d="M66.5,0H52.4l25.7,65h14.1L66.5,0z M25.7,0L0,65h14.4l5.3-13.6h26.9L51.8,65h14.4L40.5,0C40.5,0,25.7,0,25.7,0z
216-
M24.3,39.3l8.8-22.8l8.8,22.8H24.3z"
217-
/>
218-
</svg>
219-
);
220-
};

0 commit comments

Comments
 (0)