feat(models): reach the fleet from off-network through the OpenWebUI door
Every provider in agent/models.yml is a LAN address, so a workstation that leaves the house has no model at all. Add `openwebui (franksdank.cloud)`: the already-public OpenWebUI vhost as an OpenAI-compatible provider that proxies the same engines, with frank-dragoon and frank-zealot rows and the WAN row as its own compaction target. LAN rows, modelRoles and the speech runner stay untouched — the door is purely additive. The key is command-backed (`apiKey: "!cat ~/.config/secrets/openwebui_api_key"`, mode 600) instead of inline: a literal sk-... in a tracked file is a key in every agent context on every box that pulls this repo, and the pre-commit shape gate would block it anyway. Rotate = rewrite the file + restart omp, because a `!cmd` value is cached for the process lifetime. Compat blocks copy their LAN twins because the proxy was probed, not assumed (2026-10-21): streaming SSE carries reasoning_content and tool_calls deltas plus final usage, image_url content parts reach both vision towers, and chat_template_kwargs is forwarded — proven on frank-zealot, where enable_thinking false yields zero reasoning and true a real block. frank-archon gets no WAN row: OpenWebUI answers 400 "Model not found" because its picker never published it, so that path is a fleet change. Verified end-to-end from a scratch HOME whose LAN baseUrls point at a dead port, so any answer can only have come through the door (text and a real tool round-trip); `omp models` still lists all four chat providers and the tts runner, so the file validates. bats 137/137, kb-lint clean. Docs: omp-docs/core/wan-model-access.md.
This commit is contained in:
1 parent
8c3dc6bfc9
commit
f3360a28f1
4 files changed
+290
No files matched your search
@@ -306,3 +306,135 @@ providers:
|
||||
name: "frank-tts (dankclaw) — cloned voices"
|
||||
api: openai-speech
|
||||
input: [text]
|
||||
|
||||
# ─── WAN door ───────────────────────────────────────────────────────────
|
||||
# The only block in this file that answers OFF the home network. Every
|
||||
# provider above is a LAN IP, and `modelRoles` (config.yml) still points at
|
||||
# those, so away from home you must NAME one of the rows below:
|
||||
# omp --model 'openwebui (franksdank.cloud)/frank-dragoon:medium'
|
||||
# The door is the OpenWebUI vhost `ai.franksdank.cloud` — Caddy → OWU on
|
||||
# Dankclaw:3005, Anubis-exempt because OWU enforces its own auth, public A
|
||||
# record present (LAN clients resolve 192.168.0.143 via Pi-hole, measured
|
||||
# 2026-10-21). It exposes an OpenAI-compatible surface at /api/v1 and
|
||||
# proxies to the SAME engines as the LAN rows — no second inference stack,
|
||||
# so every engine-side law below still holds through it.
|
||||
# CREDENTIAL LAW: the key is not in this file. `apiKey` accepts a
|
||||
# command-backed value (`!cmd`, resolved by src/config/resolve-config-value.ts:
|
||||
# stdout trimmed, cached for the process lifetime; anything else is tried as
|
||||
# an env-var name, then taken literally), so the secret lives in
|
||||
# ~/.config/secrets/openwebui_api_key, mode 600, like every other credential
|
||||
# on these boxes. A literal `sk-…` here would trip the pre-commit shape gate
|
||||
# and then live in every agent context on every box that pulls this repo.
|
||||
# Rotate: rewrite the secret file, then RESTART omp — the `!cmd` result is
|
||||
# cached per process, so a live session keeps serving the old key.
|
||||
# WHAT THE DOOR SHOWS (probed 2026-10-21 with this key): /api/v1/models
|
||||
# answers frank-dragoon and frank-zealot only. frank-archon is NOT published
|
||||
# in OpenWebUI's picker and the API answers 400 "Model not found" for it, so
|
||||
# the fleet's biggest endpoint has no WAN path until it is published
|
||||
# server-side on Dankclaw — a fleet change, not a harness change.
|
||||
# CONTRACT PROBED THROUGH THE DOOR (2026-10-21): streaming SSE carries
|
||||
# `reasoning_content` deltas and `tool_calls` deltas, finish_reason
|
||||
# `tool_calls`, and a final `usage` under stream_options.include_usage;
|
||||
# `image_url` content parts reach both vision towers (a 64 px red PNG comes
|
||||
# back "Red" from each); and `chat_template_kwargs` IS forwarded — proven on
|
||||
# frank-zealot, where enable_thinking false gives reasoning 0 chars and true
|
||||
# gives a real thinking block on the same prompt. Every compat block below is
|
||||
# therefore a copy of its LAN twin, never a simplification: dropping the
|
||||
# kwargs here would quietly change thinking behaviour on the WAN path.
|
||||
# Chat completions made with an API key create NO OpenWebUI chat row (the
|
||||
# chat list is unchanged after probing), so an off-network session leaves no
|
||||
# trail in the family's chat history.
|
||||
# NOT BEHIND THIS DOOR: speech. OWU does route /api/v1/audio/speech and it
|
||||
# does reach frank-tts:7772, but OWU substitutes its own voice enum first —
|
||||
# a cloned pack name comes back 400 from the upstream. `modelRoles.speech`
|
||||
# stays on `frank-tts (dankclaw)`; do not repoint it here.
|
||||
"openwebui (franksdank.cloud)":
|
||||
baseUrl: https://ai.franksdank.cloud/api/v1
|
||||
api: openai-completions
|
||||
# Quoted because an unquoted leading `!` is a YAML tag, not a command.
|
||||
apiKey: "!cat ~/.config/secrets/openwebui_api_key"
|
||||
models:
|
||||
- id: frank-dragoon
|
||||
# Same halogen flash-server as `llamacpp (dankclaw)` above, one hop
|
||||
# further out; the picker row and the engine share this id, so there
|
||||
# is no alias to keep in sync.
|
||||
name: "frank-dragoon (WAN)"
|
||||
# Off-network the compaction hub must be reachable too: the LAN
|
||||
# frank-zealot row is a dead address from anywhere but the house.
|
||||
# Same engine, same slot arithmetic, so the keepRecent/threshold
|
||||
# math in the LAN rows transfers untouched.
|
||||
compactionModel: openwebui (franksdank.cloud)/frank-zealot
|
||||
# Inherited from the engine, not invented: 200,000 under the native
|
||||
# 262,144 ceiling (the 400-zone law — never sit at the ceiling). The
|
||||
# proxy neither widens the window nor truncates: an over-ceiling
|
||||
# request is still a 400.
|
||||
contextWindow: 200000
|
||||
# Same clamp arithmetic as the LAN row (see frank_dragoon_unit.bats);
|
||||
# HALOGEN_MAX_TOKENS_CAP on the engine is unchanged.
|
||||
maxTokens: 45056
|
||||
reasoning: true
|
||||
input: [text, image]
|
||||
compat:
|
||||
# Verified through the door: reasoning arrives as
|
||||
# `reasoning_content` in the deltas, not folded into content.
|
||||
reasoningContentField: "reasoning_content"
|
||||
# `:off` must be a real switch, and it is: this engine honours
|
||||
# reasoning_effort "none"; the harness default would only send the
|
||||
# lowest effort, which still thinks.
|
||||
reasoningDisableMode: none-effort
|
||||
# Identity map, same reason as the LAN row — flash-server lists
|
||||
# minimal|low|medium|high|xhigh|max|none and 400s anything else.
|
||||
reasoningEffortMap:
|
||||
minimal: minimal
|
||||
low: low
|
||||
medium: medium
|
||||
high: high
|
||||
xhigh: xhigh
|
||||
max: max
|
||||
# Pinned so a server-side HALOGEN_TOP_K edit cannot change this
|
||||
# session's recipe; the engine accepts all three keys.
|
||||
extraBody:
|
||||
temperature: 1.0
|
||||
top_p: 0.95
|
||||
top_k: 20
|
||||
- id: frank-zealot
|
||||
# The 3090 endpoint, same llama.cpp engine behind the proxy. Stays
|
||||
# the widest slot in the file, so it is the WAN compaction target.
|
||||
name: "frank-zealot (WAN)"
|
||||
compactionModel: openwebui (franksdank.cloud)/frank-zealot
|
||||
# PER-SLOT window: llama.cpp divides --ctx-size across slots and
|
||||
# answers a longer prompt with a 400, never a truncation. Follows
|
||||
# every reslice on the engine (3 × 200,704 today).
|
||||
contextWindow: 200704
|
||||
# Prompt + thinking budget inside the slot; compaction fires at 85 %
|
||||
# of the window on this row set (see the LAN twin for the full
|
||||
# worst-case arithmetic).
|
||||
maxTokens: 16384
|
||||
reasoning: true
|
||||
input: [text, image]
|
||||
compat:
|
||||
reasoningContentField: "reasoning_content"
|
||||
# LOAD-BEARING, exactly as on the LAN row: the Bonsai-2 template
|
||||
# accepts ONLY xhigh|medium|low and raises on anything else, so an
|
||||
# unmapped harness level is a hard failure on the role's first
|
||||
# request. Probed 2026-10-21 that the mapped value, not the
|
||||
# harness-level, is what reaches the template through this proxy.
|
||||
reasoningEffortMap:
|
||||
minimal: low
|
||||
low: low
|
||||
medium: medium
|
||||
high: xhigh
|
||||
max: xhigh
|
||||
# The payload's own sampling recipe plus the thinking switches.
|
||||
# chat_template_kwargs survives the proxy (verified), so
|
||||
# preserve_thinking keeps prior assistants' thinking blocks in
|
||||
# history for multi-turn tool traffic — same contract as at home.
|
||||
extraBody:
|
||||
temperature: 1.0
|
||||
top_p: 0.95
|
||||
top_k: 20
|
||||
min_p: 0.0
|
||||
presence_penalty: 0.0
|
||||
chat_template_kwargs:
|
||||
enable_thinking: true
|
||||
preserve_thinking: true
|
||||
Reference in new issue
Block a user