Skip to content

Commit

Permalink
Redesign (#59)
Browse files Browse the repository at this point in the history
* added menu dropdown

* sign in btn reset

* updates to the layout

* refinissimo

* sizes

* chatbox changes

* updated file handing

* design changes

* design tokens, added back spinner

* visual nicities

* improved chat ux

* api key input password

* added url bar, make it more round

* tooltips everywhere

* fixes vercel build

* added artifact history

* added undo

* share sheet

* improves reactivity around recent changes

* css grid

* auto grid

* mobile responsiveness

* mobile-friendly sidebar

* new chat reset chatinput

* error handling + retry

* grammar

* improved imput ux, minor changes

* updated design tokens

* form overflow fix

* refreshed sign in screen

* moved sign in design around

* google auth sign-in

* updated logo, wording

* logo component

* added dark theme

* tooltip theming

* added light theme switch

* removed unnecessary files/dependencies, added prettierrc

* removed unused deps, code import sort

* prettify

* publish, url shortener

* format select.tsx

* publish sandbox extended timeout

* publish button changes

* fixes vercel build

* pass e2b api key to publish

* posthog capture on url publish

* undo rebrand (for now)

* next neutral logo

* file conventions

* renamed files

* sandbox endpoint server action

* renamed file structure

* work on types

* work on types and files

* renamed side to preview

* improved chat input error ui

* fixes invalid property in svg

* changed tooltip duration

* flipped dark/light switch

* new chat > clear chat

* added tooltip for profile menu

* added disclaimer

* undo favicon

* improved contrast ratio

* added multi-file picker changed artifact view

* changed display of loading states

* duplicated loading state to the chat input

* added toast

* copy button and fixes preview streaming

* fixes tooltip on copybutton

* copy-button moved props order

* changed share link > copy url

* added display name to copy button

* improved reactive data structures in preview

* moved loader to more prominent place

* chat input e2b star

* moved llm settings to chat input

* llm settings picker

* moved toggle to navbar

* sandbox api endpoint

* updated preview.png

* publish > deploy

* lint, o1 rate limit env var

---------

Co-authored-by: Mish Ushakov <[email protected]>
  • Loading branch information
mishushakov and mishushakov authored Oct 3, 2024
1 parent 88ad984 commit 816e16d
Show file tree
Hide file tree
Showing 63 changed files with 3,008 additions and 2,097 deletions.
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"semi": false,
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
30 changes: 30 additions & 0 deletions app/actions/publish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use server'

import { Sandbox } from '@e2b/code-interpreter'
import { kv } from '@vercel/kv'
import { customAlphabet } from 'nanoid'

const nanoid = customAlphabet('1234567890abcdef', 7)
const sandboxTimeout = 3 * 60 * 60 * 1000 // 3 hours

export async function publish(
url: string,
sbxId: string,
apiKey: string | undefined,
) {
if (process.env.KV_REST_API_URL && process.env.KV_REST_API_TOKEN) {
const id = nanoid()
await kv.set(`fragment:${id}`, url)
await Sandbox.setTimeout(sbxId, sandboxTimeout, { apiKey })

return {
url: process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}/s/${id}`
: `/s/${id}`,
}
}

return {
url,
}
}
48 changes: 31 additions & 17 deletions app/api/chat-o1/route.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,51 @@
import {
streamObject,
LanguageModel,
CoreMessage,
generateText,
} from 'ai'

import ratelimit from '@/lib/ratelimit'
import { Templates, templatesToPrompt } from '@/lib/templates'
import { getModelClient, getDefaultMode } from '@/lib/models'
import { getModelClient } from '@/lib/models'
import { LLMModel, LLMModelConfig } from '@/lib/models'
import { toPrompt } from '@/lib/prompt'
import ratelimit, { Duration } from '@/lib/ratelimit'
import { artifactSchema as schema } from '@/lib/schema'
import { Templates, templatesToPrompt } from '@/lib/templates'
import { openai } from '@ai-sdk/openai'
import { toPrompt } from '@/lib/prompt'
import { streamObject, LanguageModel, CoreMessage, generateText } from 'ai'

export const maxDuration = 60

const rateLimitMaxRequests = 10
const ratelimitWindow = '1d'
const rateLimitMaxRequests = process.env.RATE_LIMIT_MAX_REQUESTS
? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS)
: 10
const ratelimitWindow = process.env.RATE_LIMIT_WINDOW
? (process.env.RATE_LIMIT_WINDOW as Duration)
: '1d'

export async function POST(req: Request) {
const limit = await ratelimit(req.headers.get('x-forwarded-for'), rateLimitMaxRequests, ratelimitWindow)
const limit = await ratelimit(
req.headers.get('x-forwarded-for'),
rateLimitMaxRequests,
ratelimitWindow,
)
if (limit) {
return new Response('You have reached your request limit for the day.', {
status: 429,
headers: {
'X-RateLimit-Limit': limit.amount.toString(),
'X-RateLimit-Remaining': limit.remaining.toString(),
'X-RateLimit-Reset': limit.reset.toString()
}
'X-RateLimit-Reset': limit.reset.toString(),
},
})
}

const { messages, userID, template, model, config }: { messages: CoreMessage[], userID: string, template: Templates, model: LLMModel, config: LLMModelConfig } = await req.json()
const {
messages,
userID,
template,
model,
config,
}: {
messages: CoreMessage[]
userID: string
template: Templates
model: LLMModel
config: LLMModelConfig
} = await req.json()
console.log('userID', userID)
// console.log('template', template)
console.log('model', model)
Expand Down
45 changes: 30 additions & 15 deletions app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,50 @@
import {
streamObject,
LanguageModel,
CoreMessage,
} from 'ai'

import ratelimit, { Duration } from '@/lib/ratelimit'
import { Templates, templatesToPrompt } from '@/lib/templates'
import { getModelClient, getDefaultMode } from '@/lib/models'
import { LLMModel, LLMModelConfig } from '@/lib/models'
import { artifactSchema as schema } from '@/lib/schema'
import { toPrompt } from '@/lib/prompt'
import ratelimit, { Duration } from '@/lib/ratelimit'
import { artifactSchema as schema } from '@/lib/schema'
import { Templates } from '@/lib/templates'
import { streamObject, LanguageModel, CoreMessage } from 'ai'

export const maxDuration = 60

const rateLimitMaxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) : 10
const ratelimitWindow = process.env.RATE_LIMIT_WINDOW ? process.env.RATE_LIMIT_WINDOW as Duration : '1d'
const rateLimitMaxRequests = process.env.RATE_LIMIT_MAX_REQUESTS
? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS)
: 10
const ratelimitWindow = process.env.RATE_LIMIT_WINDOW
? (process.env.RATE_LIMIT_WINDOW as Duration)
: '1d'

export async function POST(req: Request) {
const limit = await ratelimit(req.headers.get('x-forwarded-for'), rateLimitMaxRequests, ratelimitWindow)
const limit = await ratelimit(
req.headers.get('x-forwarded-for'),
rateLimitMaxRequests,
ratelimitWindow,
)
if (limit) {
return new Response('You have reached your request limit for the day.', {
status: 429,
headers: {
'X-RateLimit-Limit': limit.amount.toString(),
'X-RateLimit-Remaining': limit.remaining.toString(),
'X-RateLimit-Reset': limit.reset.toString()
}
'X-RateLimit-Reset': limit.reset.toString(),
},
})
}

const { messages, userID, template, model, config }: { messages: CoreMessage[], userID: string, template: Templates, model: LLMModel, config: LLMModelConfig } = await req.json()
const {
messages,
userID,
template,
model,
config,
}: {
messages: CoreMessage[]
userID: string
template: Templates
model: LLMModel
config: LLMModelConfig
} = await req.json()
console.log('userID', userID)
// console.log('template', template)
console.log('model', model)
Expand Down
72 changes: 44 additions & 28 deletions app/api/sandbox/route.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import { ArtifactSchema } from '@/lib/schema'
import { Sandbox, CodeInterpreter, Execution, Result, ExecutionError } from "@e2b/code-interpreter";
import { TemplateId } from '@/lib/templates';
import { ExecutionResultInterpreter, ExecutionResultWeb } from '@/lib/types'
import { Sandbox, CodeInterpreter } from '@e2b/code-interpreter'

const sandboxTimeout = 10 * 60 * 1000 // 10 minute in ms

export const maxDuration = 60

export type ExecutionResult = {
template: TemplateId
stdout: string[]
stderr: string[]
runtimeError?: ExecutionError
cellResults: Result[]
url: string
}

export async function POST(req: Request) {
const { artifact, userID, apiKey }: { artifact: ArtifactSchema, userID: string, apiKey: string } = await req.json()
const {
artifact,
userID,
apiKey,
}: { artifact: ArtifactSchema; userID: string; apiKey?: string } =
await req.json()
console.log('artifact', artifact)
console.log('userID', userID)
console.log('apiKey', apiKey)
Expand All @@ -25,21 +21,33 @@ export async function POST(req: Request) {

// Create a interpreter or a sandbox
if (artifact.template === 'code-interpreter-multilang') {
sbx = await CodeInterpreter.create({ metadata: { template: artifact.template, userID: userID }, timeoutMs: sandboxTimeout, apiKey })
sbx = await CodeInterpreter.create({
metadata: { template: artifact.template, userID: userID },
timeoutMs: sandboxTimeout,
apiKey,
})
console.log('Created code interpreter', sbx.sandboxID)
} else {
sbx = await Sandbox.create(artifact.template, { metadata: { template: artifact.template, userID: userID }, timeoutMs: sandboxTimeout, apiKey })
sbx = await Sandbox.create(artifact.template, {
metadata: { template: artifact.template, userID: userID },
timeoutMs: sandboxTimeout,
apiKey,
})
console.log('Created sandbox', sbx.sandboxID)
}

// Install packages
if (artifact.has_additional_dependencies) {
if (sbx instanceof CodeInterpreter) {
await sbx.notebook.execCell(artifact.install_dependencies_command)
console.log(`Installed dependencies: ${artifact.additional_dependencies.join(', ')} in code interpreter ${sbx.sandboxID}`)
console.log(
`Installed dependencies: ${artifact.additional_dependencies.join(', ')} in code interpreter ${sbx.sandboxID}`,
)
} else if (sbx instanceof Sandbox) {
await sbx.commands.run(artifact.install_dependencies_command)
console.log(`Installed dependencies: ${artifact.additional_dependencies.join(', ')} in sandbox ${sbx.sandboxID}`)
console.log(
`Installed dependencies: ${artifact.additional_dependencies.join(', ')} in sandbox ${sbx.sandboxID}`,
)
}
}

Expand All @@ -56,19 +64,27 @@ export async function POST(req: Request) {

// Execute code or return a URL to the running sandbox
if (artifact.template === 'code-interpreter-multilang') {
const result = await (sbx as CodeInterpreter).notebook.execCell(artifact.code || '')
const result = await (sbx as CodeInterpreter).notebook.execCell(
artifact.code || '',
)
await (sbx as CodeInterpreter).close()
return new Response(JSON.stringify({
template: artifact.template,
stdout: result.logs.stdout,
stderr: result.logs.stderr,
runtimeError: result.error,
cellResults: result.results,
}))
return new Response(
JSON.stringify({
sbxId: sbx?.sandboxID,
template: artifact.template,
stdout: result.logs.stdout,
stderr: result.logs.stderr,
runtimeError: result.error,
cellResults: result.results,
} as ExecutionResultInterpreter),
)
} else {
return new Response(JSON.stringify({
template: artifact.template,
url: `https://${sbx?.getHost(artifact.port || 80)}`
}))
return new Response(
JSON.stringify({
sbxId: sbx?.sandboxID,
template: artifact.template,
url: `https://${sbx?.getHost(artifact.port || 80)}`,
} as ExecutionResultWeb),
)
}
}
33 changes: 29 additions & 4 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
@import './xterm.css';

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--ring: 240 10% 3.9%;
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
--radius: 0.75rem;
}

