From dc7cf27a19a6444eb0463a68899bead740abe69b Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Wed, 23 Jul 2025 10:49:29 +0200 Subject: [PATCH 01/11] Open WebUI: Add full end-to-end example rig --- application/open-webui/README.md | 64 ++++++++++++ application/open-webui/backlog.md | 6 ++ application/open-webui/compose.yml | 124 +++++++++++++++++++++++ application/open-webui/init.sql | 25 +++++ application/open-webui/setup.Dockerfile | 34 +++++++ application/open-webui/setup.sh | 38 +++++++ application/open-webui/tool-servers.json | 18 ++++ 7 files changed, 309 insertions(+) create mode 100644 application/open-webui/README.md create mode 100644 application/open-webui/backlog.md create mode 100644 application/open-webui/compose.yml create mode 100644 application/open-webui/init.sql create mode 100644 application/open-webui/setup.Dockerfile create mode 100755 application/open-webui/setup.sh create mode 100644 application/open-webui/tool-servers.json diff --git a/application/open-webui/README.md b/application/open-webui/README.md new file mode 100644 index 00000000..eda3eb55 --- /dev/null +++ b/application/open-webui/README.md @@ -0,0 +1,64 @@ +# Use CrateDB with Open WebUI + +## About + +A complete end-to-end rig including CrateDB, CrateDB MCPO, and Open WebUI. + +[Open WebUI] is an extensible, feature-rich, and user-friendly self-hosted AI +platform designed to operate entirely offline. It supports various LLM runners +like Ollama and OpenAI-compatible APIs, with built-in inference engine for RAG, +making it a powerful AI deployment solution. + +CrateDB MCPO is a wrapper around CrateDB MCP, publishing the exposed tools via +OpenAPI, to connect it to Open WebUI. + +## What's inside + +- `.env`: The dotenv file defines `OPENAI_API_KEY` for `compose.yml` and `OPEN_WEBUI_URL` for `setup.sh`. +- `compose.yml`: The service composition file defines three services: CrateDB, CrateDB MCPO, and Open WebUI. + Use it with `docker compose` or `podman compose`. +- `setup.Dockerfile`: Container runtime definition for `setup.sh`, including all requirements. +- `setup.sh`: Program that configures Open WebUI via HTTP API. +- `tool-servers.json`: JSON snippet for configuring the CrateDB MCPO server through `/api/v1/configs/tool_servers`. + +## Operate + +Configure the API key for OpenAI as environment variable, or define it within +the `.env` file. +```shell +export OPENAI_API_KEY= +``` + +Spin up the software stack. +```shell +docker compose up -d +``` + +## Usage + +You can access the services and their resources on those URLs. + +- CrateDB: http://localhost:4200/ +- CrateDB MCPO: http://localhost:5200/docs, http://localhost:5200/openapi.json +- Open WebUI: http://localhost:6200/, http://localhost:6200/docs + +## Configure + +To make the ensemble work well, you need to configure a few bits on the Open WebUI +user interface after ramping up the rig. + +- Make sure to enable the "CrateDB" tool on the left side of the query prompt, + behind the "More (`+`)" button. +- Make sure to enable "Function Calling: Native" within the flyout widget about + "Chat Controls", to be opened in the top right corner of the screen. +- If you like, you can dial down to "Temperature: 0.5". + +## Example questions + +Enjoy conversations with CrateDB and its documentation. + +- Text-to-SQL: What is the average value for sensor 1? +- Knowledgebase: How do I use CrateDB with SQLAlchemy? + + +[Open WebUI]: https://docs.openwebui.com/ diff --git a/application/open-webui/backlog.md b/application/open-webui/backlog.md new file mode 100644 index 00000000..1dd32add --- /dev/null +++ b/application/open-webui/backlog.md @@ -0,0 +1,6 @@ +# Open WebUI / MCPO backlog + +- `compose.yml`: The MCPO OCI currently does not include `curl`. + Improve healthcheck after adding it. +- Add example questions to general instructions à la "If the user asks about questions + to use here, enumerate those items: [...]" diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml new file mode 100644 index 00000000..4059b7a7 --- /dev/null +++ b/application/open-webui/compose.yml @@ -0,0 +1,124 @@ +# Use CrateDB with Open WebUI +# +# https://cratedb.com/docs/ +# https://docs.openwebui.com/getting-started/quick-start +--- +networks: + llm-demo: + name: llm-demo + driver: bridge + +volumes: + cratedb: + open-webui: + +services: + + # ------- + # CrateDB + # ------- + cratedb: + image: docker.io/crate/crate:6.0.0 + environment: + CRATE_HEAP_SIZE: 2g + ports: + - "4200:4200" + - "5432:5432" + command: [ + "crate", + "-Cdiscovery.type=single-node", + "-Ccluster.routing.allocation.disk.threshold_enabled=false", + ] + networks: + - llm-demo + volumes: + - cratedb:/data + healthcheck: + test: [ "CMD", "curl", "--fail", "http://localhost:4200" ] + start_period: 3s + interval: 10s + + # ------------ + # CrateDB MCPO + # ------------ + cratedb-mcpo: + image: ghcr.io/crate/cratedb-mcpo:0.0.5 + environment: + CRATEDB_CLUSTER_URL: http://crate:crate@cratedb:4200/ + ports: + - "5200:8000" + networks: + - llm-demo + #healthcheck: + # test: [ "CMD", "curl", "--fail", "http://localhost:8000/docs" ] + # start_period: 3s + # interval: 10s + + # ---------- + # Open WebUI + # ---------- + open-webui: + image: ghcr.io/open-webui/open-webui:0.6.18 + # https://docs.openwebui.com/getting-started/env-configuration + # https://docs.openwebui.com/getting-started/api-endpoints/#swagger-documentation-links + environment: + # From `.env` file. + OPENAI_API_KEY: + # Defined here. + ENABLE_SIGNUP: False + ENABLE_LOGIN_FORM: False + WEBUI_AUTH: False + DEFAULT_MODELS: "gpt-4.1" + DEFAULT_USER_ROLE: "admin" + ENABLE_CHANNELS: True + RESPONSE_WATERMARK: "This text is AI generated" + WEBUI_NAME: "CrateDB LLM Cockpit" + BYPASS_MODEL_ACCESS_CONTROL: True + ENABLE_OLLAMA_API: False + ENABLE_OPENAI_API: True + ENABLE_DIRECT_CONNECTIONS: True + ENV: "dev" + ports: + - "6200:8080" + networks: + - llm-demo + volumes: + - open-webui:/app/backend/data + healthcheck: + test: [ "CMD", "curl", "--fail", "http://localhost:8080" ] + start_period: 3s + interval: 10s + timeout: 90s + retries: 60 + + # ----- + # Setup + # ----- + setup: + build: + context: . + dockerfile: setup.Dockerfile + command: bash /app/setup.sh + networks: + - llm-demo + depends_on: + open-webui: + condition: service_healthy + + # ------- + # Bundler + # ------- + # Wait for all defined services to be fully available by probing their health + # status, even when using `docker compose up --detach`. + # https://marcopeg.com/2019/docker-compose-healthcheck/ + start-dependencies: + image: docker.io/dadarek/wait-for-dependencies + depends_on: + cratedb: + condition: service_healthy + #cratedb-mcpo: + # condition: service_healthy + open-webui: + condition: service_healthy + setup: + condition: service_completed_successfully diff --git a/application/open-webui/init.sql b/application/open-webui/init.sql new file mode 100644 index 00000000..811b16dd --- /dev/null +++ b/application/open-webui/init.sql @@ -0,0 +1,25 @@ +DROP TABLE IF EXISTS time_series_data; + +CREATE TABLE IF NOT EXISTS time_series_data ( + timestamp TIMESTAMP, + value DOUBLE, + location STRING, + sensor_id INT +); + +INSERT INTO time_series_data (timestamp, value, location, sensor_id) +VALUES + ('2023-09-14T00:00:00', 10.5, 'Sensor A', 1), + ('2023-09-14T01:00:00', 15.2, 'Sensor A', 1), + ('2023-09-14T02:00:00', 18.9, 'Sensor A', 1), + ('2023-09-14T03:00:00', 12.7, 'Sensor B', 2), + ('2023-09-14T04:00:00', 17.3, 'Sensor B', 2), + ('2023-09-14T05:00:00', 20.1, 'Sensor B', 2), + ('2023-09-14T06:00:00', 22.5, 'Sensor A', 1), + ('2023-09-14T07:00:00', 18.3, 'Sensor A', 1), + ('2023-09-14T08:00:00', 16.8, 'Sensor A', 1), + ('2023-09-14T09:00:00', 14.6, 'Sensor B', 2), + ('2023-09-14T10:00:00', 13.2, 'Sensor B', 2), + ('2023-09-14T11:00:00', 11.7, 'Sensor B', 2); + +REFRESH TABLE time_series_data; diff --git a/application/open-webui/setup.Dockerfile b/application/open-webui/setup.Dockerfile new file mode 100644 index 00000000..2d0d71f9 --- /dev/null +++ b/application/open-webui/setup.Dockerfile @@ -0,0 +1,34 @@ +FROM python:3.13-slim-bookworm + +# Configure operating system. +ENV DEBIAN_FRONTEND=noninteractive +ENV TERM=linux + +RUN set -e \ + && apt-get update \ + && apt-get --yes install --no-install-recommends --no-install-suggests curl jq \ + && rm -rf /var/lib/apt/lists/* + +# Install and configure `uv`. +# Guidelines that have been followed. +# - https://hynek.me/articles/docker-uv/ + +# Install the `uv` package manager. +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv +COPY --from=ghcr.io/astral-sh/uv:latest /uvx /usr/local/bin/uvx + +# - Tell uv to byte-compile packages for faster application startups. +# - Silence uv complaining about not being able to use hard links. +# - Prevent uv from accidentally downloading isolated Python builds. +# - Install packages into the system Python environment. +ENV \ + UV_COMPILE_BYTECODE=true \ + UV_LINK_MODE=copy \ + UV_PYTHON_DOWNLOADS=never \ + UV_SYSTEM_PYTHON=true + +RUN uv pip install crash cratedb-mcp httpie + +RUN mkdir /app +WORKDIR /app +COPY .env setup.sh *.json *.sql /app/ diff --git a/application/open-webui/setup.sh b/application/open-webui/setup.sh new file mode 100755 index 00000000..aada1b21 --- /dev/null +++ b/application/open-webui/setup.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Configure Open WebUI via HTTP API. + +# Runtime options. +set -e +set -x + +# Load variables. +source .env + +# Sign in to receive JWT token. +token=$( http --ignore-stdin POST ${OPEN_WEBUI_URL}/api/v1/auths/signin email= password= | jq -r .token ) +echo "JWT token: ${token}" + +# Inquire health. +http --ignore-stdin ${OPEN_WEBUI_URL}/health + +# Inquire available models. +#http ${OPEN_WEBUI_URL}/api/models Authorization:"Bearer $token" + +# List available tools. +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/tools/ Authorization:"Bearer $token" + +# Configure system prompt. +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/users/user/settings/update Authorization:"Bearer $token" ui[system]="$( cratedb-mcp show-prompt )" ui[notificationEnabled]="true" + +# Configure CrateDB MCPO server. +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/configs/tool_servers Authorization:"Bearer $token" "@tool-servers.json" + +# Configure chat model. +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/configs/models Authorization:"Bearer $token" DEFAULT_MODELS="gpt-4.1" MODEL_ORDER_LIST="[]" + +# Configure embedding model. +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/retrieval/embedding/update Authorization:"Bearer $token" embedding_engine="openai" embedding_model="text-embedding-3-small" + +# Provision database +crash --hosts ${CRATEDB_URL} < init.sql diff --git a/application/open-webui/tool-servers.json b/application/open-webui/tool-servers.json new file mode 100644 index 00000000..ff65f05e --- /dev/null +++ b/application/open-webui/tool-servers.json @@ -0,0 +1,18 @@ +{ + "TOOL_SERVER_CONNECTIONS": [ + { + "url": "http://cratedb-mcpo:8000", + "path": "openapi.json", + "auth_type": "bearer", + "key": "", + "config": { + "enable": true, + "access_control": null + }, + "info": { + "name": "CrateDB", + "description": "CrateDB Text-to-SQL and documentation inquiry." + } + } + ] +} \ No newline at end of file From a36c4972bdc4ce27efd5c0a25054e91157a66fe2 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Wed, 23 Jul 2025 21:51:56 +0200 Subject: [PATCH 02/11] Open WebUI: Add missing `.env` file --- application/open-webui/.env | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 application/open-webui/.env diff --git a/application/open-webui/.env b/application/open-webui/.env new file mode 100644 index 00000000..bb7ae504 --- /dev/null +++ b/application/open-webui/.env @@ -0,0 +1,19 @@ +# ------------------------------------------ +# User configuration +# ------------------------------------------ + +# Define your OpenAI API key here if you want to make it persistent. +# You can also use other models with Open WebUI, however that is +# currently out of the scope of this miniature rig. + +# OPENAI_API_KEY=your_openai_api_key + +# ------------------------------------------ +# System configuration +# ------------------------------------------ + +# If you run `setup.sh` from within the Docker Compose +# environment, you will not need to change those settings. + +CRATEDB_URL=http://cratedb:4200 +OPEN_WEBUI_URL=http://open-webui:8080 From 56eb694601c972044bb4abedfb86a18ad43c2f18 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Wed, 23 Jul 2025 22:18:06 +0200 Subject: [PATCH 03/11] Open WebUI: Improve documentation --- application/open-webui/README.md | 108 ++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 31 deletions(-) diff --git a/application/open-webui/README.md b/application/open-webui/README.md index eda3eb55..b5af2ba5 100644 --- a/application/open-webui/README.md +++ b/application/open-webui/README.md @@ -2,63 +2,109 @@ ## About -A complete end-to-end rig including CrateDB, CrateDB MCPO, and Open WebUI. +A complete end-to-end rig including CrateDB, CrateDB MCPO, and Open WebUI, +including a touch of integration tests on CI/GHA. + +## Introduction [Open WebUI] is an extensible, feature-rich, and user-friendly self-hosted AI platform designed to operate entirely offline. It supports various LLM runners like Ollama and OpenAI-compatible APIs, with built-in inference engine for RAG, making it a powerful AI deployment solution. -CrateDB MCPO is a wrapper around CrateDB MCP, publishing the exposed tools via -OpenAPI, to connect it to Open WebUI. +CrateDB MCPO is an adapter wrapper around the [CrateDB MCP] server. Because +Open WebUI uses [OpenAPI Tool Servers] to integrate external tooling and data +sources into LLM agents and workflows, standard MCP servers need to adapt to +how [Open WebUI MCP Support] works. -## What's inside +## Usage -- `.env`: The dotenv file defines `OPENAI_API_KEY` for `compose.yml` and `OPEN_WEBUI_URL` for `setup.sh`. -- `compose.yml`: The service composition file defines three services: CrateDB, CrateDB MCPO, and Open WebUI. - Use it with `docker compose` or `podman compose`. -- `setup.Dockerfile`: Container runtime definition for `setup.sh`, including all requirements. -- `setup.sh`: Program that configures Open WebUI via HTTP API. -- `tool-servers.json`: JSON snippet for configuring the CrateDB MCPO server through `/api/v1/configs/tool_servers`. +### Sources + +It is advised to clone the Git repository and run the demo stack from there. +In this spirit, it will be easy for you to receive updates. +```shell +git clone https://github.com/crate/cratedb-examples +cd cratedb-examples/application/open-webui +``` -## Operate +### Start services -Configure the API key for OpenAI as environment variable, or define it within -the `.env` file. +Configure the API key for OpenAI as environment variable, or otherwise define +it within the `.env` file to make it persistent for unattended service +operations. ```shell -export OPENAI_API_KEY= +export OPENAI_API_KEY= ``` -Spin up the software stack. +Spin up the software stack. On the first occasion, it will take a while to +download the OCI images and let Open WebUI do its thing when bootstrapping +the very first time. ```shell -docker compose up -d +docker compose up ``` -## Usage +### User interface -You can access the services and their resources on those URLs. +You can access the service's resources on those URLs. - CrateDB: http://localhost:4200/ -- CrateDB MCPO: http://localhost:5200/docs, http://localhost:5200/openapi.json -- Open WebUI: http://localhost:6200/, http://localhost:6200/docs +- Open WebUI: http://localhost:6200/ + +Explore the APIs here. -## Configure +- CrateDB MCPO: + - Swagger: http://localhost:5200/docs + - OpenAPI: http://localhost:5200/openapi.json +- Open WebUI: + - Swagger: http://localhost:6200/docs + - OpenAPI: http://localhost:6200/openapi.json + +### Configure To make the ensemble work well, you need to configure a few bits on the Open WebUI -user interface after ramping up the rig. +user interface. + +- Make sure to enable the "CrateDB" tool. The toggle switch is located within the + flyout menu on the left side of the query prompt, which can be opened using the + `More (+)` button. -- Make sure to enable the "CrateDB" tool on the left side of the query prompt, - behind the "More (`+`)" button. -- Make sure to enable "Function Calling: Native" within the flyout widget about - "Chat Controls", to be opened in the top right corner of the screen. -- If you like, you can dial down to "Temperature: 0.5". +- In the "Chat Controls" flyout widget, located in the top right corner of the page, + - make sure to enable `Function Calling: Native`, see [OPEN-WEBUI-15939], + - and dial down to `Temperature: 0.0`. -## Example questions +### Example questions -Enjoy conversations with CrateDB and its documentation. +Enjoy conversations with CrateDB (talk to your data) and its documentation +(talk to your knowledgebase). + +- Text-to-SQL: _What is the average value for sensor 1?_ +- Knowledgebase: _How do I use CrateDB with SQLAlchemy?_ + +### Stop services +Tear down services. +```shell +docker compose down +``` +Delete volumes. +```shell +docker volume rm open-webui_open-webui +``` +```shell +docker volume rm open-webui_cratedb +``` + +## What's inside -- Text-to-SQL: What is the average value for sensor 1? -- Knowledgebase: How do I use CrateDB with SQLAlchemy? +- `.env`: The dotenv file defines `OPENAI_API_KEY` for `compose.yml`. +- `compose.yml`: The service composition file defines three services: + CrateDB, CrateDB MCPO, and Open WebUI. Helper jobs (setup, test, ...) + excluded for brevity. Use it with Docker or Podman. +- `init/`: Runtime configuration snippets. +[CrateDB MCP]: https://cratedb.com/docs/guide/integrate/mcp/cratedb-mcp.html +[OpenAPI Tool Servers]: https://docs.openwebui.com/openapi-servers/ [Open WebUI]: https://docs.openwebui.com/ +[Open WebUI MCP Support]: https://docs.openwebui.com/openapi-servers/mcp/ +[OPEN-WEBUI-15939]: https://github.com/open-webui/open-webui/issues/15939#issuecomment-3108279768 From 7b07179436addf35ab419d7b0f51d2586d078f46 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 24 Jul 2025 00:11:07 +0200 Subject: [PATCH 04/11] Open WebUI: Address review suggestions by CodeRabbit --- application/open-webui/README.md | 21 ++++++++++++++++----- application/open-webui/backlog.md | 6 ------ application/open-webui/compose.yml | 11 ++++++++--- application/open-webui/init.sql | 4 ++-- application/open-webui/setup.sh | 13 +++++++++---- 5 files changed, 35 insertions(+), 20 deletions(-) delete mode 100644 application/open-webui/backlog.md diff --git a/application/open-webui/README.md b/application/open-webui/README.md index b5af2ba5..05d72616 100644 --- a/application/open-webui/README.md +++ b/application/open-webui/README.md @@ -5,6 +5,9 @@ A complete end-to-end rig including CrateDB, CrateDB MCPO, and Open WebUI, including a touch of integration tests on CI/GHA. +This stack is intended solely for demonstration purposes and does **not** +implement any security hardening. Do **not** deploy it to production. + ## Introduction [Open WebUI] is an extensible, feature-rich, and user-friendly self-hosted AI @@ -30,11 +33,15 @@ cd cratedb-examples/application/open-webui ### Start services -Configure the API key for OpenAI as environment variable, or otherwise define -it within the `.env` file to make it persistent for unattended service -operations. +Configure the API key for OpenAI within the `.env` file next to `compose.yml` +to make it persistent for unattended service operations. +```dotenv +# .env +OPENAI_API_KEY=your_openai_api_key_here +``` +Or export it for a one-off run: ```shell -export OPENAI_API_KEY= +export OPENAI_API_KEY=your_openai_api_key_here ``` Spin up the software stack. On the first occasion, it will take a while to @@ -86,7 +93,11 @@ Tear down services. ```shell docker compose down ``` -Delete volumes. +Delete all volumes. +```shell +docker compose down --volumes +``` +Delete individual volumes. ```shell docker volume rm open-webui_open-webui ``` diff --git a/application/open-webui/backlog.md b/application/open-webui/backlog.md deleted file mode 100644 index 1dd32add..00000000 --- a/application/open-webui/backlog.md +++ /dev/null @@ -1,6 +0,0 @@ -# Open WebUI / MCPO backlog - -- `compose.yml`: The MCPO OCI currently does not include `curl`. - Improve healthcheck after adding it. -- Add example questions to general instructions à la "If the user asks about questions - to use here, enumerate those items: [...]" diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml index 4059b7a7..573f883b 100644 --- a/application/open-webui/compose.yml +++ b/application/open-webui/compose.yml @@ -62,9 +62,9 @@ services: # https://docs.openwebui.com/getting-started/env-configuration # https://docs.openwebui.com/getting-started/api-endpoints/#swagger-documentation-links environment: - # From `.env` file. - OPENAI_API_KEY: - # Defined here. + # From caller's environment or `.env` file. + OPENAI_API_KEY: ${OPENAI_API_KEY} + # Currently defined here. ENABLE_SIGNUP: False ENABLE_LOGIN_FORM: False WEBUI_AUTH: False @@ -90,6 +90,9 @@ services: interval: 10s timeout: 90s retries: 60 + #depends_on: + # cratedb-mcpo: + # condition: service_healthy # ----- # Setup @@ -102,6 +105,8 @@ services: networks: - llm-demo depends_on: + cratedb: + condition: service_healthy open-webui: condition: service_healthy diff --git a/application/open-webui/init.sql b/application/open-webui/init.sql index 811b16dd..f38cff8e 100644 --- a/application/open-webui/init.sql +++ b/application/open-webui/init.sql @@ -1,13 +1,13 @@ DROP TABLE IF EXISTS time_series_data; CREATE TABLE IF NOT EXISTS time_series_data ( - timestamp TIMESTAMP, + "timestamp" TIMESTAMP, value DOUBLE, location STRING, sensor_id INT ); -INSERT INTO time_series_data (timestamp, value, location, sensor_id) +INSERT INTO time_series_data ("timestamp", value, location, sensor_id) VALUES ('2023-09-14T00:00:00', 10.5, 'Sensor A', 1), ('2023-09-14T01:00:00', 15.2, 'Sensor A', 1), diff --git a/application/open-webui/setup.sh b/application/open-webui/setup.sh index aada1b21..a21ded67 100755 --- a/application/open-webui/setup.sh +++ b/application/open-webui/setup.sh @@ -3,15 +3,20 @@ # Configure Open WebUI via HTTP API. # Runtime options. -set -e -set -x +set -euo pipefail + +# Uncomment for local debugging, but **never** in automated runs. +# set -x # Load variables. source .env # Sign in to receive JWT token. -token=$( http --ignore-stdin POST ${OPEN_WEBUI_URL}/api/v1/auths/signin email= password= | jq -r .token ) -echo "JWT token: ${token}" +token=$( http --ignore-stdin POST ${OPEN_WEBUI_URL}/api/v1/auths/signin email= password= | jq -r .token // empty ) +if [[ -z "${token}" ]]; then + echo "FATAL: Could not obtain JWT token from Open WebUI" >&2 + exit 1 +fi # Inquire health. http --ignore-stdin ${OPEN_WEBUI_URL}/health From bce6cf6f759669f07ac2109cd66caddaa4b11f25 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 24 Jul 2025 00:29:02 +0200 Subject: [PATCH 05/11] Open WebUI: Refactor bootstrapping code to `init` subdirectory --- application/open-webui/.env | 10 ------ application/open-webui/compose.yml | 8 +++-- application/open-webui/init/.env | 9 +++++ .../{setup.Dockerfile => init/Dockerfile} | 0 application/open-webui/{ => init}/init.sql | 0 application/open-webui/{ => init}/setup.sh | 33 ++++++++++++------- .../open-webui/{ => init}/tool-servers.json | 0 7 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 application/open-webui/init/.env rename application/open-webui/{setup.Dockerfile => init/Dockerfile} (100%) rename application/open-webui/{ => init}/init.sql (100%) rename application/open-webui/{ => init}/setup.sh (64%) rename application/open-webui/{ => init}/tool-servers.json (100%) diff --git a/application/open-webui/.env b/application/open-webui/.env index bb7ae504..e3d01049 100644 --- a/application/open-webui/.env +++ b/application/open-webui/.env @@ -7,13 +7,3 @@ # currently out of the scope of this miniature rig. # OPENAI_API_KEY=your_openai_api_key - -# ------------------------------------------ -# System configuration -# ------------------------------------------ - -# If you run `setup.sh` from within the Docker Compose -# environment, you will not need to change those settings. - -CRATEDB_URL=http://cratedb:4200 -OPEN_WEBUI_URL=http://open-webui:8080 diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml index 573f883b..fb77378f 100644 --- a/application/open-webui/compose.yml +++ b/application/open-webui/compose.yml @@ -53,6 +53,9 @@ services: # test: [ "CMD", "curl", "--fail", "http://localhost:8000/docs" ] # start_period: 3s # interval: 10s + depends_on: + cratedb: + condition: service_healthy # ---------- # Open WebUI @@ -99,14 +102,15 @@ services: # ----- setup: build: - context: . - dockerfile: setup.Dockerfile + context: init command: bash /app/setup.sh networks: - llm-demo depends_on: cratedb: condition: service_healthy + #cratedb-mcpo: + # condition: service_healthy open-webui: condition: service_healthy diff --git a/application/open-webui/init/.env b/application/open-webui/init/.env new file mode 100644 index 00000000..a6977fcc --- /dev/null +++ b/application/open-webui/init/.env @@ -0,0 +1,9 @@ +# ------------------------------------------ +# System configuration +# ------------------------------------------ + +# If you run `setup.sh` from within the Docker Compose +# environment, you will not need to change those settings. + +CRATEDB_URL=http://cratedb:4200 +OPEN_WEBUI_URL=http://open-webui:8080 diff --git a/application/open-webui/setup.Dockerfile b/application/open-webui/init/Dockerfile similarity index 100% rename from application/open-webui/setup.Dockerfile rename to application/open-webui/init/Dockerfile diff --git a/application/open-webui/init.sql b/application/open-webui/init/init.sql similarity index 100% rename from application/open-webui/init.sql rename to application/open-webui/init/init.sql diff --git a/application/open-webui/setup.sh b/application/open-webui/init/setup.sh similarity index 64% rename from application/open-webui/setup.sh rename to application/open-webui/init/setup.sh index a21ded67..fb3eb4f7 100755 --- a/application/open-webui/setup.sh +++ b/application/open-webui/init/setup.sh @@ -8,11 +8,22 @@ set -euo pipefail # Uncomment for local debugging, but **never** in automated runs. # set -x -# Load variables. +# Load configuration. source .env +# ------- +# CrateDB +# ------- + +# Provision database. +crash --hosts ${CRATEDB_URL} < init.sql + +# ---------- +# Open WebUI +# ---------- + # Sign in to receive JWT token. -token=$( http --ignore-stdin POST ${OPEN_WEBUI_URL}/api/v1/auths/signin email= password= | jq -r .token // empty ) +token=$( http --ignore-stdin POST ${OPEN_WEBUI_URL}/api/v1/auths/signin email= password= | jq -r .token ) if [[ -z "${token}" ]]; then echo "FATAL: Could not obtain JWT token from Open WebUI" >&2 exit 1 @@ -21,23 +32,21 @@ fi # Inquire health. http --ignore-stdin ${OPEN_WEBUI_URL}/health -# Inquire available models. -#http ${OPEN_WEBUI_URL}/api/models Authorization:"Bearer $token" - # List available tools. http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/tools/ Authorization:"Bearer $token" # Configure system prompt. -http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/users/user/settings/update Authorization:"Bearer $token" ui[system]="$( cratedb-mcp show-prompt )" ui[notificationEnabled]="true" +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/users/user/settings/update Authorization:"Bearer $token" \ + ui[system]="$( cratedb-mcp show-prompt )" ui[notificationEnabled]="true" # Configure CrateDB MCPO server. -http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/configs/tool_servers Authorization:"Bearer $token" "@tool-servers.json" +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/configs/tool_servers Authorization:"Bearer $token" \ + "@tool-servers.json" # Configure chat model. -http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/configs/models Authorization:"Bearer $token" DEFAULT_MODELS="gpt-4.1" MODEL_ORDER_LIST="[]" +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/configs/models Authorization:"Bearer $token" \ + DEFAULT_MODELS="gpt-4.1" MODEL_ORDER_LIST="[]" # Configure embedding model. -http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/retrieval/embedding/update Authorization:"Bearer $token" embedding_engine="openai" embedding_model="text-embedding-3-small" - -# Provision database -crash --hosts ${CRATEDB_URL} < init.sql +http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/retrieval/embedding/update Authorization:"Bearer $token" \ + embedding_engine="openai" embedding_model="text-embedding-3-small" diff --git a/application/open-webui/tool-servers.json b/application/open-webui/init/tool-servers.json similarity index 100% rename from application/open-webui/tool-servers.json rename to application/open-webui/init/tool-servers.json From 3c1de231785fdbb052467d7b7a942f14c932595c Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 24 Jul 2025 01:49:31 +0200 Subject: [PATCH 06/11] Open WebUI: Add basic software test and CI/GHA configuration --- .github/dependabot.yml | 10 ++++ .github/workflows/application-open-webui.yml | 56 ++++++++++++++++++++ application/open-webui/README.md | 8 +++ application/open-webui/compose.yml | 23 +++++--- application/open-webui/init/Dockerfile | 2 +- application/open-webui/init/test.sh | 29 ++++++++++ 6 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/application-open-webui.yml create mode 100644 application/open-webui/init/test.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c9e74b9b..e7d01afb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -120,6 +120,16 @@ updates: schedule: interval: "daily" + - directory: "/application/open-webui" + package-ecosystem: "docker-compose" + schedule: + interval: "daily" + + - directory: "/application/open-webui/init" + package-ecosystem: "docker" + schedule: + interval: "daily" + - directory: "/application/ingestr" package-ecosystem: "pip" schedule: diff --git a/.github/workflows/application-open-webui.yml b/.github/workflows/application-open-webui.yml new file mode 100644 index 00000000..19a190c9 --- /dev/null +++ b/.github/workflows/application-open-webui.yml @@ -0,0 +1,56 @@ +name: "Open WebUI" + +on: + pull_request: + paths: + - '.github/workflows/application-open-webui.yml' + - 'application/open-webui/**' + push: + branches: [ main ] + paths: + - '.github/workflows/application-open-webui.yml' + - 'application/open-webui/**' + + # Allow job to be triggered manually. + workflow_dispatch: + + # Run job each night after CrateDB nightly has been published. + #schedule: + # - cron: '0 3 * * *' + +# Cancel in-progress jobs when pushing to the same branch. +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + +jobs: + + test: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: true + matrix: + os: [ "ubuntu-latest" ] + + name: OS ${{ matrix.os }} + + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + + steps: + + - name: Acquire sources + uses: actions/checkout@v4 + + - name: Validate application/open-webui + run: | + # TODO: Generalize invocation into `ngr` test runner. + + # Invoke software stack. + cd application/open-webui + docker compose up --detach + + # Invoke validation payload. + # TODO: Currently does not work on GHA. + # docker compose run --rm test diff --git a/application/open-webui/README.md b/application/open-webui/README.md index 05d72616..9e3eeae0 100644 --- a/application/open-webui/README.md +++ b/application/open-webui/README.md @@ -105,6 +105,14 @@ docker volume rm open-webui_open-webui docker volume rm open-webui_cratedb ``` +### Jobs +Invoke individual jobs defined in the Compose file. +```shell +export BUILDKIT_PROGRESS=plain +docker compose run --rm setup +docker compose run --rm test +``` + ## What's inside - `.env`: The dotenv file defines `OPENAI_API_KEY` for `compose.yml`. diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml index fb77378f..5b78a827 100644 --- a/application/open-webui/compose.yml +++ b/application/open-webui/compose.yml @@ -91,8 +91,8 @@ services: test: [ "CMD", "curl", "--fail", "http://localhost:8080" ] start_period: 3s interval: 10s - timeout: 90s retries: 60 + timeout: 90s #depends_on: # cratedb-mcpo: # condition: service_healthy @@ -114,6 +114,21 @@ services: open-webui: condition: service_healthy + # ---- + # Test + # ---- + test: + build: + context: init + command: bash /app/test.sh + networks: + - llm-demo + depends_on: + setup: + condition: service_completed_successfully + deploy: + replicas: 0 + # ------- # Bundler # ------- @@ -123,11 +138,5 @@ services: start-dependencies: image: docker.io/dadarek/wait-for-dependencies depends_on: - cratedb: - condition: service_healthy - #cratedb-mcpo: - # condition: service_healthy - open-webui: - condition: service_healthy setup: condition: service_completed_successfully diff --git a/application/open-webui/init/Dockerfile b/application/open-webui/init/Dockerfile index 2d0d71f9..22fbea3f 100644 --- a/application/open-webui/init/Dockerfile +++ b/application/open-webui/init/Dockerfile @@ -31,4 +31,4 @@ RUN uv pip install crash cratedb-mcp httpie RUN mkdir /app WORKDIR /app -COPY .env setup.sh *.json *.sql /app/ +COPY .env *.sh *.json *.sql /app/ diff --git a/application/open-webui/init/test.sh b/application/open-webui/init/test.sh new file mode 100644 index 00000000..57124386 --- /dev/null +++ b/application/open-webui/init/test.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# Test Open WebUI configuration via HTTP API. + +# Runtime options. +set -euo pipefail + +# Uncomment for local debugging, but **never** in automated runs. +# set -x + +# Load configuration. +source .env + +# Sign in to receive JWT token. +token=$( http --ignore-stdin POST ${OPEN_WEBUI_URL}/api/v1/auths/signin email= password= | jq -r .token ) +if [[ -z "${token}" ]]; then + echo "FATAL: Could not obtain JWT token from Open WebUI" >&2 + exit 1 +fi + +# Check for a canonical available model to validate that Open WebUI configuration worked. +http ${OPEN_WEBUI_URL}/api/models Authorization:"Bearer $token" refresh==true | \ + grep '"id":"gpt-4.1"' >/dev/null 2>&1 || { + echo "ERROR: Model gpt-4.1 not available" + exit 1 + } + +echo "gpt-4.1 model found" +echo "Ready." From b747ac1cc7fb80480bc58112a6ebfd7deb85cc5f Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sun, 27 Jul 2025 21:20:53 +0200 Subject: [PATCH 07/11] Open WebUI: Update to MCPO v0.0.6 - This adds the new tool `get_table_columns`. - Activate health checks for MCPO container. --- application/open-webui/compose.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml index 5b78a827..94ed8a92 100644 --- a/application/open-webui/compose.yml +++ b/application/open-webui/compose.yml @@ -42,17 +42,17 @@ services: # CrateDB MCPO # ------------ cratedb-mcpo: - image: ghcr.io/crate/cratedb-mcpo:0.0.5 + image: ghcr.io/crate/cratedb-mcpo:0.0.6 environment: CRATEDB_CLUSTER_URL: http://crate:crate@cratedb:4200/ ports: - "5200:8000" networks: - llm-demo - #healthcheck: - # test: [ "CMD", "curl", "--fail", "http://localhost:8000/docs" ] - # start_period: 3s - # interval: 10s + healthcheck: + test: [ "CMD", "curl", "--fail", "http://localhost:8000/docs" ] + start_period: 3s + interval: 10s depends_on: cratedb: condition: service_healthy @@ -93,9 +93,9 @@ services: interval: 10s retries: 60 timeout: 90s - #depends_on: - # cratedb-mcpo: - # condition: service_healthy + depends_on: + cratedb-mcpo: + condition: service_healthy # ----- # Setup @@ -109,8 +109,8 @@ services: depends_on: cratedb: condition: service_healthy - #cratedb-mcpo: - # condition: service_healthy + cratedb-mcpo: + condition: service_healthy open-webui: condition: service_healthy From ad8b384fb15bd6ccd03638c4ff98a20e1f2df81e Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sun, 27 Jul 2025 21:21:52 +0200 Subject: [PATCH 08/11] =?UTF-8?q?Open=20WebUI:=20Configure=20=C2=BBGeneral?= =?UTF-8?q?=20=C2=BB=20Advanced=20Parameters"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use `function_calling=native` and `temperature=0` --- application/open-webui/init/setup.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application/open-webui/init/setup.sh b/application/open-webui/init/setup.sh index fb3eb4f7..0f289677 100755 --- a/application/open-webui/init/setup.sh +++ b/application/open-webui/init/setup.sh @@ -37,7 +37,10 @@ http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/tools/ Authorization:"Bearer $token # Configure system prompt. http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/users/user/settings/update Authorization:"Bearer $token" \ - ui[system]="$( cratedb-mcp show-prompt )" ui[notificationEnabled]="true" + ui[system]="$( cratedb-mcp show-prompt )" \ + ui[params][function_calling]="native" \ + ui[params][temperature]=0 \ + ui[notificationEnabled]="true" # Configure CrateDB MCPO server. http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/configs/tool_servers Authorization:"Bearer $token" \ From 06c91e8fe7df42c357091c393c92e6bb3aed9982 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sun, 27 Jul 2025 22:13:05 +0200 Subject: [PATCH 09/11] Open WebUI: Add Jupyter for code interpretation and execution --- application/open-webui/README.md | 6 +++-- application/open-webui/compose.yml | 37 ++++++++++++++++++++++++++++ application/open-webui/init/setup.sh | 2 +- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/application/open-webui/README.md b/application/open-webui/README.md index 9e3eeae0..8909b6b3 100644 --- a/application/open-webui/README.md +++ b/application/open-webui/README.md @@ -66,6 +66,8 @@ Explore the APIs here. - Open WebUI: - Swagger: http://localhost:6200/docs - OpenAPI: http://localhost:6200/openapi.json +- Jupyter: + - http://localhost:7200/ ### Configure @@ -116,8 +118,8 @@ docker compose run --rm test ## What's inside - `.env`: The dotenv file defines `OPENAI_API_KEY` for `compose.yml`. -- `compose.yml`: The service composition file defines three services: - CrateDB, CrateDB MCPO, and Open WebUI. Helper jobs (setup, test, ...) +- `compose.yml`: The service composition file defines four main services: + CrateDB, CrateDB MCPO, Open WebUI, and Jupyter. Helper jobs (setup, test, ...) excluded for brevity. Use it with Docker or Podman. - `init/`: Runtime configuration snippets. diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml index 94ed8a92..c248792a 100644 --- a/application/open-webui/compose.yml +++ b/application/open-webui/compose.yml @@ -11,6 +11,7 @@ networks: volumes: cratedb: open-webui: + jupyter: services: @@ -81,6 +82,18 @@ services: ENABLE_OPENAI_API: True ENABLE_DIRECT_CONNECTIONS: True ENV: "dev" + # Jupyter code execution and interpreter. + ENABLE_CODE_INTERPRETER: True + CODE_EXECUTION_ENGINE: "jupyter" + CODE_EXECUTION_JUPYTER_URL: "http://jupyter:8888" + CODE_EXECUTION_JUPYTER_AUTH: "token" + CODE_EXECUTION_JUPYTER_AUTH_TOKEN: "123456" + CODE_EXECUTION_JUPYTER_TIMEOUT: 60 + CODE_INTERPRETER_ENGINE: "jupyter" + CODE_INTERPRETER_JUPYTER_URL: "http://jupyter:8888" + CODE_INTERPRETER_JUPYTER_AUTH: "token" + CODE_INTERPRETER_JUPYTER_AUTH_TOKEN: "123456" + CODE_INTERPRETER_JUPYTER_TIMEOUT: 60 ports: - "6200:8080" networks: @@ -96,6 +109,30 @@ services: depends_on: cratedb-mcpo: condition: service_healthy + jupyter: + condition: service_healthy + + # ------- + # Jupyter + # ------- + jupyter: + image: quay.io/jupyter/minimal-notebook:notebook-7.4.4 + # https://docs.openwebui.com/tutorials/jupyter/ + environment: + JUPYTER_ENABLE_LAB: "yes" + JUPYTER_TOKEN: "123456" + ports: + - "7200:8888" + networks: + - llm-demo + volumes: + - jupyter:/home/jovyan/work + healthcheck: + test: [ "CMD", "curl", "--fail", "http://localhost:8888" ] + start_period: 3s + interval: 10s + retries: 60 + timeout: 90s # ----- # Setup diff --git a/application/open-webui/init/setup.sh b/application/open-webui/init/setup.sh index 0f289677..96dacf81 100755 --- a/application/open-webui/init/setup.sh +++ b/application/open-webui/init/setup.sh @@ -39,7 +39,7 @@ http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/tools/ Authorization:"Bearer $token http --ignore-stdin ${OPEN_WEBUI_URL}/api/v1/users/user/settings/update Authorization:"Bearer $token" \ ui[system]="$( cratedb-mcp show-prompt )" \ ui[params][function_calling]="native" \ - ui[params][temperature]=0 \ + ui[params][temperature]:=0.0 \ ui[notificationEnabled]="true" # Configure CrateDB MCPO server. From 8e91e9e78543ea23ac5a730bd01989d8278ad391 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 28 Jul 2025 15:53:15 +0200 Subject: [PATCH 10/11] Open WebUI: Streamline environment variables --- application/open-webui/compose.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml index c248792a..e30cc215 100644 --- a/application/open-webui/compose.yml +++ b/application/open-webui/compose.yml @@ -69,21 +69,21 @@ services: # From caller's environment or `.env` file. OPENAI_API_KEY: ${OPENAI_API_KEY} # Currently defined here. - ENABLE_SIGNUP: False - ENABLE_LOGIN_FORM: False - WEBUI_AUTH: False + ENABLE_SIGNUP: "false" + ENABLE_LOGIN_FORM: "false" + WEBUI_AUTH: "false" DEFAULT_MODELS: "gpt-4.1" DEFAULT_USER_ROLE: "admin" - ENABLE_CHANNELS: True + ENABLE_CHANNELS: "true" RESPONSE_WATERMARK: "This text is AI generated" WEBUI_NAME: "CrateDB LLM Cockpit" - BYPASS_MODEL_ACCESS_CONTROL: True - ENABLE_OLLAMA_API: False - ENABLE_OPENAI_API: True - ENABLE_DIRECT_CONNECTIONS: True + BYPASS_MODEL_ACCESS_CONTROL: "true" + ENABLE_OLLAMA_API: "false" + ENABLE_OPENAI_API: "true" + ENABLE_DIRECT_CONNECTIONS: "true" ENV: "dev" # Jupyter code execution and interpreter. - ENABLE_CODE_INTERPRETER: True + ENABLE_CODE_INTERPRETER: "true" CODE_EXECUTION_ENGINE: "jupyter" CODE_EXECUTION_JUPYTER_URL: "http://jupyter:8888" CODE_EXECUTION_JUPYTER_AUTH: "token" From 357914e6aeba30e4c6b8cfdd7da6fcfc1e3f50c6 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 28 Jul 2025 15:53:33 +0200 Subject: [PATCH 11/11] Open WebUI: Update to CrateDB MCPO 0.0.7 --- application/open-webui/compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/open-webui/compose.yml b/application/open-webui/compose.yml index e30cc215..0d395cc0 100644 --- a/application/open-webui/compose.yml +++ b/application/open-webui/compose.yml @@ -43,7 +43,7 @@ services: # CrateDB MCPO # ------------ cratedb-mcpo: - image: ghcr.io/crate/cratedb-mcpo:0.0.6 + image: ghcr.io/crate/cratedb-mcpo:0.0.7 environment: CRATEDB_CLUSTER_URL: http://crate:crate@cratedb:4200/ ports: