Skip to content

Commit 597bb6c

Browse files
committed
0.3.4: validate --deep, fix stale docker-compose.yaml
validate --deep pings Open WebUI API to verify connectivity, API key validity, and that each KB ID exists. Catches config errors before deployment. Also applies defaults and env var interpolation in validate (was missing). docker-compose.yaml updated from stale watch mode to production daemon mode with healthcheck, LOG_FORMAT, and OIKB_API_KEY.
1 parent fe3a949 commit 597bb6c

7 files changed

Lines changed: 74 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [0.3.4] - 2025-05-21
8+
9+
### Added
10+
11+
- `oikb validate --deep` — verifies Open WebUI connectivity, API key validity, and that each KB ID exists. Catches config errors before deployment.
12+
13+
### Fixed
14+
15+
- `docker-compose.yaml` updated from stale `watch` mode to production `daemon` mode with healthcheck, `LOG_FORMAT=json`, and `OIKB_API_KEY` support.
16+
- `validate` now applies `defaults:` and env var interpolation to entries before checking (was only doing raw yaml).
17+
718
## [0.3.3] - 2025-05-21
819

920
### Added

docker-compose.yaml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,25 @@ services:
66
volumes:
77
- open-webui:/app/backend/data
88

9-
# One-shot sync on container start, then watch for changes
109
oikb:
1110
image: ghcr.io/open-webui/oikb:latest
1211
environment:
1312
- OPEN_WEBUI_URL=http://open-webui:8080
1413
- OPEN_WEBUI_API_KEY=${OPEN_WEBUI_API_KEY}
14+
- OIKB_API_KEY=${OIKB_API_KEY}
15+
- LOG_FORMAT=json
1516
volumes:
16-
- ./docs:/data
17-
command: watch /data --kb ${KB_ID}
17+
- ./.oikb.yaml:/app/.oikb.yaml:ro
18+
command: daemon
19+
ports:
20+
- "8080:8080"
1821
depends_on:
1922
- open-webui
2023
restart: unless-stopped
24+
healthcheck:
25+
test: ["CMD", "curl", "-f", "http://localhost:8080/health/ready"]
26+
interval: 30s
27+
timeout: 5s
2128

2229
volumes:
2330
open-webui:

docs/guide.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ oikb daemon --log-format json JSON logging
645645
oikb daemon --config /path/to/yaml Custom config path
646646
oikb diff <source> --kb-id ID Preview changes
647647
oikb validate Check .oikb.yaml syntax
648+
oikb validate --deep Verify API, auth, and KB existence
648649
oikb ls --kb-id ID List files in a KB
649650
oikb status --kb-id ID Show KB info
650651
oikb history View sync history
@@ -686,7 +687,8 @@ You're running `oikb sync` without arguments and there's no `.oikb.yaml` in the
686687
### Daemon won't start
687688

688689
```bash
689-
oikb validate # Check config syntax first
690+
oikb validate # Check config syntax
691+
oikb validate --deep # Verify API + KB connectivity
690692
```
691693

692694
### How do I find my KB ID?

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "oikb"
3-
version = "0.3.3"
3+
version = "0.3.4"
44
description = "Sync anything to Open WebUI Knowledge Bases"
55
readme = "README.md"
66
authors = [

src/oikb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""oikb — Open WebUI Knowledge Base CLI."""
22

3-
__version__ = "0.3.3"
3+
__version__ = "0.3.4"

src/oikb/cli.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -830,20 +830,44 @@ def init(force: bool):
830830

831831
@cli.command()
832832
@click.option("--config", "config_file", default=None, type=click.Path(), help="Path to .oikb.yaml (default: ./.oikb.yaml).")
833-
def validate(config_file: str | None):
834-
"""Validate .oikb.yaml without running anything."""
833+
@click.option("--deep", is_flag=True, help="Verify connectivity: ping Open WebUI, check API key, confirm each KB exists.")
834+
def validate(config_file: str | None, deep: bool):
835+
"""Validate .oikb.yaml without running anything.
836+
837+
By default, checks YAML structure and source syntax.
838+
With --deep, also verifies the Open WebUI API is reachable,
839+
the API key is valid, and each Knowledge Base ID exists.
840+
"""
835841
if config_file:
836842
import yaml
837843
with open(config_file) as f:
838844
data = yaml.safe_load(f)
845+
data = _interpolate_env(data) if data else data
839846
entries = (data.get("sources") or data.get("sync", [])) if data else []
847+
defaults = data.get("defaults", {}) if data else {}
848+
if defaults and entries:
849+
entries = [_deep_merge(defaults, e) for e in entries]
840850
else:
841851
entries = _load_oikb_yaml()
842852

843853
if not entries:
844854
click.echo(click.style("No .oikb.yaml found or file is empty.", fg="red"), err=True)
845855
sys.exit(1)
846856

857+
# Deep validation: create a client and verify connectivity first.
858+
client = None
859+
if deep:
860+
click.echo(click.style("Deep validation\n", bold=True))
861+
try:
862+
client = _make_client(
863+
url=entries[0].get("url"),
864+
token=entries[0].get("token"),
865+
)
866+
click.echo(click.style(" ✓ API reachable", fg="green") + f" {client._http.base_url}")
867+
except Exception as e:
868+
click.echo(click.style(f" ✗ Cannot reach Open WebUI: {e}", fg="red"))
869+
sys.exit(1)
870+
847871
has_errors = False
848872
for i, entry in enumerate(entries, 1):
849873
entry_name = entry.get("name", f"entry #{i}")
@@ -855,13 +879,32 @@ def validate(config_file: str | None):
855879
has_errors = True
856880
continue
857881

858-
# Try resolving the connector to catch bad source strings.
882+
# Syntax check: resolve the connector.
859883
try:
860884
_resolve_connector(source)
861-
click.echo(click.style(f" ✓ {entry_name}", fg="green") + f" {source}{kb_id}")
862885
except Exception as e:
863886
click.echo(click.style(f" ✗ {entry_name}: {e}", fg="red"))
864887
has_errors = True
888+
continue
889+
890+
# Deep check: verify the KB exists.
891+
if deep and client:
892+
try:
893+
kb = client.get_kb(kb_id)
894+
kb_name = kb.get("name", "?")
895+
file_count = len(kb.get("files", []))
896+
click.echo(
897+
click.style(f" ✓ {entry_name}", fg="green")
898+
+ f" {source}{kb_name} ({file_count} files)"
899+
)
900+
except Exception as e:
901+
click.echo(click.style(f" ✗ {entry_name}: KB {kb_id}{e}", fg="red"))
902+
has_errors = True
903+
else:
904+
click.echo(click.style(f" ✓ {entry_name}", fg="green") + f" {source}{kb_id}")
905+
906+
if client:
907+
client.close()
865908

866909
if has_errors:
867910
sys.exit(1)

src/oikb/daemon.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async def verify_api_key(
4040
app = FastAPI(
4141
title="oikb",
4242
description="Sync engine for Open WebUI Knowledge Bases. Trigger syncs, check status, and query history.",
43-
version="0.3.3",
43+
version="0.3.4",
4444
)
4545

4646
# Runtime state populated by start_daemon().

0 commit comments

Comments
 (0)