├── .env.local.example ├── public ├── favicon.ico └── vercel.svg ├── pages ├── index.tsx ├── _document.tsx ├── _app.tsx ├── api │ ├── edit.ts │ ├── embedding.ts │ └── completion.ts └── demos │ ├── editor.tsx │ ├── pig-latin.tsx │ ├── movie-to-emoji.tsx │ ├── docstring.tsx │ ├── tweet-sentiment-classifier.tsx │ ├── sql-generator.tsx │ ├── chatbot.tsx │ ├── fake-review-generator.tsx │ ├── temperature.tsx │ ├── top-p.tsx │ └── search.tsx ├── next.config.js ├── .gitignore ├── .pre-commit-config.yaml ├── components ├── layout.tsx ├── completion.tsx └── navbar.tsx ├── tsconfig.json ├── package.json ├── .github └── workflows │ └── node.js.yml ├── LICENSE.md ├── README.md ├── .eslintrc.js └── libs └── openai-client.ts /.env.local.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=asdf 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/babldev/gpt3-demo/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | export default function Home() { 2 | return 'Welcome to the GPT-3 interactive demo! Select a demo on the left.'; 3 | } 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { createGetInitialProps } from '@mantine/next'; 2 | import Document, { 3 | Head, Html, Main, NextScript, 4 | } from 'next/document'; 5 | 6 | const getInitialProps = createGetInitialProps(); 7 | 8 | export default class Gpt3Document extends Document { 9 | static getInitialProps = getInitialProps; 10 | 11 | render() { 12 | return ( 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.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 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.1.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-yaml 8 | - id: check-added-large-files 9 | - repo: https://github.com/pre-commit/mirrors-eslint 10 | rev: v8.12.0 11 | hooks: 12 | - id: eslint 13 | files: \.[jt]sx?$ # *.js, *.jsx, *.ts and *.tsx 14 | types: [file] 15 | args: ['--fix'] 16 | - repo: local 17 | hooks: 18 | - id: typescript-check 19 | name: typescript-check 20 | entry: npx tsc 21 | language: system 22 | types_or: [ts, tsx] 23 | pass_filenames: false 24 | -------------------------------------------------------------------------------- /components/layout.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AppShell, Header, Title, 3 | } from '@mantine/core'; 4 | import React from 'react'; 5 | import AppNavbar from './navbar'; 6 | 7 | export default function Layout({ children }: { children: React.ReactNode }) { 8 | return ( 9 | } 12 | header={( 13 |
14 | GPT-3 Demo 15 |
16 | )} 17 | styles={(theme) => ({ 18 | main: { backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.colors.gray[0] }, 19 | })} 20 | > 21 | { children } 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "preserve", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": false, 11 | "skipLibCheck": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "noEmit": true, 15 | "incremental": true, 16 | "esModuleInterop": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "experimentalDecorators": true, 22 | "baseUrl": "./" 23 | }, 24 | "include": [ 25 | "next-env.d.ts", 26 | "**/*.ts", 27 | "**/*.tsx" 28 | ], 29 | "exclude": [ 30 | "node_modules" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { AppProps } from 'next/app'; 2 | import Head from 'next/head'; 3 | import { MantineProvider } from '@mantine/core'; 4 | import Layout from 'components/layout'; 5 | 6 | export default function App(props: AppProps) { 7 | const { Component, pageProps } = props; 8 | 9 | return ( 10 | <> 11 | 12 | GPT-3 Demo 13 | 14 | 15 | 16 | 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /pages/api/edit.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from 'next'; 2 | import { 3 | Configuration, CreateEditRequest, CreateEditResponse, OpenAIApi, 4 | } from 'openai'; 5 | 6 | const configuration = new Configuration({ 7 | apiKey: process.env.OPENAI_API_KEY, 8 | }); 9 | const openai = new OpenAIApi(configuration); 10 | type APIError = { message: string }; 11 | 12 | export default async function handler( 13 | req: NextApiRequest, 14 | res: NextApiResponse, 15 | ) { 16 | if (req.method !== 'POST') { 17 | res.status(405).send({ message: 'Only POST requests allowed' }); 18 | return; 19 | } 20 | 21 | // This demo repo "trusts" the input and does not validate. 22 | const body = req.body as CreateEditRequest; 23 | 24 | const edit = await openai.createEdit(body); 25 | res.status(200).json(edit.data); 26 | } 27 | -------------------------------------------------------------------------------- /pages/api/embedding.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from 'next'; 2 | import { 3 | Configuration, CreateEmbeddingResponse, CreateEmbeddingRequest, OpenAIApi, 4 | } from 'openai'; 5 | 6 | const configuration = new Configuration({ 7 | apiKey: process.env.OPENAI_API_KEY, 8 | }); 9 | const openai = new OpenAIApi(configuration); 10 | type APIError = { message: string }; 11 | 12 | export default async function handler( 13 | req: NextApiRequest, 14 | res: NextApiResponse, 15 | ) { 16 | if (req.method !== 'POST') { 17 | res.status(405).send({ message: 'Only POST requests allowed' }); 18 | return; 19 | } 20 | 21 | // This demo repo "trusts" the input and does not validate. 22 | const body = req.body as CreateEmbeddingRequest; 23 | 24 | const completion = await openai.createEmbedding(body); 25 | res.status(200).json(completion.data); 26 | } 27 | -------------------------------------------------------------------------------- /pages/api/completion.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from 'next'; 2 | import { 3 | Configuration, CreateCompletionRequest, CreateCompletionResponse, OpenAIApi, 4 | } from 'openai'; 5 | 6 | const configuration = new Configuration({ 7 | apiKey: process.env.OPENAI_API_KEY, 8 | }); 9 | const openai = new OpenAIApi(configuration); 10 | type APIError = { message: string }; 11 | 12 | export default async function handler( 13 | req: NextApiRequest, 14 | res: NextApiResponse, 15 | ) { 16 | if (req.method !== 'POST') { 17 | res.status(405).send({ message: 'Only POST requests allowed' }); 18 | return; 19 | } 20 | 21 | // This demo repo "trusts" the input and does not validate. 22 | const body = req.body as CreateCompletionRequest; 23 | 24 | const completion = await openai.createCompletion(body); 25 | res.status(200).json(completion.data); 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gpt3-demo", 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 | }, 11 | "dependencies": { 12 | "@mantine/core": "^5.2.3", 13 | "@mantine/form": "^5.2.4", 14 | "@mantine/hooks": "^5.2.3", 15 | "@mantine/next": "^5.2.3", 16 | "@mantine/prism": "^5.2.6", 17 | "@tabler/icons": "^1.86.0", 18 | "next": "12.2.5", 19 | "openai": "^3.0.0", 20 | "react": "18.2.0", 21 | "react-dom": "18.2.0" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "18.7.13", 25 | "@types/react": "18.0.17", 26 | "@types/react-dom": "18.0.6", 27 | "eslint": "8.22.0", 28 | "eslint-config-airbnb": "^19.0.4", 29 | "eslint-config-airbnb-typescript": "^17.0.0", 30 | "eslint-config-next": "12.2.5", 31 | "typescript": "4.7.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: pre-commit 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | node-version: [18.x] 17 | python-version: ["3.10"] 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | cache: 'npm' 25 | - run: python -m pip install pre-commit 26 | - run: npm ci 27 | - run: pre-commit run --all-files 28 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Brady Law 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/completion.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Code, Mark, Paper, Table, 4 | } from '@mantine/core'; 5 | import { CreateCompletionRequest } from 'openai'; 6 | 7 | interface GPTCompletionProps { 8 | request: CreateCompletionRequest; 9 | result?: string; 10 | } 11 | 12 | export default function GPTCompletion({ request, result }: GPTCompletionProps) { 13 | return ( 14 | 15 | 16 | { request.prompt } 17 | { result } 18 | 19 | 20 | 21 | { Object.entries(request).map(([key, value]) => { 22 | if (key === 'prompt') { 23 | return null; 24 | } 25 | return ( 26 | 27 | 28 | 29 | 30 | ); 31 | })} 32 | 33 |
{ key }{ value }
34 |
35 | ); 36 | } 37 | GPTCompletion.defaultProps = { 38 | result: undefined, 39 | }; 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gpt3-demo 2 | 3 | A collection of interactive GPT-3 demos in a NextJS+Mantine web app. 4 | 5 |

