├── .dockerignore ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .parcelrc ├── .postcssrc ├── .prettierrc ├── package.json ├── src │ ├── app.tsx │ ├── components │ │ ├── inputarea.tsx │ │ ├── multi-select.tsx │ │ ├── navbar.tsx │ │ ├── parameter-slider.tsx │ │ ├── parameters-side-panel.tsx │ │ └── ui │ │ │ ├── alert-dialog.tsx │ │ │ ├── button.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── dialog-sheet-base.tsx │ │ │ ├── dialog.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── navigation-menu.tsx │ │ │ ├── popover.tsx │ │ │ ├── right-sheet.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── slider.tsx │ │ │ ├── switch.tsx │ │ │ ├── textarea.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ └── tooltip.tsx │ ├── error-page.tsx │ ├── hooks │ │ ├── ui │ │ │ └── use-toast.tsx │ │ └── use-breakpoint.ts │ ├── index.css │ ├── index.html │ ├── index.tsx │ ├── lib │ │ ├── ctrl-meta-keypress.tsx │ │ ├── editor-styles.tsx │ │ ├── keypress.tsx │ │ ├── meta-keypress.tsx │ │ └── utils.ts │ └── pages │ │ ├── compare.tsx │ │ ├── index.tsx │ │ ├── playground.tsx │ │ └── settings.tsx ├── tailwind.config.js ├── tsconfig.json └── tsconfig.tsbuildinfo ├── build.py ├── dockerfile ├── poetry.lock ├── pyproject.toml └── server ├── __init__.py ├── app.py ├── lib ├── __init__.py ├── api │ ├── __init__.py │ ├── inference.py │ ├── provider.py │ └── response_utils.py ├── entities.py ├── event_emitter.py ├── inference │ ├── __init__.py │ └── huggingface │ │ ├── __init__.py │ │ ├── generator.py │ │ ├── helpers.py │ │ └── hf.py ├── sse.py ├── sseserver.py └── storage.py ├── models.json └── requirements.txt /.dockerignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # These files should be generated during dev/publishing 2 | server/static/* 3 | #mISC 4 | .DS_Store 5 | .vscode/ 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | yarn.lock* 13 | package-lock.json 14 | Cargo.lock 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (http://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # Typescript v1 declaration files 48 | typings/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | 68 | # Parcel related files 69 | dist/ 70 | .cache/ 71 | .parcel-cache/ 72 | 73 | # Byte-compiled / optimized / DLL files 74 | __pycache__/ 75 | *.py[cod] 76 | 77 | # C extensions 78 | *.so 79 | 80 | # Distribution / packaging 81 | bin/ 82 | # build/ 83 | develop-eggs/ 84 | dist/ 85 | eggs/ 86 | lib64/ 87 | parts/ 88 | sdist/ 89 | var/ 90 | *.egg-info/ 91 | .installed.cfg 92 | *.egg 93 | 94 | # Installer logs 95 | pip-log.txt 96 | pip-delete-this-directory.txt 97 | 98 | # Unit test / coverage reports 99 | .tox/ 100 | .coverage 101 | .cache 102 | nosetests.xml 103 | coverage.xml 104 | 105 | # Translations 106 | *.mo 107 | 108 | # Mr Developer 109 | .mr.developer.cfg 110 | .project 111 | .pydevproject 112 | 113 | # Rope 114 | .ropeproject 115 | 116 | # Django stuff: 117 | *.log 118 | *.pot 119 | 120 | # Sphinx documentation 121 | docs/_build/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2023] [OpenPlayground] 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openplayground 2 | 3 | An LLM playground you can run on your laptop. 4 | 5 | https://user-images.githubusercontent.com/111631/227399583-39b23f48-9823-4571-a906-985dbe282b20.mp4 6 | 7 | #### Features 8 | 9 | - Use any model from [OpenAI](https://openai.com), [Anthropic](https://anthropic.com), [Cohere](https://cohere.com), [Forefront](https://forefront.ai), [HuggingFace](https://huggingface.co), [Aleph Alpha](https://aleph-alpha.com), [Replicate](https://replicate.com), [Banana](https://banana.dev) and [llama.cpp](https://github.com/ggerganov/llama.cpp). 10 | - Full playground UI, including history, parameter tuning, keyboard shortcuts, and logprops. 11 | - Compare models side-by-side with the same prompt, individually tune model parameters, and retry with different parameters. 12 | - Automatically detects local models in your HuggingFace cache, and lets you install new ones. 13 | - Works OK on your phone. 14 | - Probably won't kill everyone. 15 | 16 | ## Try on nat.dev 17 | 18 | Try the hosted version: [nat.dev](https://nat.dev). 19 | 20 | ## How to install and run 21 | 22 | ```sh 23 | pip install openplayground 24 | openplayground run 25 | ``` 26 | 27 | Alternatively, run it as a docker container: 28 | ```sh 29 | docker run --name openplayground -p 5432:5432 -d --volume openplayground:/web/config natorg/openplayground 30 | ``` 31 | 32 | This runs a Flask process, so you can add the typical flags such as setting a different port `openplayground run -p 1235` and others. 33 | 34 | ## How to run for development 35 | 36 | ```sh 37 | git clone https://github.com/nat/openplayground 38 | cd app && npm install && npx parcel watch src/index.html --no-cache 39 | cd server && pip3 install -r requirements.txt && cd .. && python3 -m server.app 40 | ``` 41 | 42 | ## Docker 43 | 44 | ```sh 45 | docker build . --tag "openplayground" 46 | docker run --name openplayground -p 5432:5432 -d --volume openplayground:/web/config openplayground 47 | ``` 48 | 49 | First volume is optional. It's used to store API keys, models settings. 50 | 51 | ## Ideas for contributions 52 | 53 | - Add a token counter to the playground 54 | - Add a cost counter to the playground and the compare page 55 | - Measure and display time to first token 56 | - Setup automatic builds with GitHub Actions 57 | - The default parameters for each model are configured in the `server/models.json` file. If you find better default parameters for a model, please submit a pull request! 58 | - Someone can help us make a homebrew package, and a dockerfile 59 | - Easier way to install open source models directly from openplayground, with `openplayground install ` or in the UI. 60 | - Find and fix bugs 61 | - ChatGPT UI, with turn-by-turn, markdown rendering, chatgpt plugin support, etc. 62 | - We will probably need multimodal inputs and outputs at some point in 2023 63 | 64 | ### llama.cpp 65 | 66 | ## Adding models to openplayground 67 | 68 | Models and providers have three types in openplayground: 69 | 70 | - Searchable 71 | - Local inference 72 | - API 73 | 74 | You can add models in `server/models.json` with the following schema: 75 | 76 | #### Local inference 77 | 78 | For models running locally on your device you can add them to openplayground like the following (a minimal example): 79 | 80 | ```json 81 | "llama": { 82 | "api_key" : false, 83 | "models" : { 84 | "llama-70b": { 85 | "parameters": { 86 | "temperature": { 87 | "value": 0.5, 88 | "range": [ 89 | 0.1, 90 | 1.0 91 | ] 92 | }, 93 | } 94 | } 95 | } 96 | } 97 | ``` 98 | 99 | Keep in mind you will need to add a generation method for your model in `server/app.py`. Take a look at `local_text_generation()` as an example. 100 | 101 | #### API Provider Inference 102 | 103 | This is for model providers like OpenAI, cohere, forefront, and more. You can connect them easily into openplayground (a minimal example): 104 | 105 | ```json 106 | "cohere": { 107 | "api_key" : true, 108 | "models" : { 109 | "xlarge": { 110 | "parameters": { 111 | "temperature": { 112 | "value": 0.5, 113 | "range": [ 114 | 0.1, 115 | 1.0 116 | ] 117 | }, 118 | } 119 | } 120 | } 121 | } 122 | ``` 123 | 124 | Keep in mind you will need to add a generation method for your model in `server/app.py`. Take a look at `openai_text_generation()` or `cohere_text_generation()` as an example. 125 | 126 | #### Searchable models 127 | 128 | We use this for Huggingface Remote Inference models, the search endpoint is useful for scaling to N models in the settings page. 129 | 130 | ```json 131 | "provider_name": { 132 | "api_key": true, 133 | "search": { 134 | "endpoint": "ENDPOINT_URL" 135 | }, 136 | "parameters": { 137 | "parameter": { 138 | "value": 1.0, 139 | "range": [ 140 | 0.1, 141 | 1.0 142 | ] 143 | }, 144 | } 145 | } 146 | ``` 147 | 148 | #### Credits 149 | 150 | Instigated by Nat Friedman. Initial implementation by [Zain Huda](https://github.com/zainhuda) as a repl.it bounty. Many features and extensive refactoring by [Alex Lourenco](https://github.com/AlexanderLourenco). 151 | -------------------------------------------------------------------------------- /app/.parcelrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@parcel/config-default", 3 | "compressors": { 4 | "*.{html,css,js,svg,map}": [ 5 | "...", 6 | "@parcel/compressor-gzip", 7 | "@parcel/compressor-brotli" 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /app/.postcssrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "tailwindcss": {} 4 | } 5 | } -------------------------------------------------------------------------------- /app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "semi": false 4 | } 5 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@parcel/compressor-brotli": "^2.8.3", 4 | "@parcel/compressor-gzip": "^2.8.3", 5 | "@parcel/config-default": "^2.8.3", 6 | "@types/chroma-js": "^2.4.0", 7 | "@types/react": "^18.0.27", 8 | "@types/react-dom": "^18.0.10", 9 | "autoprefixer": "^10.4.13", 10 | "buffer": "^5.7.1", 11 | "events": "^3.3.0", 12 | "https-browserify": "^1.0.0", 13 | "parcel": "^2.8.3", 14 | "postcss": "^8.4.21", 15 | "process": "^0.11.10", 16 | "punycode": "^1.4.1", 17 | "querystring-es3": "^0.2.1", 18 | "stream-http": "^3.2.0", 19 | "tailwindcss": "^3.2.4", 20 | "url": "^0.11.0", 21 | "util": "^0.12.5" 22 | }, 23 | "dependencies": { 24 | "@radix-ui/react-alert-dialog": "^1.0.2", 25 | "@radix-ui/react-checkbox": "^1.0.1", 26 | "@radix-ui/react-dialog": "^1.0.3", 27 | "@radix-ui/react-dropdown-menu": "^2.0.2", 28 | "@radix-ui/react-label": "^2.0.0", 29 | "@radix-ui/react-navigation-menu": "^1.1.1", 30 | "@radix-ui/react-popover": "^1.0.4", 31 | "@radix-ui/react-scroll-area": "^1.0.2", 32 | "@radix-ui/react-select": "^1.2.0", 33 | "@radix-ui/react-separator": "^1.0.1", 34 | "@radix-ui/react-slider": "^1.1.0", 35 | "@radix-ui/react-switch": "^1.0.1", 36 | "@radix-ui/react-toast": "^1.1.2", 37 | "@radix-ui/react-tooltip": "^1.0.3", 38 | "@types/draft-js": "^0.11.10", 39 | "chroma-js": "^2.4.2", 40 | "class-variance-authority": "^0.4.0", 41 | "clsx": "^1.2.1", 42 | "draft-js": "^0.11.7", 43 | "event-source-polyfill": "^1.0.31", 44 | "eventsource": "^2.0.2", 45 | "immutable": "^4.2.3", 46 | "localforage": "^1.10.0", 47 | "lucide-react": "^0.108.0", 48 | "match-sorter": "^6.3.1", 49 | "react": "^18.2.0", 50 | "react-dom": "^18.2.0", 51 | "react-responsive": "^9.0.2", 52 | "react-router-dom": "^6.8.1", 53 | "react-scroll": "^1.8.9", 54 | "react-select": "^5.7.0", 55 | "react-tiny-popover": "^7.2.3", 56 | "react-transition-group": "^4.4.5", 57 | "sort-by": "^1.2.0", 58 | "sse.js": "^0.6.1", 59 | "tailwind-merge": "^1.9.0", 60 | "tailwindcss-animate": "^1.0.5", 61 | "uuidv4": "^6.2.13" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/components/inputarea.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button" 2 | import { Textarea } from "@/components/ui/textarea" 3 | 4 | export function InputArea() { 5 | return ( 6 |
7 |