.dark {
--background: 240, 6%, 10%;
--foreground: 0 0% 98%;
--card: 240 10% 3.9%;
Expand All @@ -24,13 +50,12 @@
--destructive-foreground: 0 0% 98%;
--border: 270, 2%, 19%;
--input: 240 3.7% 15.9%;
--ring: 240 4.9% 83.9%;
--ring: 0, 0%, 100%, 0.1;
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
--radius: 0.5rem;
}
}

Expand Down
24 changes: 17 additions & 7 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import './globals.css'
import { PostHogProvider, ThemeProvider } from './providers'
import { Toaster } from '@/components/ui/toaster'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { PostHogProvider } from './providers'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
title: 'AI Artifacts by E2B',
description: 'About Hackable open-source version of Anthropic\'s AI Artifacts chat',
title: 'Artifacts by E2B',
description:
"About Hackable open-source version of Anthropic's AI Artifacts chat",
}

export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
children: React.ReactNode
}>) {
return (
<html lang="en">
<html lang="en" suppressHydrationWarning>
<PostHogProvider>
<body className={inter.className}>
{children}
<ThemeProvider
attribute="class"
defaultTheme="dark"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
<Toaster />
</body>
</PostHogProvider>
</html>
Expand Down
Loading

0 comments on commit 816e16d

Please sign in to comment.