6 | Screenshot of npm app with GPT-3 demos 8 |

9 | 10 | ## Bootstrap 11 | 12 | Run the webserver locally to interact with the demo 13 | 14 | ### Node 18.* recommended 15 | 16 | ```sh 17 | node --version 18 | v18.7.0 19 | ``` 20 | 21 | Other versions may work but your mileage may vary. Installable here: https://nodejs.org/en/download/current/ 22 | 23 | ### Open AI token required 24 | 25 | Create one here: [https://openai.com/api/](https://openai.com/api/) 26 | 27 | ```sh 28 | cp .env.local.example .env.local 29 | ``` 30 | 31 | Declare your API token in `.env.local` 32 | 33 | ### Codex note 34 | 35 | Codex is in private beta and requires approval from Open AI. See details here: https://openai.com/blog/openai-codex/ 36 | 37 | ### Starting the server 38 | 39 | ```sh 40 | npm install 41 | npm run dev 42 | ``` 43 | 44 | If you are seeing HTTP Code 429 errors, make sure your OpenAI account is in good standing (trial could be expired). 45 | 46 | ## Pre-commit setup 47 | ```sh 48 | python3 -m pip install pre-commit 49 | pre-commit --install 50 | ``` 51 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** 4 | * @type {import('eslint').Linter.Config} 5 | **/ 6 | module.exports = { 7 | env: { 8 | browser: true, 9 | es2021: true, 10 | }, 11 | extends: [ 12 | 'airbnb', 13 | 'airbnb-typescript', 14 | 'plugin:@next/next/recommended', 15 | 'plugin:@typescript-eslint/recommended', 16 | 'plugin:import/recommended', 17 | 'plugin:import/typescript', 18 | 'next/core-web-vitals' 19 | ], 20 | parser: '@typescript-eslint/parser', 21 | parserOptions: { 22 | ecmaFeatures: { 23 | jsx: true, 24 | }, 25 | ecmaVersion: 'latest', 26 | sourceType: 'module', 27 | project: './tsconfig.json' 28 | }, 29 | settings: { 30 | 'import/parsers': { 31 | '@typescript-eslint/parser': ['.ts', '.tsx'] 32 | }, 33 | 'import/resolver': { 34 | 'node': { 35 | 'paths': ['./'] 36 | } 37 | } 38 | }, 39 | plugins: [ 40 | '@typescript-eslint' 41 | ], 42 | ignorePatterns: ['.eslintrc.js', 'next.config.js'], 43 | rules: { 44 | 'import/no-unresolved': 'error', 45 | 'no-unused-vars': 'error', 46 | 'import/prefer-default-export': 'off', 47 | 'no-console': 'off', 48 | 'react/jsx-props-no-spreading': 'off', 49 | 'no-constant-condition': 'off', 50 | 'no-continue': 'off', 51 | 'no-underscore-dangle': 'off', 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /libs/openai-client.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateCompletionRequest, CreateCompletionResponse, CreateEditRequest, 3 | CreateEditResponse, CreateEmbeddingRequest, CreateEmbeddingResponse, 4 | } from 'openai'; 5 | 6 | async function getCompletion(request: CreateCompletionRequest): Promise { 7 | return fetch('/api/completion', { 8 | method: 'POST', 9 | headers: { 10 | 'Content-Type': 'application/json', 11 | }, 12 | body: JSON.stringify(request), 13 | }).then( 14 | (response) => response.json() as CreateCompletionResponse, 15 | ); 16 | } 17 | 18 | async function getEdit(request: CreateEditRequest): Promise { 19 | return fetch('/api/edit', { 20 | method: 'POST', 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | }, 24 | body: JSON.stringify(request), 25 | }).then( 26 | (response) => response.json() as CreateEditResponse, 27 | ); 28 | } 29 | 30 | async function getEmbedding(request: CreateEmbeddingRequest): Promise { 31 | return fetch('/api/embedding', { 32 | method: 'POST', 33 | headers: { 34 | 'Content-Type': 'application/json', 35 | }, 36 | body: JSON.stringify(request), 37 | }).then( 38 | (response) => response.json() as CreateEmbeddingResponse, 39 | ); 40 | } 41 | 42 | export { getCompletion, getEdit, getEmbedding }; 43 | -------------------------------------------------------------------------------- /pages/demos/editor.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { 4 | Box, Button, Divider, Paper, Stack, Text, Textarea, Title, 5 | } from '@mantine/core'; 6 | import { useForm } from '@mantine/form'; 7 | import GPTCompletion from 'components/completion'; 8 | import { CreateEditRequest } from 'openai'; 9 | import { getEdit } from 'libs/openai-client'; 10 | 11 | interface FormValues { 12 | input: string; 13 | } 14 | 15 | function generatePrompt({ input }: FormValues): CreateEditRequest { 16 | return { 17 | model: 'text-davinci-edit-001', 18 | input, 19 | instruction: 'Fix capitalization, typos, and grammatical issues.', 20 | temperature: 0, 21 | }; 22 | } 23 | 24 | export default function Editor() { 25 | const form = useForm({ 26 | initialValues: { 27 | input: 'i update page With new assetz. it loks better noww. ' 28 | + 'sry for ani typos i was in a hurry and had to tipe quickly', 29 | }, 30 | validate: { 31 | input: (value) => (value.length > 0 ? null : 'Invalid input'), 32 | }, 33 | }); 34 | const [loading, setLoading] = React.useState(false); 35 | const [result, setResult] = React.useState(undefined); 36 | const [openaiRequest, setOpenaiRequest] = React 37 | .useState(undefined); 38 | 39 | const onSubmit = async (values: FormValues) => { 40 | setLoading(true); 41 | const request = generatePrompt(values); 42 | setOpenaiRequest(request); 43 | await getEdit(request).then((completion) => { 44 | setResult(completion && completion.choices 45 | ? completion.choices[0].text 46 | : 'No result, check the logs.'); 47 | }).finally(() => { 48 | setLoading(false); 49 | }); 50 | }; 51 | 52 | return ( 53 | 54 |
55 | 56 |