mirror of
https://github.com/coder/registry.git
synced 2026-06-02 20:48:14 +00:00
feat: add open-webui module (#580)
This module installs and runs Open WebUI using Python and pip within your Coder workspace.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="250" cy="250" r="250" fill="#fff"/>
|
||||
<path d="m335 150h40v200h-40zm-130 0a100 100 0 1 0 0 200 100 100 0 1 0 0-200zm0 40a60 60 0 1 1 0 120 60 60 0 1 1 0-120z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 293 B |
Binary file not shown.
|
After Width: | Height: | Size: 407 KiB |
@@ -0,0 +1,64 @@
|
||||
---
|
||||
display_name: Open WebUI
|
||||
description: A self-hosted AI chat interface supporting various LLM providers
|
||||
icon: ../../../../.icons/openwebui.svg
|
||||
verified: false
|
||||
tags: [ai, llm, chat, web, python]
|
||||
---
|
||||
|
||||
# Open WebUI
|
||||
|
||||
Open WebUI is a user-friendly web interface for interacting with Large Language Models. It provides a ChatGPT-like interface that can connect to various LLM providers including OpenAI, Ollama, and more.
|
||||
|
||||
```tf
|
||||
module "open-webui" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder-labs/open-webui/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.main.id
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Python 3.11 or higher** must be installed in your image (with `venv` module)
|
||||
- Port 7800 (default) or your custom port must be available
|
||||
|
||||
For Ubuntu/Debian, you can install Python 3.11 from [deadsnakes PPA](https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa):
|
||||
|
||||
```shell
|
||||
sudo add-apt-repository -y ppa:deadsnakes/ppa
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python3.11 python3.11-venv
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### With OpenAI API Key
|
||||
|
||||
```tf
|
||||
module "open-webui" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder-labs/open-webui/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.main.id
|
||||
|
||||
openai_api_key = var.openai_api_key
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Port and Data Directory
|
||||
|
||||
```tf
|
||||
module "open-webui" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder-labs/open-webui/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.main.id
|
||||
|
||||
http_server_port = 8080
|
||||
data_dir = "/home/coder/open-webui-data"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,94 @@
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = ">= 2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "agent_id" {
|
||||
type = string
|
||||
description = "The ID of a Coder agent."
|
||||
}
|
||||
|
||||
variable "http_server_log_path" {
|
||||
type = string
|
||||
description = "The path to log Open WebUI to."
|
||||
default = "/tmp/open-webui.log"
|
||||
}
|
||||
|
||||
variable "http_server_port" {
|
||||
type = number
|
||||
description = "The port to run Open WebUI on."
|
||||
default = 7800
|
||||
}
|
||||
|
||||
variable "open_webui_version" {
|
||||
type = string
|
||||
description = "The version of Open WebUI to install"
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "data_dir" {
|
||||
type = string
|
||||
description = "The directory where Open WebUI stores its data (database, uploads, vector_db, cache)."
|
||||
default = ".open-webui"
|
||||
}
|
||||
|
||||
variable "openai_api_key" {
|
||||
type = string
|
||||
description = "OpenAI API key for accessing OpenAI models. If not provided, OpenAI integration will need to be configured manually in the UI."
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "share" {
|
||||
type = string
|
||||
description = "The sharing level for the Open WebUI app. Set to 'owner' for private access, 'authenticated' for access by any authenticated user, or 'public' for public access."
|
||||
default = "owner"
|
||||
validation {
|
||||
condition = var.share == "owner" || var.share == "authenticated" || var.share == "public"
|
||||
error_message = "Incorrect value. Please set either 'owner', 'authenticated', or 'public'."
|
||||
}
|
||||
}
|
||||
|
||||
variable "order" {
|
||||
type = number
|
||||
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "group" {
|
||||
type = string
|
||||
description = "The name of a group that this app belongs to."
|
||||
default = null
|
||||
}
|
||||
|
||||
resource "coder_script" "open-webui" {
|
||||
agent_id = var.agent_id
|
||||
display_name = "open-webui"
|
||||
icon = "/icon/openwebui.svg"
|
||||
script = templatefile("${path.module}/run.sh", {
|
||||
HTTP_SERVER_LOG_PATH : var.http_server_log_path,
|
||||
HTTP_SERVER_PORT : var.http_server_port,
|
||||
VERSION : var.open_webui_version,
|
||||
DATA_DIR : var.data_dir,
|
||||
OPENAI_API_KEY : var.openai_api_key,
|
||||
})
|
||||
run_on_start = true
|
||||
}
|
||||
|
||||
resource "coder_app" "open-webui" {
|
||||
agent_id = var.agent_id
|
||||
slug = "open-webui"
|
||||
display_name = "Open WebUI"
|
||||
url = "http://localhost:${var.http_server_port}"
|
||||
icon = "/icon/openwebui.svg"
|
||||
subdomain = true
|
||||
share = var.share
|
||||
order = var.order
|
||||
group = var.group
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
mock_provider "coder" {}
|
||||
|
||||
run "test_defaults" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-123"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.http_server_port == 7800
|
||||
error_message = "Default port should be 7800"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.http_server_log_path == "/tmp/open-webui.log"
|
||||
error_message = "Default log path should be /tmp/open-webui.log"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.share == "owner"
|
||||
error_message = "Default share should be 'owner'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.open_webui_version == "latest"
|
||||
error_message = "Default version should be 'latest'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = coder_app.open-webui.subdomain == true
|
||||
error_message = "App should use subdomain"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = coder_app.open-webui.display_name == "Open WebUI"
|
||||
error_message = "App display name should be 'Open WebUI'"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_custom_port" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-456"
|
||||
http_server_port = 9000
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.http_server_port == 9000
|
||||
error_message = "Custom port should be 9000"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = coder_app.open-webui.url == "http://localhost:9000"
|
||||
error_message = "App URL should use custom port"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_custom_log_path" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-789"
|
||||
http_server_log_path = "/var/log/open-webui.log"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.http_server_log_path == "/var/log/open-webui.log"
|
||||
error_message = "Custom log path should be set"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_share_authenticated" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-auth"
|
||||
share = "authenticated"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = coder_app.open-webui.share == "authenticated"
|
||||
error_message = "Share should be 'authenticated'"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_share_public" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-public"
|
||||
share = "public"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = coder_app.open-webui.share == "public"
|
||||
error_message = "Share should be 'public'"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_order_and_group" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-order"
|
||||
order = 10
|
||||
group = "AI Tools"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = coder_app.open-webui.order == 10
|
||||
error_message = "Order should be 10"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = coder_app.open-webui.group == "AI Tools"
|
||||
error_message = "Group should be 'AI Tools'"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_custom_version" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-version"
|
||||
open_webui_version = "0.5.0"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.open_webui_version == "0.5.0"
|
||||
error_message = "Custom version should be '0.5.0'"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_custom_data_dir" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-data"
|
||||
data_dir = "/home/coder/open-webui-data"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.data_dir == "/home/coder/open-webui-data"
|
||||
error_message = "Custom data_dir should be set"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_default_data_dir" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-data-default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.data_dir == ".open-webui"
|
||||
error_message = "Default data_dir should be '.open-webui'"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_openai_api_key" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-openai"
|
||||
openai_api_key = "sk-test-key-123"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.openai_api_key == "sk-test-key-123"
|
||||
error_message = "OpenAI API key should be set"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_default_openai_api_key" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-openai-default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.openai_api_key == ""
|
||||
error_message = "Default OpenAI API key should be empty"
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -eu
|
||||
|
||||
printf '\033[0;1mInstalling Open WebUI %s...\n\n' "${VERSION}"
|
||||
|
||||
check_python_version() {
|
||||
python_cmd="$1"
|
||||
if command -v "$python_cmd" > /dev/null 2>&1; then
|
||||
version=$("$python_cmd" --version 2>&1 | awk '{print $2}')
|
||||
major=$(echo "$version" | cut -d. -f1)
|
||||
minor=$(echo "$version" | cut -d. -f2)
|
||||
if [ "$major" -eq 3 ] && [ "$minor" -ge 11 ]; then
|
||||
echo "$python_cmd"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
PYTHON_CMD=""
|
||||
for cmd in python3.13 python3.12 python3.11 python3 python; do
|
||||
if result=$(check_python_version "$cmd"); then
|
||||
PYTHON_CMD="$result"
|
||||
echo "✅ Found suitable Python: $PYTHON_CMD ($($PYTHON_CMD --version 2>&1))"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$PYTHON_CMD" ]; then
|
||||
echo "❌ Python 3.11 or higher is required but not found."
|
||||
echo ""
|
||||
echo "Please install Python 3.11+ in your image. For example on Ubuntu/Debian:"
|
||||
echo " sudo add-apt-repository -y ppa:deadsnakes/ppa"
|
||||
echo " sudo apt-get update"
|
||||
echo " sudo apt-get install -y python3.11 python3.11-venv"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VENV_DIR="$HOME/.open-webui-venv"
|
||||
if [ ! -d "$VENV_DIR" ]; then
|
||||
echo "📦 Creating virtual environment..."
|
||||
"$PYTHON_CMD" -m venv "$VENV_DIR"
|
||||
fi
|
||||
. "$VENV_DIR/bin/activate"
|
||||
|
||||
if ! pip show open-webui > /dev/null 2>&1; then
|
||||
echo "📦 Installing Open WebUI version ${VERSION}..."
|
||||
if [ "${VERSION}" = "latest" ]; then
|
||||
pip install open-webui
|
||||
else
|
||||
pip install "open-webui==${VERSION}"
|
||||
fi
|
||||
echo "🥳 Open WebUI has been installed"
|
||||
else
|
||||
echo "✅ Open WebUI is already installed"
|
||||
fi
|
||||
|
||||
echo "👷 Starting Open WebUI in background..."
|
||||
echo "Check logs at ${HTTP_SERVER_LOG_PATH}"
|
||||
|
||||
DATA_DIR="${DATA_DIR}" \
|
||||
OPENAI_API_KEY="${OPENAI_API_KEY}" \
|
||||
open-webui serve --host 0.0.0.0 --port "${HTTP_SERVER_PORT}" > "${HTTP_SERVER_LOG_PATH}" 2>&1 &
|
||||
|
||||
echo "🥳 Open WebUI is ready. HTTP server is listening on port ${HTTP_SERVER_PORT}"
|
||||
Reference in New Issue
Block a user