├── .eslintrc.json
├── .gitignore
├── .npmrc
├── .prettierrc
├── .vscode
└── settings.json
├── README.md
├── components.json
├── drizzle.config.ts
├── next.config.ts
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── dots-background.png
├── file.svg
├── globe.svg
├── logo.svg
├── model-background.png
├── models
│ ├── gemma.png
│ ├── llama.png
│ ├── mixtral.png
│ ├── nemotron.png
│ ├── noushermes.webp
│ ├── qwen.png
│ └── wizardlm.png
├── next.svg
├── og-image.png
├── vercel.svg
└── window.svg
├── src
├── app
│ ├── actions.ts
│ ├── api
│ │ └── generate-app
│ │ │ └── route.ts
│ ├── favicon.ico
│ ├── fonts
│ │ ├── GeistMonoVF.woff
│ │ └── GeistVF.woff
│ ├── globals.css
│ ├── layout.tsx
│ ├── models.tsx
│ ├── page.tsx
│ └── top-models
│ │ ├── loading.tsx
│ │ └── page.tsx
├── components
│ ├── icons
│ │ ├── arrows.tsx
│ │ ├── github.tsx
│ │ ├── ribbon.tsx
│ │ ├── swords.tsx
│ │ ├── three-arrows.tsx
│ │ ├── up-right-arrow.tsx
│ │ └── x.tsx
│ ├── logo.tsx
│ ├── spinner.tsx
│ └── ui
│ │ ├── button.tsx
│ │ ├── table.tsx
│ │ └── tabs.tsx
├── db.ts
├── lib
│ └── utils.ts
└── schema.ts
├── tailwind.config.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/core-web-vitals", "next/typescript"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 |
32 | # env files (can opt-in for committing if needed)
33 | .env*
34 |
35 | # vercel
36 | .vercel
37 |
38 | # typescript
39 | *.tsbuildinfo
40 | next-env.d.ts
41 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | force=true
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["prettier-plugin-tailwindcss"]
3 | }
4 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
CodeArena
4 |
5 |
6 |
7 | Watch LLMs battle to build the same app and see a live leaderboard of the best OSS coding LLMs. 8 |
9 | 10 | ## Tech stack 11 | 12 | - LLMs on [Together AI](https://dub.sh/together-ai) on Together AI to generate code 13 | - [Sandpack](https://sandpack.codesandbox.io/) for rendering the UI code 14 | - [Next.js](https://nextjs.org/) with TypeScript for the app framework 15 | - [Shadcn](https://ui.shadcn.com/) for UI components & [Tailwind](https://tailwindcss.com/) for styling 16 | - [Plausible](https://plausible.io/) & [Helicone](https://helicone.ai/) for analytics & observability 17 | 18 | ## Cloning & running 19 | 20 | 1. Clone the repo: `git clone https://github.com/Nutlope/codearena` 21 | 2. Create a `.env` file and add your [Together AI API key](https://api.together.xyz/settings/api-keys): `TOGETHER_API_KEY=` 22 | 3. Create a postgres DB (I recommend [Neon](https://neon.tech/)) and add connection details to `.env`: `DATABASE_URL=` 23 | 4. Run `npm install` and `npm run dev` to install dependencies and run locally. 24 | 25 | ## Future tasks 26 | 27 | - [ ] Add two chained LLM calls generations for better results (also do a DB migration to tag each app/battle with single/multi calls) 28 | - [ ] Add Elo scores 29 | - [ ] Add the abilty to generate + run scripts 30 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "gray", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import { config } from "dotenv"; 2 | import { defineConfig } from "drizzle-kit"; 3 | 4 | config({ path: ".env.local" }); 5 | 6 | if (typeof process.env.DATABASE_URL !== "string") { 7 | throw new Error("DATABASE_URL is not set"); 8 | } 9 | 10 | export default defineConfig({ 11 | schema: "./src/schema.ts", 12 | dialect: "postgresql", 13 | dbCredentials: { 14 | url: process.env.DATABASE_URL, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-arena", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "db:push": "drizzle-kit push", 11 | "db:pull": "drizzle-kit pull", 12 | "db:studio": "drizzle-kit studio" 13 | }, 14 | "dependencies": { 15 | "@codesandbox/sandpack-react": "^2.19.10", 16 | "@codesandbox/sandpack-themes": "^2.0.21", 17 | "@neondatabase/serverless": "^0.10.4", 18 | "@radix-ui/react-slot": "^1.1.0", 19 | "@radix-ui/react-tabs": "^1.1.1", 20 | "class-variance-authority": "^0.7.1", 21 | "clsx": "^2.1.1", 22 | "dedent": "^1.5.3", 23 | "dotenv": "^16.4.5", 24 | "drizzle-orm": "^0.36.4", 25 | "lucide-react": "^0.462.0", 26 | "next": "15.0.3", 27 | "next-plausible": "^3.12.4", 28 | "party-js": "^2.2.0", 29 | "postgres": "^3.4.5", 30 | "react": "19.0.0-rc-66855b96-20241106", 31 | "react-dom": "19.0.0-rc-66855b96-20241106", 32 | "react-use-measure": "^2.1.1", 33 | "tailwind-merge": "^2.5.5", 34 | "tailwindcss-animate": "^1.0.7", 35 | "together-ai": "^0.9.0", 36 | "zod": "^3.23.8" 37 | }, 38 | "devDependencies": { 39 | "@types/node": "^20", 40 | "@types/react": "^18", 41 | "@types/react-dom": "^18", 42 | "drizzle-kit": "^0.28.1", 43 | "eslint": "^8", 44 | "eslint-config-next": "15.0.3", 45 | "postcss": "^8", 46 | "prettier": "^3.3.3", 47 | "prettier-plugin-tailwindcss": "^0.6.9", 48 | "tailwindcss": "^3.4.1", 49 | "typescript": "^5" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /public/dots-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/dots-background.png -------------------------------------------------------------------------------- /public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/model-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/model-background.png -------------------------------------------------------------------------------- /public/models/gemma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/models/gemma.png -------------------------------------------------------------------------------- /public/models/llama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/models/llama.png -------------------------------------------------------------------------------- /public/models/mixtral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/models/mixtral.png -------------------------------------------------------------------------------- /public/models/nemotron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/models/nemotron.png -------------------------------------------------------------------------------- /public/models/noushermes.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/models/noushermes.webp -------------------------------------------------------------------------------- /public/models/qwen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/models/qwen.png -------------------------------------------------------------------------------- /public/models/wizardlm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/models/wizardlm.png -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nutlope/codearena/46ed246bed4288e3ed4e96a221271cef7a00aa47/public/og-image.png -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/actions.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { db } from "@/db"; 4 | import { apps, battles } from "@/schema"; 5 | import { cookies } from "next/headers"; 6 | 7 | type App = { 8 | model: { label: string; apiName: string }; 9 | code: string; 10 | trimmedCode: string; 11 | completionTokens: number; 12 | totalTime: number; 13 | }; 14 | 15 | export default async function saveBattle({ 16 | prompt, 17 | winners, 18 | losers, 19 | }: { 20 | prompt: string; 21 | winners: App[]; 22 | losers: App[]; 23 | }) { 24 | const creatorCookie = await findOrCreateCreatorCookie(); 25 | 26 | const result = await db 27 | .insert(battles) 28 | .values({ 29 | prompt, 30 | creatorCookie, 31 | }) 32 | .returning(); 33 | 34 | const battle = result[0]; 35 | 36 | for (const winner of winners) { 37 | await db.insert(apps).values({ 38 | battleId: battle.id, 39 | model: winner.model.apiName, 40 | code: winner.code, 41 | trimmedCode: winner.trimmedCode, 42 | completionTokens: winner.completionTokens, 43 | totalTime: winner.totalTime, 44 | didWin: true, 45 | }); 46 | } 47 | 48 | for (const loser of losers) { 49 | await db.insert(apps).values({ 50 | battleId: battle.id, 51 | model: loser.model.apiName, 52 | code: loser.code, 53 | trimmedCode: loser.trimmedCode, 54 | completionTokens: loser.completionTokens, 55 | totalTime: loser.totalTime, 56 | didWin: false, 57 | }); 58 | } 59 | 60 | return battle; 61 | } 62 | 63 | async function findOrCreateCreatorCookie() { 64 | const cookieStore = await cookies(); 65 | let creatorId = cookieStore.get("creatorCookie")?.value; 66 | if (!creatorId) { 67 | creatorId = crypto.randomUUID(); 68 | cookieStore.set("creatorCookie", creatorId, { maxAge: 60 * 60 * 24 * 365 }); 69 | } 70 | return creatorId; 71 | } 72 | -------------------------------------------------------------------------------- /src/app/api/generate-app/route.ts: -------------------------------------------------------------------------------- 1 | import dedent from "dedent"; 2 | import Together from "together-ai"; 3 | 4 | const options: ConstructorParameters216 | Watch AI models compete in real-time, and see who emerges victorious. 217 |
218 |{placeholder}
343 |353 | {placeholder} 354 |
355 |479 | Which one did better? 480 |
481 | 482 | 527 |532 | app.model.apiName) 536 | .includes(appA.model.apiName) 537 | ? "font-bold text-gray-900" 538 | : "" 539 | } 540 | > 541 | Model A: 542 | {" "} 543 | 544 | {appA.model.shortLabel} 545 | 546 |
547 |548 | app.model.apiName) 552 | .includes(appB.model.apiName) 553 | ? "font-bold text-gray-900" 554 | : "" 555 | } 556 | > 557 | Model B: 558 | {" "} 559 | 560 | {appB.model.shortLabel} 561 | 562 |
563 |571 | {state.winners?.length === 1 ? ( 572 | <>{state.winners[0].model.shortLabel} wins!> 573 | ) : state.winners?.length === 2 ? ( 574 | <>Both models won!> 575 | ) : ( 576 | <>Neither model won.> 577 | )} 578 |
579 | 580 |Thanks for voting!
581 |582 | Check out the{" "} 583 | 587 | leaderboard 588 | {" "} 589 | or try again. 590 |
591 |194 | {model.shortLabel} 195 |
196 |Organization:
201 |{model.organization}
202 |Total Games:
205 |{result.games}
206 |Win %:
209 |210 | {result.winPercentage}% 211 |
212 |