309 lines
17 KiB
YAML
309 lines
17 KiB
YAML
# OMP agent model registry — single source of truth (OMP is the primary
|
||
# harness; there is no second copy to keep in sync).
|
||
# Conversion notes from the pi-era import:
|
||
# - apiKey "EMPTY" -> auth: none (keyless local endpoint)
|
||
# - thinkingLevelMap {max: xhigh} -> compat.reasoningEffortMap
|
||
providers:
|
||
# Provider NAMES are labels, and every reference to them lives in config.yml
|
||
# (modelRoles, agentModelOverrides) or in another model's compactionModel —
|
||
# rename one and a missed reference fails at call time. So each label is
|
||
# migrated with its model in the same edit. One provider per HOST:PORT, so
|
||
# Dankclaw's second endpoint (:7771) is a separate block rather than a second
|
||
# model inside the :7770 one.
|
||
"llamacpp (dankserver)":
|
||
baseUrl: http://192.168.0.235:7770/v1
|
||
api: openai-completions
|
||
auth: none
|
||
models:
|
||
- id: frank-archon
|
||
# Model id renamed from frank-medium on 2026-10-08 (official
|
||
# unit-name scheme; the systemd service, scripts and runbooks keep
|
||
# the frank-archon name — service names are unchanged).
|
||
# Server-side compaction routes to compaction.remoteEndpoint in
|
||
# config.yml (192.168.0.143:7771 — frank-zealot's 3090); the
|
||
# compactionModel candidate keeps the wire model id valid there.
|
||
compactionModel: llamacpp (dankclaw-3090)/frank-zealot
|
||
name: "frank-archon"
|
||
# Dankserver: BOTH Radeon AI PRO R9700s (gfx1201, TP=2, wht6 wire) on
|
||
# the digest-pinned radiance image, plain `podman run` under a root
|
||
# unit. Payload, shape, eras, laws, rollback:
|
||
# serverAppConfigs-docs/LLM/frank-archon.md — and
|
||
# scripts/frank-archon.env is the authority for every number there.
|
||
# contextWindow MUST quote the engine's --max-model-len: radiance
|
||
# refuses a longer prompt and never truncates.
|
||
contextWindow: 200000
|
||
# maxTokens must survive the harness compaction gap AND the chars/4
|
||
# token-estimate under-run on code-heavy prompts. The clamp is
|
||
# max_tokens = min(maxTokens, contextWindow − estimate − 4096); the
|
||
# estimate under-runs by ~3 % on code. Compaction fires at 85 % of
|
||
# the window = 170,000 est; worst-case prompt + budget ≈
|
||
# 170,000 × 1.03 + 16,384 ≈ 191,500 ≤ 200,000 — the engine
|
||
# refuses a longer request, never truncates.
|
||
maxTokens: 16384
|
||
reasoning: true
|
||
# Text + image: the official FP8 checkpoint carries the vision
|
||
# tower (vision_config depth 27; the first blocks stay bf16 in
|
||
# modules_to_not_convert) and the plugin declares it
|
||
# (arch/common/rad_block_vit.h). Video preprocessing configs ship
|
||
# too; images are the contract that matters here.
|
||
input: [text, image]
|
||
compat:
|
||
# radiance's --reasoning-format defaults to auto: thinking
|
||
# comes back in `reasoning_content` (config.cpp). No parser
|
||
# flags exist or are needed.
|
||
reasoningContentField: "reasoning_content"
|
||
# The froggeric template does not gate reasoning_effort the way
|
||
# Qwen's shipped template did, and radiance special-cases only
|
||
# none/minimal (core/server/oai.cpp:609). The map stays as the
|
||
# zero-cost safety net, same as frank-zealot's.
|
||
reasoningEffortMap:
|
||
minimal: low
|
||
low: low
|
||
medium: medium
|
||
high: xhigh
|
||
max: xhigh
|
||
# Qwen recommended sampling. extraBody is merged last and wins.
|
||
# Every key is inside radiance's known-parameter list
|
||
# (core/server/oai.cpp:194-205); unknown keys are refused 400.
|
||
extraBody:
|
||
temperature: 1.0
|
||
top_p: 0.95
|
||
top_k: 20
|
||
min_p: 0.0
|
||
presence_penalty: 0.0
|
||
repetition_penalty: 1.0
|
||
chat_template_kwargs:
|
||
enable_thinking: true
|
||
preserve_thinking: true
|
||
|
||
"llamacpp (dankclaw)":
|
||
baseUrl: http://192.168.0.143:7770/v1
|
||
api: openai-completions
|
||
auth: none
|
||
models:
|
||
- id: frank-dragoon
|
||
# Model id renamed from frank-large on 2026-10-08 (official
|
||
# unit-name scheme; the service/unit/scripts keep the frank-dragoon
|
||
# name — service names are unchanged). The pre-rename id is
|
||
# `frank-large`, recorded here because the callsign rename overwrote
|
||
# its own explanation and left "renamed from frank-dragoon".
|
||
name: "frank-dragoon (dankclaw-gpu)"
|
||
# Server-side compaction runs on the compaction.remoteEndpoint in
|
||
# config.yml (192.168.0.143:7771 — frank-zealot's 3090); this steers the
|
||
# compaction model candidate so the wire model id stays valid
|
||
# there even from a frank-dragoon session.
|
||
compactionModel: llamacpp (dankclaw-3090)/frank-zealot
|
||
# halogen flash-server on the Strix Halo iGPU. Payload, shape, eras,
|
||
# laws, rollback: serverAppConfigs-docs/LLM/frank-dragoon.md — and
|
||
# scripts/frank-dragoon.env is the authority for every number there.
|
||
# contextWindow: 200,000 under the native 262,144 engine ceiling
|
||
# (HALOGEN_CTX, 2026-10-21) — the 400-zone law: the window MUST sit
|
||
# ~45k under the ceiling, never at it.
|
||
contextWindow: 200000
|
||
# The clamp is max_tokens = min(maxTokens, contextWindow − est −
|
||
# 4096); the chars/4 est UNDER-counts the rendered prompt by ~3 %
|
||
# on code/agent traffic (measured 2026-10-07: est 152,938 ->
|
||
# server 157,388). Worst-case request = (200,000 − 4,096) × 1.03
|
||
# ≈ 201.8k; the compaction trigger fires at 95 % (config.yml
|
||
# thresholdPercent): 190,000 × 1.03 + 45,056 ≈ 240.8k — both inside
|
||
# 262,144 (margin 60k / 21k). Compaction rides frank-zealot:
|
||
# input ≈ 190,000 − keepRecent(20k) ≈ 175k ≤ its 200,704 slot.
|
||
# Recompute all of it after ANY window or threshold change; tests/
|
||
# frank_dragoon_unit.bats pins this arithmetic at 95 %.
|
||
# HALOGEN_MAX_TOKENS_CAP in frank-dragoon.env must stay >= this.
|
||
maxTokens: 45056
|
||
reasoning: true
|
||
# VISION ON as of 2026-10-06: the v2 vision tower loads beside the
|
||
# checkpoint (HALOGEN_VISION_TOWER; priced at 0.8 GiB in the boot
|
||
# ledger, and re-probed 2026-10-21 — a 512 px solid-red PNG comes back
|
||
# "Red" at 282 prompt tokens). image_url content parts only — the
|
||
# legacy top-level images field is the frank-zealot trap, do not
|
||
# repeat it here.
|
||
input: [text, image]
|
||
compat:
|
||
# flash-server emits thinking as `reasoning_content` — same
|
||
# field as every engine this endpoint has run, no harness
|
||
# change needed.
|
||
reasoningContentField: "reasoning_content"
|
||
# flash-server validates reasoning_effort against its own list and
|
||
# 400s anything else, naming it: "reasoning_effort 'maximum'
|
||
# unsupported; use minimal|low|medium|high|xhigh|max|none" (measured
|
||
# 2026-10-21). Every omp level is already spelled by the engine, so
|
||
# the map is identity — it exists to record that, not to fold. An
|
||
# earlier version rewrote high->xhigh and minimal->low to stop
|
||
# rejections that do not exist, and the rewrite asked the engine for
|
||
# a different thinking budget than the caller named. Levels really do
|
||
# differ here: thinking length on one prompt at max_tokens 2048
|
||
# measured low 766 tokens, xhigh 614, max 1049 with the block closed
|
||
# at 1024 (reasoning_closed_at). The harness passes an unmapped level
|
||
# through verbatim (`compat.reasoningEffortMap?.[effort] ?? effort`),
|
||
# so removing an entry is a pass-through, never a 400.
|
||
#
|
||
# `max` still does not reach the engine, and the map is not why: the
|
||
# harness clamps the requested effort to the model's own effort ladder
|
||
# before the map applies, and its ladder for this model tops out at
|
||
# xhigh — captured on the wire 2026-10-21, `:max` left as
|
||
# `reasoning_effort: "xhigh"` with this identity map in place. The
|
||
# `max` row below is the engine-side half of the contract; it takes
|
||
# effect the moment the harness ladder gains `max`, and nothing here
|
||
# can force it sooner.
|
||
#
|
||
# Thinking OFF needs an explicit mode. The default disable mode sends
|
||
# the LOWEST effort rather than a switch — `:off` left the wire as
|
||
# `reasoning_effort: "minimal"` (captured), and minimal still thinks:
|
||
# a canned 26-token block on every "off" request. `none-effort` makes
|
||
# the harness send `reasoning_effort: "none"`, which this engine
|
||
# honours as a real switch: `reasoning_tokens: 0`, answer only, no
|
||
# thinking block in the rendered output (both verified 2026-10-21).
|
||
#
|
||
# Re-measure before changing any of it. The allow-list, in the
|
||
# engine's own words:
|
||
# curl -s :7770/v1/chat/completions -H 'content-type: application/json' \
|
||
# -d '{"model":"frank-dragoon","messages":[{"role":"user","content":"x"}],
|
||
# "reasoning_effort":"maximum"}'
|
||
# -> 400 "reasoning_effort 'maximum' unsupported; use
|
||
# minimal|low|medium|high|xhigh|max|none"
|
||
# and read usage.completion_tokens_details.reasoning_tokens at
|
||
# max_tokens 2048 to see the levels separate. The wire itself is
|
||
# settled by pointing a scratch HOME at a logging proxy on :7799 that
|
||
# forwards to :7770 and running `omp --model …:off` — config claims
|
||
# are worth exactly what their last capture says.
|
||
reasoningDisableMode: none-effort
|
||
reasoningEffortMap:
|
||
minimal: minimal
|
||
low: low
|
||
medium: medium
|
||
high: high
|
||
xhigh: xhigh
|
||
max: max
|
||
# The card's thinking recipe, on the wire. The flash-server engine
|
||
# HAS a server-side sampler (HALOGEN_TEMPERATURE/TOP_P/TOP_K =
|
||
# 1.0 / 0.95 / 20 in frank-dragoon.env), but an omitted parameter is
|
||
# the engine's choice, not a pinned one: top_k is sent explicitly so
|
||
# the recipe survives someone editing HALOGEN_TOP_K. The engine
|
||
# accepts top_k, min_p, presence_penalty and repetition_penalty
|
||
# (measured 2026-10-21, all 200) — only the values this endpoint is
|
||
# meant to run are pinned here.
|
||
extraBody:
|
||
temperature: 1.0
|
||
top_p: 0.95
|
||
top_k: 20
|
||
|
||
"llamacpp (dankclaw-3090)":
|
||
baseUrl: http://192.168.0.143:7771/v1
|
||
api: openai-completions
|
||
auth: none
|
||
models:
|
||
- id: frank-zealot
|
||
# Model id renamed from frank-small on 2026-10-08 (official
|
||
# unit-name scheme; the service/unit/scripts keep the
|
||
# frank-zealot name — service names are unchanged).
|
||
name: "frank-zealot (dankclaw-3090)"
|
||
# Server-side compaction routes to compaction.remoteEndpoint in
|
||
# config.yml (192.168.0.143:7771 — this endpoint); the
|
||
# compactionModel candidate keeps the wire model id valid there.
|
||
compactionModel: llamacpp (dankclaw-3090)/frank-zealot
|
||
# Dankclaw's RTX 3090 llama.cpp endpoint (PrismML fork build — the only
|
||
# loader for this payload's custom tensor type). Shape, payload, eras,
|
||
# laws, rollback: serverAppConfigs-docs/LLM/frank-zealot.md, and
|
||
# scripts/frank-zealot.env is the authority for every number there.
|
||
# contextWindow is the PER-SLOT window: llama.cpp divides --ctx-size
|
||
# across slots and answers a longer prompt with a 400, never a
|
||
# truncation, so this value must follow every reslice (live now:
|
||
# 3 x 200,704 over f16 K + q8_0 V with the MTP drafter armed, ba69ea5).
|
||
contextWindow: 200704
|
||
# Prompt + budget must fit inside the slot window; thinking counts
|
||
# against the budget. Compaction fires at 85 % of the window =
|
||
# 170,598 est; worst-case prompt + budget ≈ 170,598 × 1.03 +
|
||
# 16,384 ≈ 192,100 ≤ 200,704. This endpoint stays the fleet's
|
||
# compaction hub: still the widest window in this file.
|
||
maxTokens: 16384
|
||
reasoning: true
|
||
input: [text, image]
|
||
compat:
|
||
# llama.cpp --reasoning-format deepseek emits thinking as
|
||
# `reasoning_content` — the same field the MiMo/Ornith eras used;
|
||
# harness reads unchanged.
|
||
reasoningContentField: "reasoning_content"
|
||
# Bonsai-2's template DOES read reasoning_effort (the Qwen3.5-9B
|
||
# template did not — the map used to be inert here): it accepts ONLY
|
||
# xhigh (default), medium, low and calls raise_exception on anything
|
||
# else, so an unmapped harness level is a hard HTTP 500 on the role's
|
||
# first request, naming Jinja and not the model. The fold below is
|
||
# therefore load-bearing, identical to the frank-archon/dragoon law.
|
||
# llama.cpp passes chat_template_kwargs through to Jinja.
|
||
reasoningEffortMap:
|
||
minimal: low
|
||
low: low
|
||
medium: medium
|
||
high: xhigh
|
||
max: xhigh
|
||
# The payload's OWN sampling metadata (general.sampling.* in the
|
||
# GGUF): temperature 1.0 / top_p 0.95 / top_k 20 — thinking-mode
|
||
# recipe, the same trio the other Qwen3.8-family endpoints carry.
|
||
# (The old 0.6 was the Qwen3.5-9B publisher's card, not this file.)
|
||
# extraBody is merged last and wins over the harness default.
|
||
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
|
||
# The template DOES read preserve_thinking: when false it drops
|
||
# prior assistants' <think> blocks from history. Keep them —
|
||
# multi-turn tool traffic on this endpoint is the majority.
|
||
preserve_thinking: true
|
||
|
||
# Speech endpoint — the fleet's TTS, NOT a chat provider. `api:
|
||
# openai-speech` is a runner API (catalog kind `tts`), so this row never
|
||
# enters the chat picker, the Ctrl+P cycle, or any chat role: it is
|
||
# reachable only through the `speech` model role and the tts device.
|
||
# frank-tts (host port 7772) is the OpenAI /v1/audio/speech façade that
|
||
# resolves a voice BY NAME; its engine (frank-tts-engine, container-internal
|
||
# 8880, never published) is arm A qwentts.cpp 5916bf7 — measured 0.55-0.67
|
||
# RTF warm and 86-451 ms to the first audio byte on Dankclaw's CPU, with
|
||
# mainline llama-tts (TTS_BACKEND=llama-tts-cli) kept as the rollback.
|
||
# Transport contract, read out of the harness itself: the request body is
|
||
# {model, input, response_format, voice?, speed?}; `voice` is forwarded ONLY
|
||
# when explicitly set, and sampleRate/bitRate are REJECTED outright (the
|
||
# transport throws "openai-speech does not support sampleRate"), so the
|
||
# codec comes from the output_path suffix and the only tunable that matters
|
||
# is the voice name — and it comes from ONE place: the tool's `voice_id`
|
||
# argument. No setting feeds it (`speech.voice` is the speak-aloud Kokoro enum,
|
||
# `tts.localVoice` is the local backend's), so a call that omits voice_id arrives
|
||
# at 7772 with no `voice` key and the endpoint answers 400 "unknown voice None;
|
||
# available voices: narrator1, narrator2, narrator3". Say the voice in the call.
|
||
# WIRED 2026-10-18: `config.yml` `modelRoles.speech` is this row, flipped only
|
||
# after the service was deployed and answered real synthesis (two packs,
|
||
# different bytes, 24 kHz PCM16, RTF 0.57). Why the gate existed: `auth: none`
|
||
# makes this provider read as AVAILABLE even while the container is down, and an
|
||
# explicit speech role has NO fallback candidate, so a dead 7772 fails the device
|
||
# loudly instead of quietly narrating in Kokoro. Reverting is ONE key:
|
||
# `modelRoles.speech: local/kokoro`. `speech.voice` stays a Kokoro name either
|
||
# way — it was never this endpoint's voice.
|
||
# SCHEMA LAW, learned the hard way (2026-10-18): provider-level `api` must be
|
||
# a CHAT transport — the models.yml schema accepts only the chat/judge APIs
|
||
# there, and naming a runner API (`openai-speech`) at provider level fails the
|
||
# file's validation, which DISABLES EVERY custom provider: all three fleet
|
||
# endpoints vanish from the registry, not just this one (`omp models` prints
|
||
# "models.yml validation failed — custom providers disabled" and lists only
|
||
# local/kokoro). The runner api belongs on the MODEL entry only, which is what
|
||
# the block below does — `auth: none` covers the credential requirement, and
|
||
# `contextWindow`/`maxTokens` stay unset because the check only fires when they
|
||
# are provided (the bundled Kokoro row is null on both). `omp models --kind tts`
|
||
# still prints 128K/16K for this row — the registry fills display defaults — and
|
||
# the speech transport never reads them: its whole request body is the five
|
||
# fields named above. Verified 2026-10-18 with `omp models --kind tts` (row
|
||
# present) and `omp models` (frank-tts absent from the chat roster, all three
|
||
# fleet providers still listed).
|
||
"frank-tts (dankclaw)":
|
||
baseUrl: http://192.168.0.143:7772/v1
|
||
auth: none
|
||
models:
|
||
- id: frank-tts
|
||
name: "frank-tts (dankclaw) — cloned voices"
|
||
api: openai-speech
|
||
input: [text]
|