Merge pull request #111 from ollama-webui/dev

WIP feat: backend reverse proxy
This commit is contained in:
Timothy Jaeryang Baek 2023-11-15 15:33:03 -05:00 committed by GitHub
commit 29788ca3d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 233 additions and 71 deletions

View file

@ -1,20 +1,36 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM node:alpine FROM node:alpine as build
WORKDIR /app
ARG OLLAMA_API_BASE_URL='' ARG OLLAMA_API_BASE_URL='/ollama/api'
RUN echo $OLLAMA_API_BASE_URL RUN echo $OLLAMA_API_BASE_URL
ENV ENV prod
ENV PUBLIC_API_BASE_URL $OLLAMA_API_BASE_URL ENV PUBLIC_API_BASE_URL $OLLAMA_API_BASE_URL
RUN echo $PUBLIC_API_BASE_URL RUN echo $PUBLIC_API_BASE_URL
WORKDIR /app
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
RUN npm ci RUN npm ci
COPY . . COPY . .
RUN npm run build RUN npm run build
CMD [ "npm", "run", "start"] FROM python:3.11-slim-buster as base
ARG OLLAMA_API_BASE_URL='/ollama/api'
ENV ENV=prod
ENV OLLAMA_API_BASE_URL $OLLAMA_API_BASE_URL
WORKDIR /app
COPY --from=build /app/build /app/build
WORKDIR /app/backend
COPY ./backend/requirements.txt ./requirements.txt
RUN pip3 install -r requirements.txt
COPY ./backend .
CMD [ "sh", "start.sh"]

100
README.md
View file

