├── .eslintrc.json ├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── README.md ├── next.config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js ├── _document.js ├── api │ └── predictions │ │ ├── [id].js │ │ └── index.js └── index.js ├── postcss.config.js ├── public ├── tilemaker-ubicj24iwvcbpghexn3pnqegvq.png ├── tilemaker-y7w5qqrsynbhrinkgjt5imj2oa.png └── vercel.svg ├── styles └── globals.css └── tailwind.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v3 15 | with: 16 | node-version: 18 17 | - run: npm ci 18 | - run: npm test 19 | -------------------------------------------------------------------------------- /.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.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js template for running language models with Replicate 2 | 3 | This is a [Next.js](https://nextjs.org/) template project that's preconfigured for interacting with a language model like [FLAN-T5](https://replicate.com/daanelson/flan-t5) with Replicate's API. 4 | 5 | You can use this as a quick jumping-off point to build a web app using Replicate's API, or you can recreate this codebase from scratch by following the guide at [replicate.com/docs/get-started/nextjs](https://replicate.com/docs/get-started/nextjs) 6 | 7 | ![Screenshot 2023-03-13 at 7 00 18 PM](https://user-images.githubusercontent.com/2289/224872889-2709eb76-38b0-45cb-8dae-3090eb7ec228.png) 8 | 9 | 10 | ## Noteworthy files 11 | 12 | - [pages/index.js](pages/index.js) - The React frontend that renders the home page in the browser 13 | - [pages/api/predictions/index.js](pages/api/predictions/index.js) - The backend API endpoint that calls Replicate's API to create a prediction 14 | - [pages/api/predictions/[id].js](pages/api/predictions/[id].js) - The backend API endpoint that calls Replicate's API to get the prediction result 15 | 16 | ## Usage 17 | 18 | Install dependencies: 19 | 20 | ```console 21 | npm install 22 | ``` 23 | 24 | Add your [Replicate API token](https://replicate.com/account#token) to `.env.local`: 25 | 26 | ``` 27 | REPLICATE_API_TOKEN= 28 | ``` 29 | 30 | Run the development server: 31 | 32 | ```console 33 | npm run dev 34 | ``` 35 | 36 | Open [http://localhost:3000](http://localhost:3000) with your browser. 37 | 38 | ### Changing the model 39 | 40 | This example app is configured to use the [FLAN-T5 model](https://replicate.com/daanelson/flan-t5) by default, but you can change it to use any other Replicate model that has a similar input and output format. 41 | 42 | To change the model, update the `modelVersion` constant in [pages/api/predictions/index.js](pages/api/predictions/index.js) to the version ID of the Replicate model you want to run. 43 | 44 | ## Editing the homepage 45 | 46 | The homepage is a React component in [pages/index.js](pages/index.js). You can edit it to change the look and feel of the app. 47 | 48 | ## Deploy to Vercel 49 | 50 | For detailed instructions on how to create and use this template, see [replicate.com/docs/get-started/nextjs](https://replicate.com/docs/get-started/nextjs) 51 | 52 | There are many ways to deploy apps to Vercel, but for the sake of brevity, we'll use the vercel CLI here. Start by installing the CLI and running it: 53 | 54 | ``` 55 | npx vercel 56 | ``` 57 | 58 | The command above installs the CLI, then walks you through the process of logging in to Vercel, creating the app, and deploying it. Once you've deployed your app, you need to add your Replicate API token to the remote app's environment variables. This allows your app to make requests to the Replicate API. 59 | 60 | ``` 61 | vercel env add REPLICATE_API_TOKEN 62 | ``` 63 | 64 | The command above prompts you to enter a value for your token. Paste your token fron [replicate.com/account](https://replicate.com/account). You then need to deploy again: 65 | 66 | ``` 67 | npx vercel deploy --prod 68 | ``` 69 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | images: { 5 | remotePatterns: [ 6 | { 7 | protocol: "https", 8 | hostname: "replicate.com", 9 | }, 10 | { 11 | protocol: "https", 12 | hostname: "replicate.delivery", 13 | }, 14 | ], 15 | }, 16 | }; 17 | 18 | module.exports = nextConfig; 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getting-started-web-app", 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 | "test": "npm run lint && npm run build" 11 | }, 12 | "dependencies": { 13 | "@next/font": "13.0.7", 14 | "eslint": "8.29.0", 15 | "eslint-config-next": "13.0.7", 16 | "next": "13.0.7", 17 | "react": "18.2.0", 18 | "react-dom": "18.2.0" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^10.4.14", 22 | "postcss": "^8.4.21", 23 | "tailwindcss": "^3.2.7" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import "../styles/globals.css"; 2 | 3 | export default function App({ Component, pageProps }) { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from "next/document"; 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /pages/api/predictions/[id].js: -------------------------------------------------------------------------------- 1 | export default async function handler(req, res) { 2 | const response = await fetch( 3 | "https://api.replicate.com/v1/predictions/" + req.query.id, 4 | { 5 | headers: { 6 | Authorization: `Token ${process.env.REPLICATE_API_TOKEN}`, 7 | "Content-Type": "application/json", 8 | }, 9 | } 10 | ); 11 | if (response.status !== 200) { 12 | let error = await response.json(); 13 | res.statusCode = 500; 14 | res.end(JSON.stringify({ detail: error.detail })); 15 | return; 16 | } 17 | 18 | const prediction = await response.json(); 19 | res.end(JSON.stringify(prediction)); 20 | } 21 | -------------------------------------------------------------------------------- /pages/api/predictions/index.js: -------------------------------------------------------------------------------- 1 | // This is pinned to a specific version https://replicate.com/daanelson/flan-t5 2 | const modelVersion = 3 | "04e422a9b85baed86a4f24981d7f9953e20c5fd82f6103b74ebc431588e1cec8"; 4 | 5 | export default async function handler(req, res) { 6 | const response = await fetch("https://api.replicate.com/v1/predictions", { 7 | method: "POST", 8 | headers: { 9 | Authorization: `Token ${process.env.REPLICATE_API_TOKEN}`, 10 | "Content-Type": "application/json", 11 | }, 12 | body: JSON.stringify({ 13 | version: modelVersion, 14 | 15 | // This is the text prompt that will be submitted by a form on the frontend 16 | input: { prompt: req.body.prompt }, 17 | }), 18 | }); 19 | 20 | if (response.status !== 201) { 21 | let error = await response.json(); 22 | res.statusCode = 500; 23 | res.end(JSON.stringify({ detail: error.detail })); 24 | return; 25 | } 26 | 27 | const prediction = await response.json(); 28 | res.statusCode = 201; 29 | res.end(JSON.stringify(prediction)); 30 | } 31 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import Head from "next/head"; 3 | 4 | const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); 5 | 6 | export default function Home() { 7 | const [prediction, setPrediction] = useState(null); 8 | const [error, setError] = useState(null); 9 | const [showDialog, setShowDialog] = useState(true); 10 | 11 | const handleSubmit = async (e) => { 12 | e.preventDefault(); 13 | const response = await fetch("/api/predictions", { 14 | method: "POST", 15 | headers: { 16 | "Content-Type": "application/json", 17 | }, 18 | body: JSON.stringify({ 19 | prompt: e.target.prompt.value, 20 | }), 21 | }); 22 | let prediction = await response.json(); 23 | if (response.status !== 201) { 24 | setError(prediction.detail); 25 | return; 26 | } 27 | setPrediction(prediction); 28 | 29 | while ( 30 | prediction.status !== "succeeded" && 31 | prediction.status !== "failed" 32 | ) { 33 | await sleep(500); 34 | const response = await fetch("/api/predictions/" + prediction.id); 35 | prediction = await response.json(); 36 | if (response.status !== 200) { 37 | setError(prediction.detail); 38 | return; 39 | } 40 | console.log({ prediction }); 41 | setPrediction(prediction); 42 | } 43 | }; 44 | 45 | return ( 46 |
47 | 48 | Replicate + Next.js + FLAN-T5 49 | 50 | 51 | {/* Uncomment the following lines if you want to display a welcome dialog 52 | {showDialog && ( 53 | 56 |
57 |

Welcome to ...

58 | 59 |

60 | By using this model, you agree to the following terms and 61 | conditions: 62 |

63 | 64 |
    65 |
  1. ...
  2. 66 |
67 | 68 | 74 |
75 |
76 | )} 77 | */} 78 | 79 |

80 | Replicate + Next.js + FLAN-T5 81 |

82 | 83 |
84 | 90 | 93 |
94 | 95 | {error &&
{error}
} 96 | 97 | {prediction && ( 98 |
99 | {prediction.output &&
{prediction.output}
} 100 |

101 | status: {prediction.status} 102 |

103 |
104 | )} 105 |
106 | ); 107 | } 108 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/tilemaker-ubicj24iwvcbpghexn3pnqegvq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicate/getting-started-nextjs-language/dc2555bb865fab3c81bb9af447b222a43881a58f/public/tilemaker-ubicj24iwvcbpghexn3pnqegvq.png -------------------------------------------------------------------------------- /public/tilemaker-y7w5qqrsynbhrinkgjt5imj2oa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replicate/getting-started-nextjs-language/dc2555bb865fab3c81bb9af447b222a43881a58f/public/tilemaker-y7w5qqrsynbhrinkgjt5imj2oa.png -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | input { 6 | @apply border; 7 | @apply border-solid; 8 | @apply border-gray-300; 9 | /* @apply rounded-md; */ 10 | @apply px-4; 11 | @apply py-2; 12 | } 13 | 14 | .border-hairline { 15 | @apply border border-black border-opacity-10; 16 | } 17 | 18 | .lil-button { 19 | @apply inline-block; 20 | @apply text-sm; 21 | @apply text-gray-500; 22 | @apply rounded-md; 23 | @apply py-2; 24 | @apply mx-3; 25 | } 26 | 27 | .lil-text { 28 | @apply text-sm; 29 | @apply text-gray-500; 30 | } 31 | 32 | .lil-text a { 33 | @apply text-gray-800; 34 | @apply underline; 35 | } 36 | 37 | .icon { 38 | @apply inline relative mr-1; 39 | top: -0.1em; 40 | width: 1.1em; 41 | height: 1.1em; 42 | } 43 | 44 | .prose { 45 | @apply mt-8; 46 | @apply text-lg; 47 | @apply text-gray-500; 48 | @apply leading-7; 49 | } 50 | 51 | .prose a { 52 | @apply text-gray-600; 53 | @apply underline; 54 | } 55 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./app/**/*.{js,ts,jsx,tsx}", 5 | "./pages/**/*.{js,ts,jsx,tsx}", 6 | "./components/**/*.{js,ts,jsx,tsx}", 7 | ], 8 | theme: { 9 | backgroundImage: { 10 | llama: "url('/tilemaker-y7w5qqrsynbhrinkgjt5imj2oa.png')", 11 | }, 12 | extend: {}, 13 | }, 14 | plugins: [], 15 | }; 16 | --------------------------------------------------------------------------------