0.3.2: global defaults key, Docker usage docs

defaults: key in .oikb.yaml sets interval, concurrency, filter,
notify, etc. once for all entries — per-entry values override.
Deep merges nested dicts. Added standalone docker run example
and LOG_FORMAT=json to Docker Compose.
This commit is contained in:
Timothy Jaeryang Baek 2026-05-21 13:17:23 +04:00
parent 4e047b3307
commit 878e797b5f
6 changed files with 56 additions and 9 deletions

View file

@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [0.3.2] - 2025-05-21
### Added
- Global `defaults:` key in `.oikb.yaml` — set `interval`, `concurrency`, `filter`, `notify`, or any other key once and have it apply to all entries. Per-entry values override defaults. Deep merges nested dicts (filter, notify).
## [0.3.1] - 2025-05-21
### Added

View file

@ -56,20 +56,25 @@ Features:
```yaml
# .oikb.yaml
defaults:
interval: 1h
concurrency: 4
filter:
max-size: 50mb
notify:
url: https://hooks.slack.com/services/T.../B.../xxx
on: error
sources:
- name: wiki
source: github:owner/repo
kb-id: 8f3a2b1c-...
interval: 1h # simple interval
webhook: true
notify:
url: https://hooks.slack.com/services/T.../B.../xxx
on: error # error (default) | always
- name: handbook
source: confluence:ENG
kb-id: 4e7d9a0f-...
interval: "0 6 * * 1-5" # cron: weekdays at 6am
interval: "0 6 * * 1-5" # overrides default
```
```bash
@ -78,6 +83,19 @@ curl -X POST /sync/wiki # API: trigger by name
curl -X POST /sync/8f3a2b1c-.. # API: trigger by kb-id
```
### Docker
```bash
docker run -d \
-e OPEN_WEBUI_URL=http://open-webui:8080 \
-e OPEN_WEBUI_API_KEY=sk-... \
-e OIKB_API_KEY=your-daemon-key \
-e LOG_FORMAT=json \
-v ./.oikb.yaml:/app/.oikb.yaml:ro \
-p 8080:8080 \
ghcr.io/open-webui/oikb:latest daemon
```
### Docker Compose
```yaml
@ -93,6 +111,7 @@ services:
- OPEN_WEBUI_URL=http://open-webui:8080
- OPEN_WEBUI_API_KEY=${OPEN_WEBUI_API_KEY}
- OIKB_API_KEY=${OIKB_API_KEY}
- LOG_FORMAT=json
volumes:
- ./.oikb.yaml:/app/.oikb.yaml:ro
command: daemon

View file

@ -1,6 +1,6 @@
[project]
name = "oikb"
version = "0.3.1"
version = "0.3.2"
description = "Sync anything to Open WebUI Knowledge Bases"
readme = "README.md"
authors = [

View file

@ -1,3 +1,3 @@
"""oikb — Open WebUI Knowledge Base CLI."""
__version__ = "0.3.1"
__version__ = "0.3.2"

View file

@ -295,6 +295,17 @@ def _interpolate_env(obj: Any) -> Any:
return obj
def _deep_merge(base: dict, override: dict) -> dict:
"""Deep merge two dicts. Values in override take precedence."""
result = base.copy()
for key, val in override.items():
if key in result and isinstance(result[key], dict) and isinstance(val, dict):
result[key] = _deep_merge(result[key], val)
else:
result[key] = val
return result
def _load_oikb_yaml() -> list[dict] | None:
"""Load .oikb.yaml from the current directory if it exists."""
import yaml
@ -314,7 +325,15 @@ def _load_oikb_yaml() -> list[dict] | None:
# Prefer sources: (new), fall back to sync: (legacy).
entries = data.get("sources") or data.get("sync")
return entries or None
if not entries:
return None
# Apply global defaults to each entry.
defaults = data.get("defaults", {})
if defaults:
entries = [_deep_merge(defaults, entry) for entry in entries]
return entries
def _build_cli_filter(max_file_size: str | None):
@ -817,6 +836,9 @@ def daemon(port: int, no_server: bool, config_file: str | None, log_format: str
data = yaml.safe_load(f)
data = _interpolate_env(data) if data else data
entries = (data.get("sources") or data.get("sync", [])) if data else []
defaults = data.get("defaults", {}) if data else {}
if defaults and entries:
entries = [_deep_merge(defaults, e) for e in entries]
else:
entries = _load_oikb_yaml()

View file

@ -40,7 +40,7 @@ async def verify_api_key(
app = FastAPI(
title="oikb",
description="Sync engine for Open WebUI Knowledge Bases. Trigger syncs, check status, and query history.",
version="0.3.1",
version="0.3.2",
)
# Runtime state populated by start_daemon().