@ -47,21 +47,19 @@ ChatGPT-Style Web Interface for Ollama 🦙
- ⚙️ **Fine-Tuned Control with Advanced Parameters**: Gain a deeper level of control by adjusting parameters such as temperature and defining your system prompts to tailor the conversation to your specific preferences and needs. - ⚙️ **Fine-Tuned Control with Advanced Parameters**: Gain a deeper level of control by adjusting parameters such as temperature and defining your system prompts to tailor the conversation to your specific preferences and needs.
- 🔐 **Auth Header Support**: Effortlessly enhance security by adding Authorization headers to Ollama requests directly from the web UI settings, ensuring access to secured Ollama servers.
- 🔗 **External Ollama Server Connection**: Seamlessly link to an external Ollama server hosted on a different address by configuring the environment variable during the Docker build phase. Additionally, you can also set the external server connection URL from the web UI post-build. - 🔗 **External Ollama Server Connection**: Seamlessly link to an external Ollama server hosted on a different address by configuring the environment variable during the Docker build phase. Additionally, you can also set the external server connection URL from the web UI post-build.
- 🔑 **Auth Header Support**: Securely access Ollama servers with added Authorization headers for enhanced authentication. - 🔒 **Backend Reverse Proxy Support**: Strengthen security by enabling direct communication between Ollama Web UI backend and Ollama, eliminating the need to expose Ollama over LAN.
- 🌟 **Continuous Updates**: We are committed to improving Ollama Web UI with regular updates and new features. - 🌟 **Continuous Updates**: We are committed to improving Ollama Web UI with regular updates and new features.
## How to Install 🚀 ## How to Install 🚀
### Prerequisites ### Installing Both Ollama and Ollama Web UI Using Docker Compose
Make sure you have the latest version of Ollama installed before proceeding with the installation. You can find the latest version of Ollama at [https://ollama.ai/](https://ollama.ai/). If you don't have Ollama installed yet, you can use the provided Docker Compose file for a hassle-free installation. Simply run the following command:
#### Installing Both Ollama and Ollama Web UI Using Docker Compose
If you don't have Ollama installed, you can also use the provided Docker Compose file for a hassle-free installation. Simply run the following command:
```bash ```bash
docker compose up --build docker compose up --build
@ -69,86 +67,84 @@ docker compose up --build
This command will install both Ollama and Ollama Web UI on your system. Ensure to modify the `compose.yaml` file for GPU support if needed. This command will install both Ollama and Ollama Web UI on your system. Ensure to modify the `compose.yaml` file for GPU support if needed.
#### Checking Ollama ### Installing Ollama Web UI Only
After installing, verify that Ollama is running by accessing the following link in your web browser: [http://127.0.0.1:11434/](http://127.0.0.1:11434/). Note that the port number may differ based on your system configuration. #### Prerequisites
#### Accessing Ollama Web Interface over LAN Make sure you have the latest version of Ollama installed before proceeding with the installation. You can find the latest version of Ollama at [https://ollama.ai/](https://ollama.ai/).
If you want to access the Ollama web interface over LAN, for example, from your phone, run Ollama using the following command: ##### Checking Ollama
After installing Ollama, verify that Ollama is running by accessing the following link in your web browser: [http://127.0.0.1:11434/](http://127.0.0.1:11434/). Note that the port number may differ based on your system configuration.
#### Using Docker 🐳
If Ollama is hosted on your local machine and accessible at [http://127.0.0.1:11434/](http://127.0.0.1:11434/), run the following command:
```bash ```bash
OLLAMA_HOST=0.0.0.0 OLLAMA_ORIGINS=* ollama serve docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway --name ollama-webui --restart always ghcr.io/ollama-webui/ollama-webui:main
```
In case you encounter any issues running the command and encounter errors, ensure to turn off any existing Ollama service that might be running in the background before retrying.
If you're running Ollama via Docker:
```bash
docker run -d -v ollama:/root/.ollama -p 11434:11434 -e OLLAMA_ORIGINS="*" --name ollama ollama/ollama
```
### Using Docker 🐳
If Ollama is hosted on your local machine, run the following command:
```bash
docker run -d -p 3000:8080 --name ollama-webui --restart always ghcr.io/ollama-webui/ollama-webui:main
``` ```
Alternatively, if you prefer to build the container yourself, use the following command: Alternatively, if you prefer to build the container yourself, use the following command:
```bash ```bash
docker build --build-arg OLLAMA_API_BASE_URL='' -t ollama-webui . docker build -t ollama-webui .
docker run -d -p 3000:8080 --name ollama-webui --restart always ollama-webui docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway --name ollama-webui --restart always ollama-webui
``` ```
Your Ollama Web UI should now be hosted at [http://localhost:3000](http://localhost:3000). Enjoy! 😄 Your Ollama Web UI should now be hosted at [http://localhost:3000](http://localhost:3000) and accessible over LAN (or Network). Enjoy! 😄
#### Connecting to Ollama on a Different Server #### Accessing External Ollama on a Different Server
If Ollama is hosted on a server other than your local machine, change `OLLAMA_API_BASE_URL` to match: Change `OLLAMA_API_BASE_URL` environment variable to match the external Ollama Server url:
```bash ```bash
docker build --build-arg OLLAMA_API_BASE_URL='https://example.com/api' -t ollama-webui . docker run -d -p 3000:8080 -e OLLAMA_API_BASE_URL=https://example.com/api --name ollama-webui --restart always ghcr.io/ollama-webui/ollama-webui:main
docker run -d -p 3000:8080 --name ollama-webui --restart always ollama-webui ```
Alternatively, if you prefer to build the container yourself, use the following command:
```bash
docker build -t ollama-webui .
docker run -d -p 3000:8080 -e OLLAMA_API_BASE_URL=https://example.com/api --name ollama-webui --restart always ollama-webui
``` ```
## How to Build for Static Deployment ## How to Build for Static Deployment
1. Install `node` 1. Clone & Enter the project
```sh
# Mac, Linux
curl https://webi.sh/node@lts | sh
source ~/.config/envman/PATH.env
```
```pwsh
# Windows
curl.exe https://webi.ms/node@lts | powershell
```
2. Clone & Enter the project
```sh ```sh
git clone https://github.com/ollama-webui/ollama-webui.git git clone https://github.com/ollama-webui/ollama-webui.git
pushd ./ollama-webui/ pushd ./ollama-webui/
``` ```
3. Create and edit `.env`
2. Create and edit `.env`
```sh ```sh
cp -RPp example.env .env cp -RPp example.env .env
``` ```
3. Install node dependencies
```sh
npm i
```
4. Run in dev mode, or build the site for deployment 4. Run in dev mode, or build the site for deployment
- Test in Dev mode: - Test in Dev mode:
```sh ```sh
npm run dev npm run dev
``` ```
- Build for Deploy: \
(`PUBLIC_API_BASE_URL` will overwrite the value in `.env`) - Build for Deploy:
```sh ```sh
#`PUBLIC_API_BASE_URL` will overwrite the value in `.env`
PUBLIC_API_BASE_URL='https://example.com/api' npm run build PUBLIC_API_BASE_URL='https://example.com/api' npm run build
``` ```
5. Test the build with `caddy` (or the server of your choice) 5. Test the build with `caddy` (or the server of your choice)
```sh ```sh
@ -168,9 +164,9 @@ See [TROUBLESHOOTING.md](/TROUBLESHOOTING.md) for information on how to troubles
Here are some exciting tasks on our to-do list: Here are some exciting tasks on our to-do list:
- 🔐 **Access Control**: Securely manage requests to Ollama by utilizing the backend as a reverse proxy gateway, ensuring only authenticated users can send specific requests.
- 🧪 **Research-Centric Features**: Empower researchers in the fields of LLM and HCI with a comprehensive web UI for conducting user studies. Stay tuned for ongoing feature enhancements (e.g., surveys, analytics, and participant tracking) to facilitate their research. - 🧪 **Research-Centric Features**: Empower researchers in the fields of LLM and HCI with a comprehensive web UI for conducting user studies. Stay tuned for ongoing feature enhancements (e.g., surveys, analytics, and participant tracking) to facilitate their research.
- 📈 **User Study Tools**: Providing specialized tools, like heat maps and behavior tracking modules, to empower researchers in capturing and analyzing user behavior patterns with precision and accuracy. - 📈 **User Study Tools**: Providing specialized tools, like heat maps and behavior tracking modules, to empower researchers in capturing and analyzing user behavior patterns with precision and accuracy.
- 🌐 **Web Browser Extension**: Seamlessly integrate our services into your browsing experience with our convenient browser extension.
- 📚 **Enhanced Documentation**: Elevate your setup and customization experience with improved, comprehensive documentation. - 📚 **Enhanced Documentation**: Elevate your setup and customization experience with improved, comprehensive documentation.
Feel free to contribute and help us make Ollama Web UI even better! 🙌 Feel free to contribute and help us make Ollama Web UI even better! 🙌

2
backend/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__pycache__
.env

View file

@ -0,0 +1,55 @@
from flask import Flask, request, Response
from flask_cors import CORS
import requests
import json
from config import OLLAMA_API_BASE_URL
app = Flask(__name__)
CORS(
app
) # Enable Cross-Origin Resource Sharing (CORS) to allow requests from different domains
# Define the target server URL
TARGET_SERVER_URL = OLLAMA_API_BASE_URL
@app.route("/", defaults={"path": ""}, methods=["GET", "POST", "PUT", "DELETE"])
@app.route("/<path:path>", methods=["GET", "POST", "PUT", "DELETE"])
def proxy(path):
# Combine the base URL of the target server with the requested path
target_url = f"{TARGET_SERVER_URL}/{path}"
print(target_url)
# Get data from the original request
data = request.get_data()
headers = dict(request.headers)
# Make a request to the target server
target_response = requests.request(
method=request.method,
url=target_url,
data=data,
headers=headers,
stream=True, # Enable streaming for server-sent events
)
# Proxy the target server's response to the client
def generate():
for chunk in target_response.iter_content(chunk_size=8192):
yield chunk
response = Response(generate(), status=target_response.status_code)
# Copy headers from the target server's response to the client's response
for key, value in target_response.headers.items():
response.headers[key] = value
return response
if __name__ == "__main__":
app.run(debug=True)

15
backend/config.py Normal file
View file

@ -0,0 +1,15 @@
import sys
import os
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())
ENV = os.environ.get("ENV", "dev")
OLLAMA_API_BASE_URL = os.environ.get(
"OLLAMA_API_BASE_URL", "http://localhost:11434/api"
)
if ENV == "prod":
if OLLAMA_API_BASE_URL == "/ollama/api":
OLLAMA_API_BASE_URL = "http://host.docker.internal:11434/api"

1
backend/dev.sh Normal file
View file

@ -0,0 +1 @@
uvicorn main:app --port 8080 --reload

51
backend/main.py Normal file
View file

@ -0,0 +1,51 @@
import time
import sys
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi import HTTPException
from starlette.exceptions import HTTPException as StarletteHTTPException
from fastapi.middleware.wsgi import WSGIMiddleware
from fastapi.middleware.cors import CORSMiddleware
from apps.ollama.main import app as ollama_app
class SPAStaticFiles(StaticFiles):
async def get_response(self, path: str, scope):
try:
return await super().get_response(path, scope)
except (HTTPException, StarletteHTTPException) as ex:
if ex.status_code == 404:
return await super().get_response("index.html", scope)
else:
raise ex
app = FastAPI()
origins = ["*"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def check_url(request: Request, call_next):
start_time = int(time.time())
response = await call_next(request)
process_time = int(time.time()) - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
app.mount("/ollama/api", WSGIMiddleware(ollama_app))
app.mount("/", SPAStaticFiles(directory="../build", html=True), name="spa-static-files")

19
backend/requirements.txt Normal file
View file

@ -0,0 +1,19 @@
fastapi
uvicorn[standard]
pydantic
python-multipart
flask
flask_cors
python-socketio
python-jose
passlib[bcrypt]
uuid
requests
pymongo
bcrypt
PyJWT
pyjwt[crypto]

1
backend/start.sh Normal file
View file

@ -0,0 +1 @@
uvicorn main:app --host 0.0.0.0 --port 8080

View file

@ -15,8 +15,6 @@ services:
- ollama:/root/.ollama - ollama:/root/.ollama
ports: ports:
- 11434:11434 - 11434:11434
environment:
- 'OLLAMA_ORIGINS=*'
container_name: ollama container_name: ollama
pull_policy: always pull_policy: always
tty: true tty: true
@ -24,16 +22,18 @@ services:
image: ollama/ollama:latest image: ollama/ollama:latest
ollama-webui: ollama-webui:
restart: unless-stopped
build: build:
context: . context: .
args: args:
OLLAMA_API_BASE_URL: '' OLLAMA_API_BASE_URL: '/ollama/api'
dockerfile: Dockerfile dockerfile: Dockerfile
image: ollama-webui:latest image: ollama-webui:latest
container_name: ollama-webui container_name: ollama-webui
ports: ports:
- 3000:8080 - 3000:8080
extra_hosts:
- host.docker.internal:host-gateway
restart: unless-stopped
volumes: volumes:
ollama: {} ollama: {}

BIN
demo.gif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.5 MiB

View file

@ -3,7 +3,7 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "http-server ./build", "start": "http-server ./build -P http://localhost:8080?",
"dev": "vite dev --host", "dev": "vite dev --host",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
@ -50,4 +50,4 @@
"svelte-french-toast": "^1.2.0", "svelte-french-toast": "^1.2.0",
"uuid": "^9.0.1" "uuid": "^9.0.1"
} }
} }

2
run.sh
View file

@ -1,5 +1,5 @@
docker stop ollama-webui || true docker stop ollama-webui || true
docker rm ollama-webui || true docker rm ollama-webui || true
docker build -t ollama-webui . docker build -t ollama-webui .
docker run -d -p 3000:8080 --name ollama-webui --restart always ollama-webui docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway --name ollama-webui --restart always ollama-webui
docker image prune -f docker image prune -f

View file

@ -8,7 +8,7 @@ export const API_BASE_URL =
: `http://localhost:11434/api` : `http://localhost:11434/api`
: PUBLIC_API_BASE_URL; : PUBLIC_API_BASE_URL;
export const WEB_UI_VERSION = 'v1.0.0-alpha.5'; export const WEB_UI_VERSION = 'v1.0.0-alpha.6';
// Source: https://kit.svelte.dev/docs/modules#$env-static-public // Source: https://kit.svelte.dev/docs/modules#$env-static-public
// This feature, akin to $env/static/private, exclusively incorporates environment variables // This feature, akin to $env/static/private, exclusively incorporates environment variables

View file

@ -2,4 +2,10 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
</script> </script>
<div>{$page.status}: {$page.error.message}</div> <div class=" bg-white dark:bg-gray-800 min-h-screen">
<div class=" flex h-full">
<div class="m-auto my-10 dark:text-gray-300 text-3xl font-semibold">
{$page.status}: {$page.error.message}
</div>
</div>
</div>

View file

@ -1,13 +1,13 @@
// if you want to generate a static html file // if you want to generate a static html file
// for your page. // for your page.
// Documentation: https://kit.svelte.dev/docs/page-options#prerender // Documentation: https://kit.svelte.dev/docs/page-options#prerender
export const prerender = true; // export const prerender = true;
// if you want to Generate a SPA // if you want to Generate a SPA
// you have to set ssr to false. // you have to set ssr to false.
// This is not the case (so set as true or comment the line) // This is not the case (so set as true or comment the line)
// Documentation: https://kit.svelte.dev/docs/page-options#ssr // Documentation: https://kit.svelte.dev/docs/page-options#ssr
// export const ssr = false; export const ssr = false;
// How to manage the trailing slashes in the URLs // How to manage the trailing slashes in the URLs
// the URL for about page witll be /about with 'ignore' (default) // the URL for about page witll be /about with 'ignore' (default)

View file

@ -1184,7 +1184,7 @@
</button> </button>
{:else} {:else}
<button <button
class=" self-center dark:hover:text-gray-300 disabled:text-gray-600 disabled:hover:text-gray-600 {selectedModelIdx === class=" self-center disabled:text-gray-600 disabled:hover:text-gray-600 {selectedModelIdx ===
0 0
? 'mr-3' ? 'mr-3'
: 'mr-7'}" : 'mr-7'}"

View file

@ -14,7 +14,7 @@ const config = {
adapter: adapter({ adapter: adapter({
pages: 'build', pages: 'build',
assets: 'build', assets: 'build',
fallback: null fallback: 'index.html'
}) })
} }
}; };