├── .devcontainer ├── devcontainer.json └── postCreateCommand.sh ├── .env ├── .gitattributes ├── .github └── workflows │ └── docker.yml ├── .gitignore ├── .gitpod.yml ├── .husky ├── pre-commit └── pre-push ├── .python-version ├── LICENSE ├── README.md ├── assets ├── codespace.png ├── demo.gif ├── example-pricing-table.png ├── ollama.png ├── openui.png └── settings.jpeg ├── backend ├── .dockerignore ├── .github │ └── workflows │ │ ├── publish.yml │ │ └── test.yml ├── .gitignore ├── .python-version ├── .vscode │ ├── extensions.json │ └── settings.json ├── Dockerfile ├── LICENSE ├── README.md ├── fly.toml ├── openui │ ├── __init__.py │ ├── __main__.py │ ├── assets │ │ ├── funky.mp3 │ │ └── question.svg │ ├── config.py │ ├── config.yaml │ ├── db │ │ └── models.py │ ├── dist │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── annotator │ │ │ └── index.html │ │ ├── apple-touch-icon.png │ │ ├── assets │ │ │ ├── CodeEditor-B1zwGt1y.css │ │ │ ├── CodeEditor-B9qhAAku.js │ │ │ ├── babel-CqqbTYm7.js │ │ │ ├── codicon-B16ygVZF.ttf │ │ │ ├── css-D1nB4Vcj.js │ │ │ ├── cssMode-CMP9zKWk.js │ │ │ ├── html-B2LDEzWk.js │ │ │ ├── html-B4dTfUY8.js │ │ │ ├── htmlMode-BZEeRbEQ.js │ │ │ ├── index-B7PjGjI7.js │ │ │ ├── index-CnQwS-Fb.css │ │ │ ├── index-DnTpCebm.js │ │ │ ├── javascript-BcV1SRi8.js │ │ │ ├── jsonMode-CWFvP3uU.js │ │ │ ├── markdown-7fQo6M4U.js │ │ │ ├── python-CsxvR8Mf.js │ │ │ ├── standalone-BS_cqyLa.js │ │ │ ├── tsMode-FcR9Jej8.js │ │ │ ├── typescript-BfKWl9Pr.js │ │ │ └── yaml-DWuY8lcX.js │ │ ├── favicon.png │ │ ├── fonts │ │ │ ├── Inter-Bold.woff2 │ │ │ ├── Inter-Medium.woff2 │ │ │ └── Inter-Regular.woff2 │ │ ├── icons │ │ │ └── arrow-left.svg │ │ ├── index.html │ │ ├── logo.html │ │ ├── logo.svg │ │ ├── logo.txt │ │ ├── manifest.webmanifest │ │ ├── monacoeditorwork │ │ │ ├── css.worker.bundle.js │ │ │ ├── editor.worker.bundle.js │ │ │ ├── html.worker.bundle.js │ │ │ ├── json.worker.bundle.js │ │ │ ├── tailwindcss.worker.bundle.js │ │ │ └── ts.worker.bundle.js │ │ ├── registerSW.js │ │ ├── robots.txt │ │ ├── sw.js │ │ └── workbox-3e8df8c8.js │ ├── dummy.py │ ├── eval │ │ ├── .gitignore │ │ ├── __init__.py │ │ ├── dataset.py │ │ ├── download_and_convert.sh │ │ ├── evaluate.py │ │ ├── evaluate_weave.py │ │ ├── model.py │ │ ├── prompt_to_img.py │ │ ├── promptsearch.py │ │ ├── scrape.py │ │ ├── screenshots.py │ │ ├── svg_annotator.html │ │ ├── synthesize.py │ │ └── to_fine_tune.py │ ├── litellm.py │ ├── log_config.yaml │ ├── logo.ascii │ ├── logs.py │ ├── models.py │ ├── ollama.py │ ├── openai.py │ ├── server.py │ ├── session.py │ ├── tui │ │ ├── app.py │ │ ├── code.py │ │ ├── code_browser.tcss │ │ └── markdown.py │ └── util │ │ ├── __init__.py │ │ ├── email.py │ │ ├── screenshots.py │ │ └── storage.py ├── pyproject.toml ├── tests │ └── test_openui.py └── uv.lock ├── docker-compose.yaml ├── docs ├── frontend ├── .cz.json ├── .github │ ├── CODEOWNERS │ ├── renovate.json │ └── workflows │ │ └── codeql-analysis.yml ├── .gitignore ├── .postcssrc.json ├── .prettierrc.json ├── .stylelintrc.json ├── .vscode │ ├── extensions.json │ └── settings.json ├── LICENSE ├── README.md ├── components.json ├── eslint.config.mjs ├── index.html ├── integration_tests │ ├── basic.spec.ts │ └── util.ts ├── package.json ├── playwright.config.ts ├── pnpm-lock.yaml ├── public │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── annotator │ │ └── index.html │ ├── apple-touch-icon.png │ ├── favicon.png │ ├── fonts │ │ ├── Inter-Bold.woff2 │ │ ├── Inter-Medium.woff2 │ │ └── Inter-Regular.woff2 │ ├── icons │ │ └── arrow-left.svg │ ├── logo.html │ ├── logo.svg │ ├── logo.txt │ └── robots.txt ├── src │ ├── App.tsx │ ├── __tests__ │ │ ├── App.tsx │ │ └── utils.ts │ ├── api │ │ ├── models.ts │ │ ├── openai.ts │ │ └── openui.ts │ ├── components │ │ ├── Chat.tsx │ │ ├── CodeEditor.tsx │ │ ├── CodeViewer.tsx │ │ ├── CurrentUiContext.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── Examples.tsx │ │ ├── FileUpload.tsx │ │ ├── Head.tsx │ │ ├── History.tsx │ │ ├── HistoryItem.tsx │ │ ├── HtmlAnnotator.tsx │ │ ├── LoadingOrError.tsx │ │ ├── Logo.tsx │ │ ├── NavBar.tsx │ │ ├── Prompt.tsx │ │ ├── Register.tsx │ │ ├── Scaffold.tsx │ │ ├── Screenshot.tsx │ │ ├── Settings.tsx │ │ ├── ShareDialog.tsx │ │ ├── SyntaxHighlighter.tsx │ │ ├── VersionPreview.tsx │ │ ├── Versions.tsx │ │ ├── __tests__ │ │ │ └── LoadingOrError.tsx │ │ └── ui │ │ │ ├── avatar.tsx │ │ │ ├── button.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── popover.tsx │ │ │ ├── select.tsx │ │ │ ├── sheet.tsx │ │ │ ├── slider.tsx │ │ │ ├── switch.tsx │ │ │ ├── textarea.tsx │ │ │ └── tooltip.tsx │ ├── hooks │ │ └── index.ts │ ├── index.css │ ├── lib │ │ ├── anysphere.ts │ │ ├── constants.ts │ │ ├── events.ts │ │ ├── html.ts │ │ ├── i18n.ts │ │ ├── markdown.ts │ │ ├── simple.ts │ │ ├── themes.ts │ │ ├── utils.ts │ │ └── webauthn.ts │ ├── main.tsx │ ├── mocks │ │ ├── handlers.ts │ │ └── server.ts │ ├── pages │ │ └── AI │ │ │ └── index.tsx │ ├── setupTests.ts │ ├── state │ │ ├── atoms │ │ │ ├── history.ts │ │ │ ├── prompts.ts │ │ │ └── ui.ts │ │ └── index.ts │ └── testUtils.tsx ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json ├── vercel.json └── vite.config.ts └── openui.code-workspace /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpenUI Development", 3 | "image": "mcr.microsoft.com/devcontainers/python:3.12", 4 | 5 | // More features: https://containers.dev/features. 6 | "features": { 7 | "ghcr.io/devcontainers/features/node:1": { 8 | "nodeGypDependencies": true, 9 | "version": "lts", 10 | "nvmVersion": "latest" 11 | }, 12 | "ghcr.io/devcontainers/features/rust:1": { 13 | "version": "latest", 14 | "profile": "minimal" 15 | } 16 | }, 17 | 18 | "mounts": [ 19 | "source=codespaces-linux-cache,target=/home/vscode/.cache,consistency=delegated,type=volume" 20 | ], 21 | 22 | "customizations": { 23 | "vscode": { 24 | "settings": {}, 25 | "extensions": [ 26 | "ms-python.python", 27 | "tamasfe.even-better-toml", 28 | "charliermarsh.ruff", 29 | "ms-toolsai.jupyter", 30 | "ms-azuretools.vscode-docker", 31 | "GitHub.copilot", 32 | "ms-python.black-formatter" 33 | ] 34 | } 35 | }, 36 | 37 | "portsAttributes": { 38 | "5173": { 39 | "label": "OpenUI UI Dev Server", 40 | "onAutoForward": "notify" 41 | }, 42 | "7878": { 43 | "label": "OpenUI Server", 44 | "onAutoForward": "notify" 45 | } 46 | }, 47 | 48 | // Install Ollama in the prebuild step 49 | "onCreateCommand": "curl -fsSL https://ollama.com/install.sh | sh", 50 | "postCreateCommand": "bash ./.devcontainer/postCreateCommand.sh", 51 | "postStartCommand": "nohup bash -c 'ollama serve &'", 52 | 53 | "secrets": { 54 | "OPENAI_API_KEY": { 55 | "description": "Your OpenAI API Key", 56 | "documentationUrl": "https://platform.openai.com/api-keys" 57 | }, 58 | "WANDB_API_KEY": { 59 | "description": "Your W&B API Key", 60 | "documentationUrl": "https://wandb.ai/authorize" 61 | } 62 | } 63 | 64 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 65 | // "remoteUser": "root" 66 | } 67 | -------------------------------------------------------------------------------- /.devcontainer/postCreateCommand.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fix cache perms 4 | mkdir -p $HOME/.cache 5 | sudo chown -R $USER $HOME/.cache 6 | 7 | # Install node packages 8 | cd /workspaces/openui/frontend 9 | pnpm install 10 | 11 | # Install python packages 12 | cd /workspaces/openui/backend 13 | pip install -e .[test] 14 | 15 | # Pull a model for ollama, using llava for now as it's multi-modal 16 | ollama pull llava 17 | 18 | # addressing warning... 19 | git config --unset-all core.hooksPath 20 | pre-commit install --allow-missing-config 21 | 22 | # pre-commit hooks cause permission weirdness 23 | git config --global --add safe.directory /workspaces/openui -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/.env -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | backend/openui/dist/* binary 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | nohup.out 3 | .cache/ 4 | .env 5 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # Image of workspace. Learn more: https://www.gitpod.io/docs/configure/workspaces/workspace-image 2 | image: gitpod/workspace-full:latest 3 | 4 | # List the start up tasks. Learn more: https://www.gitpod.io/docs/configure/workspaces/tasks 5 | tasks: 6 | - name: Run Open UI 7 | init: | 8 | mkdir $GITPOD_REPO_ROOT/../venv 9 | cd $GITPOD_REPO_ROOT/../venv 10 | python -m venv openui 11 | source openui/bin/activate 12 | cd $GITPOD_REPO_ROOT/backend 13 | pip install . 14 | gp sync-done init-done 15 | command: | 16 | source $GITPOD_REPO_ROOT/../venv/openui/bin/activate 17 | cd $GITPOD_REPO_ROOT/backend 18 | python -m openui 19 | 20 | # List the ports to expose. Learn more: https://www.gitpod.io/docs/configure/workspaces/ports 21 | ports: 22 | - name: Open UI 23 | description: Port 7878 for Open UI 24 | port: 7878 25 | onOpen: notify 26 | 27 | # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart 28 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | cd frontend && pnpm lint-staged 2 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | cd frontend && pnpm test:push 2 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | openui 2 | -------------------------------------------------------------------------------- /assets/codespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/assets/codespace.png -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/assets/demo.gif -------------------------------------------------------------------------------- /assets/example-pricing-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/assets/example-pricing-table.png -------------------------------------------------------------------------------- /assets/ollama.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/assets/ollama.png -------------------------------------------------------------------------------- /assets/openui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/assets/openui.png -------------------------------------------------------------------------------- /assets/settings.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/assets/settings.jpeg -------------------------------------------------------------------------------- /backend/.dockerignore: -------------------------------------------------------------------------------- 1 | # flyctl launch added from .gitignore 2 | **/.venv 3 | **/__pycache__ 4 | **/wandb 5 | **/*.py[cod] 6 | **/*$py.class 7 | **/venv 8 | **/.eggs 9 | **/.pytest_cache 10 | **/*.egg-info 11 | **/.DS_Store 12 | **/build 13 | **/wandb 14 | **/*.db 15 | 16 | # flyctl launch added from openui/eval/.gitignore 17 | openui/eval/**/datasets 18 | openui/eval/**/components 19 | fly.toml 20 | -------------------------------------------------------------------------------- /backend/.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | cache: pip 23 | cache-dependency-path: pyproject.toml 24 | - name: Install dependencies 25 | run: | 26 | pip install '.[test]' 27 | - name: Run tests 28 | run: | 29 | pytest 30 | deploy: 31 | runs-on: ubuntu-latest 32 | needs: [test] 33 | environment: release 34 | permissions: 35 | id-token: write 36 | steps: 37 | - uses: actions/checkout@v4 38 | - name: Set up Python 39 | uses: actions/setup-python@v5 40 | with: 41 | python-version: "3.12" 42 | cache: pip 43 | cache-dependency-path: pyproject.toml 44 | - name: Install dependencies 45 | run: | 46 | pip install setuptools wheel build 47 | - name: Build 48 | run: | 49 | python -m build 50 | - name: Publish 51 | uses: pypa/gh-action-pypi-publish@release/v1 52 | 53 | -------------------------------------------------------------------------------- /backend/.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v5 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | cache: pip 21 | cache-dependency-path: pyproject.toml 22 | - name: Install dependencies 23 | run: | 24 | pip install '.[test]' 25 | - name: Run tests 26 | run: | 27 | pytest 28 | 29 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | __pycache__/ 3 | wandb/ 4 | *.py[cod] 5 | *$py.class 6 | venv 7 | .eggs 8 | .pytest_cache 9 | *.egg-info 10 | .DS_Store 11 | build 12 | eval/components 13 | eval/datasets 14 | !eval/datasets/eval.csv 15 | -------------------------------------------------------------------------------- /backend/.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /backend/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["charliermarsh.ruff"] 3 | } 4 | -------------------------------------------------------------------------------- /backend/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[python]": { 3 | "editor.formatOnSave": true, 4 | "editor.defaultFormatter": "charliermarsh.ruff" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the virtualenv as a separate step: Only re-execute this step when pyproject.toml changes 2 | FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder 3 | 4 | WORKDIR /app 5 | 6 | ENV UV_LINK_MODE=copy UV_COMPILE_BYTECODE=1 7 | 8 | RUN --mount=type=cache,target=/root/.cache/uv \ 9 | --mount=type=bind,source=uv.lock,target=uv.lock \ 10 | --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ 11 | uv sync --frozen --extra litellm --no-install-project --no-dev 12 | 13 | COPY . /app 14 | 15 | RUN --mount=type=cache,target=/root/.cache/uv \ 16 | uv sync --frozen --extra litellm --no-dev 17 | 18 | # Copy the virtualenv into a distroless image 19 | FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim 20 | 21 | WORKDIR /app 22 | 23 | COPY --from=builder --chown=app:app /app /app 24 | 25 | ENV PATH="/app/.venv/bin:$PATH" 26 | 27 | ENTRYPOINT ["python", "-m", "openui", "--litellm"] 28 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # OpenUI 2 | 3 | [![PyPI](https://img.shields.io/pypi/v/wandb-openui.svg)](https://pypi.org/project/wandb-openui/) 4 | [![Changelog](https://img.shields.io/github/v/release/wandb/openui?include_prereleases&label=changelog)](https://github.com/wandb/openui/releases) 5 | [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/wandb/openui/blob/main/LICENSE) 6 | 7 | A backend service for generating HTML components with AI 8 | 9 | ## Installation 10 | 11 | Clone this repo, then install using `pip`. You'll probably want to create a virtual env. 12 | 13 | ```bash 14 | git clone https://github.com/wandb/openui 15 | cd openui/backend 16 | pip install . 17 | ``` 18 | 19 | ## Usage 20 | 21 | You must set the `OPENAI_API_KEY` even if you just want to try Ollama models. Just set it to `xxx` in that case like below. 22 | 23 | ```bash 24 | OPENAI_API_KEY=xxx python -m openui 25 | ``` 26 | 27 | ### Docker 28 | 29 | You can build and run the docker file from the `/backend` directory: 30 | 31 | ```bash 32 | docker build . -t wandb/openui --load 33 | docker run -p 7878:7878 -e OPENAI_API_KEY wandb/openui 34 | ``` 35 | 36 | ## Development 37 | 38 | First be sure to install the package as editable, then passing `--dev` as an argument will live reload any local changes. 39 | 40 | ```bash 41 | pip install -e . 42 | python -m openui --dev 43 | ``` 44 | 45 | Now install the dependencies and test dependencies: 46 | 47 | ```bash 48 | pip install -e '.[test]' 49 | ``` 50 | 51 | To run the tests: 52 | 53 | ```bash 54 | pytest 55 | ``` 56 | 57 | ## Evaluation 58 | 59 | The [eval](./openui/eval) folder contains scripts for evaluating the performance of a model. It automates generating UI, taking screenshots of the UI, then asking `gpt-4-vision-preview` to rate the elements. More details about the eval pipeline coming soon... 60 | 61 | 62 | ## Google Vertex AI 63 | 64 | Create a service account with the appropriate permissions and authenticate with: 65 | 66 | ``` 67 | gcloud auth application-default login --impersonate-service-account ${GCLOUD_SERVICE_ACCOUNT}@${GCLOUD_PROJECT}.iam.gserviceaccount.com 68 | ``` -------------------------------------------------------------------------------- /backend/fly.toml: -------------------------------------------------------------------------------- 1 | # fly.toml app configuration file generated for openui on 2024-03-15T15:53:15-07:00 2 | # 3 | # See https://fly.io/docs/reference/configuration/ for information about how to use this file. 4 | # 5 | 6 | app = 'openui' 7 | primary_region = 'sjc' 8 | 9 | [http_service] 10 | internal_port = 7878 11 | force_https = true 12 | auto_stop_machines = true 13 | auto_start_machines = true 14 | min_machines_running = 0 15 | processes = ['app'] 16 | 17 | [mounts] 18 | source = "openui_data" 19 | destination = "/root/.openui" 20 | 21 | [env] 22 | OPENUI_ENVIRONMENT = "production" 23 | OPENUI_HOST = "https://openui.fly.dev" 24 | GITHUB_CLIENT_ID = "3af8fbeb90d06484dff0" 25 | WANDB_ENTITY = "wandb" 26 | WANDB_PROJECT = "openui-hosted" 27 | 28 | [[vm]] 29 | memory = '1gb' 30 | cpu_kind = 'shared' 31 | cpus = 1 32 | -------------------------------------------------------------------------------- /backend/openui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/backend/openui/__init__.py -------------------------------------------------------------------------------- /backend/openui/__main__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from .logs import setup_logger 3 | from . import server 4 | from . import config 5 | from .litellm import generate_config 6 | import os 7 | import uvicorn 8 | from uvicorn import Config 9 | import sys 10 | import subprocess 11 | import time 12 | 13 | 14 | def is_running_in_docker(): 15 | # Check for the .dockerenv file 16 | if os.path.exists("/.dockerenv"): 17 | return True 18 | 19 | # Check for Docker-related entries in /proc/self/cgroup 20 | try: 21 | with open("/proc/self/cgroup", "r") as file: 22 | for line in file: 23 | if "docker" in line: 24 | return True 25 | except Exception as e: 26 | pass 27 | 28 | if config.ENV == config.Env.PROD: 29 | return True 30 | 31 | return False 32 | 33 | 34 | if __name__ == "__main__": 35 | ui = any([arg == "-i" for arg in sys.argv]) 36 | litellm = ( 37 | any([arg == "--litellm" for arg in sys.argv]) 38 | or "OPENUI_LITELLM_CONFIG" in os.environ 39 | or os.path.exists("litellm-config.yaml") 40 | ) 41 | # TODO: only render in interactive mode? 42 | print( 43 | (Path(__file__).parent / "logo.ascii").read_text(), file=sys.stderr, flush=True 44 | ) 45 | logger = setup_logger("/tmp/openui.log" if ui else None) 46 | logger.info("Starting OpenUI AI Server created by W&B...") 47 | 48 | reload = any([arg == "--dev" for arg in sys.argv]) 49 | if reload: 50 | config.ENV = config.Env.DEV 51 | logger.info("Running in dev mode") 52 | 53 | try: 54 | from .tui.app import OpenUIApp 55 | 56 | app = OpenUIApp() 57 | server.queue = app.queue 58 | except ImportError: 59 | if ui: 60 | logger.warning( 61 | "Install OpenUI with pip install .[tui] to use the terminal UI" 62 | ) 63 | ui = False 64 | 65 | config_file = Path(__file__).parent / "log_config.yaml" 66 | api_server = server.Server( 67 | Config( 68 | "openui.server:app", 69 | host="0.0.0.0" if is_running_in_docker() else "127.0.0.1", 70 | log_config=str(config_file) if ui else None, 71 | port=config.PORT, 72 | reload=reload, 73 | ) 74 | ) 75 | if ui: 76 | with api_server.run_in_thread(): 77 | logger.info("Running Terminal UI App") 78 | app.run() 79 | else: 80 | if litellm: 81 | config_path = "litellm-config.yaml" 82 | if "OPENUI_LITELLM_CONFIG" in os.environ: 83 | config_path = os.environ["OPENUI_LITELLM_CONFIG"] 84 | elif os.path.exists("/app/litellm-config.yaml"): 85 | config_path = "/app/litellm-config.yaml" 86 | else: 87 | config_path = generate_config() 88 | 89 | logger.info( 90 | f"Starting LiteLLM in the background with config: {config_path}" 91 | ) 92 | litellm_process = subprocess.Popen( 93 | ["litellm", "--config", config_path, "--port", "4000"], 94 | stdout=subprocess.PIPE, 95 | stderr=subprocess.PIPE, 96 | text=True, 97 | ) 98 | # Ensure litellm stays up for 5 seconds 99 | for i in range(5): 100 | if litellm_process.poll() is not None: 101 | stdout, stderr = litellm_process.communicate() 102 | logger.error(f"LiteLLM failed to start:\n{stderr}") 103 | break 104 | time.sleep(1) 105 | logger.info("Running API Server") 106 | mkcert_dir = Path.home() / ".vite-plugin-mkcert" 107 | 108 | if reload: 109 | # TODO: hot reload wasn't working with the server approach, and ctrl-C doesn't 110 | # work with the uvicorn.run approach, so here we are 111 | uvicorn.run( 112 | "openui.server:app", 113 | host="0.0.0.0" if is_running_in_docker() else "127.0.0.1", 114 | port=config.PORT, 115 | reload=reload, 116 | ) 117 | else: 118 | api_server.run_with_wandb() 119 | -------------------------------------------------------------------------------- /backend/openui/assets/funky.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/backend/openui/assets/funky.mp3 -------------------------------------------------------------------------------- /backend/openui/assets/question.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /backend/openui/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | import secrets 4 | from urllib.parse import urlparse 5 | from enum import Enum 6 | 7 | 8 | class Env(Enum): 9 | LOCAL = 1 10 | PROD = 2 11 | DEV = 3 12 | 13 | 14 | try: 15 | env = os.getenv("OPENUI_ENVIRONMENT", "local") 16 | if env == "production": 17 | env = "prod" 18 | elif env == "development": 19 | env = "dev" 20 | ENV = Env[env.upper()] 21 | except KeyError: 22 | print("Invalid environment, defaulting to running locally") 23 | ENV = Env.LOCAL 24 | 25 | default_db = Path.home() / ".openui" / "db.sqlite" 26 | default_db.parent.mkdir(exist_ok=True) 27 | DB = os.getenv("DATABASE", default_db) 28 | HOST = os.getenv( 29 | "OPENUI_HOST", 30 | "https://localhost:5173" if ENV == Env.DEV else "http://localhost:7878", 31 | ) 32 | RP_ID = urlparse(HOST).hostname 33 | SESSION_KEY = os.getenv("OPENUI_SESSION_KEY") 34 | if SESSION_KEY is None: 35 | env_path = Path.home() / ".openui" / ".env" 36 | if env_path.exists(): 37 | SESSION_KEY = env_path.read_text().splitlines()[0].split("=")[1] 38 | else: 39 | SESSION_KEY = secrets.token_hex(32) 40 | with env_path.open("w") as f: 41 | f.write(f"OPENUI_SESSION_KEY={SESSION_KEY}") 42 | # Set the LITELLM_MASTER_KEY to a random value if it's not already set 43 | if os.getenv("LITELLM_MASTER_KEY") is None: 44 | os.environ["LITELLM_MASTER_KEY"] = "sk-{SESSION_KEY}" 45 | # GPT 3.5 is 0.0005 per 1k tokens input and 0.0015 output 46 | # 700k puts us at a max of $1.00 spent per user over a 48 hour period 47 | MAX_TOKENS = int(os.getenv("OPENUI_MAX_TOKENS", "700000")) 48 | GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID") 49 | GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET") 50 | 51 | AWS_ENDPOINT_URL_S3 = os.getenv("AWS_ENDPOINT_URL_S3") 52 | AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID") 53 | AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY") 54 | BUCKET_NAME = os.getenv("BUCKET_NAME", "openui") 55 | 56 | # Cors, if you're hosting the annotator iframe elsewhere, add it here 57 | CORS_ORIGINS = os.getenv( 58 | "OPENUI_CORS_ORIGINS", "https://wandb.github.io,https://localhost:5173" 59 | ).split(",") 60 | 61 | # Model providers 62 | OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://127.0.0.1:11434") 63 | OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1") 64 | OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "xxx") 65 | GROQ_BASE_URL = os.getenv("GROQ_BASE_URL", "https://api.groq.com/openai/v1") 66 | GROQ_API_KEY = os.getenv("GROQ_API_KEY") 67 | LITELLM_API_KEY = os.getenv("LITELLM_API_KEY", os.getenv("LITELLM_MASTER_KEY")) 68 | LITELLM_BASE_URL = os.getenv("LITELLM_BASE_URL", "http://0.0.0.0:4000") 69 | PORT = int(os.getenv("PORT", 7878)) 70 | -------------------------------------------------------------------------------- /backend/openui/config.yaml: -------------------------------------------------------------------------------- 1 | litellm_settings: 2 | # callbacks: callbacks.weave_handler 3 | -------------------------------------------------------------------------------- /backend/openui/dist/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/backend/openui/dist/android-chrome-192x192.png -------------------------------------------------------------------------------- /backend/openui/dist/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/backend/openui/dist/android-chrome-512x512.png -------------------------------------------------------------------------------- /backend/openui/dist/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/backend/openui/dist/apple-touch-icon.png -------------------------------------------------------------------------------- /backend/openui/dist/assets/codicon-B16ygVZF.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wandb/openui/17764dcdafe83cf2058eb6841f74654775f04f62/backend/openui/dist/assets/codicon-B16ygVZF.ttf -------------------------------------------------------------------------------- /backend/openui/dist/assets/css-D1nB4Vcj.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Version: 0.49.0(383fdf3fc0e1e1a024068b8d0fd4f3dcbae74d04) 4 | * Released under the MIT license 5 | * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt 6 | *-----------------------------------------------------------------------------*/var e={wordPattern:/(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g,comments:{blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{markers:{start:new RegExp("^\\s*\\/\\*\\s*#region\\b\\s*(.*?)\\s*\\*\\/"),end:new RegExp("^\\s*\\/\\*\\s*#endregion\\b.*\\*\\/")}}},t={defaultToken:"",tokenPostfix:".css",ws:`[ 7 | \r\f]*`,identifier:"-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",brackets:[{open:"{",close:"}",token:"delimiter.bracket"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],tokenizer:{root:[{include:"@selector"}],selector:[{include:"@comments"},{include:"@import"},{include:"@strings"},["[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)",{token:"keyword",next:"@keyframedeclaration"}],["[@](page|content|font-face|-moz-document)",{token:"keyword"}],["[@](charset|namespace)",{token:"keyword",next:"@declarationbody"}],["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@selectorname"},["[\\*]","tag"],["[>\\+,]","delimiter"],["\\[",{token:"delimiter.bracket",next:"@selectorattribute"}],["{",{token:"delimiter.bracket",next:"@selectorbody"}]],selectorbody:[{include:"@comments"},["[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))","attribute.name","@rulevalue"],["}",{token:"delimiter.bracket",next:"@pop"}]],selectorname:[["(\\.|#(?=[^{])|%|(@identifier)|:)+","tag"]],selectorattribute:[{include:"@term"},["]",{token:"delimiter.bracket",next:"@pop"}]],term:[{include:"@comments"},["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@functioninvocation"},{include:"@numbers"},{include:"@name"},{include:"@strings"},["([<>=\\+\\-\\*\\/\\^\\|\\~,])","delimiter"],[",","delimiter"]],rulevalue:[{include:"@comments"},{include:"@strings"},{include:"@term"},["!important","keyword"],[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],warndebug:[["[@](warn|debug)",{token:"keyword",next:"@declarationbody"}]],import:[["[@](import)",{token:"keyword",next:"@declarationbody"}]],urldeclaration:[{include:"@strings"},[`[^)\r 8 | ]+`,"string"],["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],parenthizedterm:[{include:"@term"},["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],declarationbody:[{include:"@term"},[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[/[^*/]+/,"comment"],[/./,"comment"]],name:[["@identifier","attribute.value"]],numbers:[["-?(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"attribute.value.number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","attribute.value.hex"]],units:[["(em|ex|ch|rem|fr|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","attribute.value.unit","@pop"]],keyframedeclaration:[["@identifier","attribute.value"],["{",{token:"delimiter.bracket",switchTo:"@keyframebody"}]],keyframebody:[{include:"@term"},["{",{token:"delimiter.bracket",next:"@selectorbody"}],["}",{token:"delimiter.bracket",next:"@pop"}]],functioninvocation:[["@identifier\\(",{token:"attribute.value",next:"@functionarguments"}]],functionarguments:[["\\$@identifier@ws:","attribute.name"],["[,]","delimiter"],{include:"@term"},["\\)",{token:"attribute.value",next:"@pop"}]],strings:[['~?"',{token:"string",next:"@stringenddoublequote"}],["~?'",{token:"string",next:"@stringendquote"}]],stringenddoublequote:[["\\\\.","string"],['"',{token:"string",next:"@pop"}],[/[^\\"]+/,"string"],[".","string"]],stringendquote:[["\\\\.","string"],["'",{token:"string",next:"@pop"}],[/[^\\']+/,"string"],[".","string"]]}};export{e as conf,t as language}; 9 | -------------------------------------------------------------------------------- /backend/openui/dist/assets/javascript-BcV1SRi8.js: -------------------------------------------------------------------------------- 1 | import{conf as t,language as e}from"./typescript-BfKWl9Pr.js";import"./CodeEditor-B9qhAAku.js";import"./index-B7PjGjI7.js";import"./index-DnTpCebm.js";/*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Version: 0.49.0(383fdf3fc0e1e1a024068b8d0fd4f3dcbae74d04) 4 | * Released under the MIT license 5 | * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt 6 | *-----------------------------------------------------------------------------*/var a=t,n={defaultToken:"invalid",tokenPostfix:".js",keywords:["break","case","catch","class","continue","const","constructor","debugger","default","delete","do","else","export","extends","false","finally","for","from","function","get","if","import","in","instanceof","let","new","null","return","set","static","super","switch","symbol","this","throw","true","try","typeof","undefined","var","void","while","with","yield","async","await","of"],typeKeywords:[],operators:e.operators,symbols:e.symbols,escapes:e.escapes,digits:e.digits,octaldigits:e.octaldigits,binarydigits:e.binarydigits,hexdigits:e.hexdigits,regexpctl:e.regexpctl,regexpesc:e.regexpesc,tokenizer:e.tokenizer};export{a as conf,n as language}; 7 | -------------------------------------------------------------------------------- /backend/openui/dist/assets/markdown-7fQo6M4U.js: -------------------------------------------------------------------------------- 1 | /*!----------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Version: 0.49.0(383fdf3fc0e1e1a024068b8d0fd4f3dcbae74d04) 4 | * Released under the MIT license 5 | * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt 6 | *-----------------------------------------------------------------------------*/var e={comments:{blockComment:[""]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">",notIn:["string"]}],surroundingPairs:[{open:"(",close:")"},{open:"[",close:"]"},{open:"`",close:"`"}],folding:{markers:{start:new RegExp("^\\s*"),end:new RegExp("^\\s*")}}},t={defaultToken:"",tokenPostfix:".md",control:/[\\`*_\[\]{}()#+\-\.!]/,noncontrol:/[^\\`*_\[\]{}()#+\-\.!]/,escapes:/\\(?:@control)/,jsescapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,empty:["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param"],tokenizer:{root:[[/^\s*\|/,"@rematch","@table_header"],[/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/,["white","keyword","keyword","keyword"]],[/^\s*(=+|\-+)\s*$/,"keyword"],[/^\s*((\*[ ]?)+)\s*$/,"meta.separator"],[/^\s*>+/,"comment"],[/^\s*([\*\-+:]|\d+\.)\s/,"keyword"],[/^(\t|[ ]{4})[^ ].*$/,"string"],[/^\s*~~~\s*((?:\w|[\/\-#])+)?\s*$/,{token:"string",next:"@codeblock"}],[/^\s*```\s*((?:\w|[\/\-#])+).*$/,{token:"string",next:"@codeblockgh",nextEmbedded:"$1"}],[/^\s*```\s*$/,{token:"string",next:"@codeblock"}],{include:"@linecontent"}],table_header:[{include:"@table_common"},[/[^\|]+/,"keyword.table.header"]],table_body:[{include:"@table_common"},{include:"@linecontent"}],table_common:[[/\s*[\-:]+\s*/,{token:"keyword",switchTo:"table_body"}],[/^\s*\|/,"keyword.table.left"],[/^\s*[^\|]/,"@rematch","@pop"],[/^\s*$/,"@rematch","@pop"],[/\|/,{cases:{"@eos":"keyword.table.right","@default":"keyword.table.middle"}}]],codeblock:[[/^\s*~~~\s*$/,{token:"string",next:"@pop"}],[/^\s*```\s*$/,{token:"string",next:"@pop"}],[/.*$/,"variable.source"]],codeblockgh:[[/```\s*$/,{token:"string",next:"@pop",nextEmbedded:"@pop"}],[/[^`]+/,"variable.source"]],linecontent:[[/&\w+;/,"string.escape"],[/@escapes/,"escape"],[/\b__([^\\_]|@escapes|_(?!_))+__\b/,"strong"],[/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/,"strong"],[/\b_[^_]+_\b/,"emphasis"],[/\*([^\\*]|@escapes)+\*/,"emphasis"],[/`([^\\`]|@escapes)+`/,"variable"],[/\{+[^}]+\}+/,"string.target"],[/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/,["string.link","","string.link"]],[/(!?\[)((?:[^\]\\]|@escapes)*)(\])/,"string.link"],{include:"html"}],html:[[/<(\w+)\/>/,"tag"],[/<(\w+)(\-|\w)*/,{cases:{"@empty":{token:"tag",next:"@tag.$1"},"@default":{token:"tag",next:"@tag.$1"}}}],[/<\/(\w+)(\-|\w)*\s*>/,{token:"tag"}],[//,"comment","@pop"],[/