Refs librefang/librefang#4842 — long-term replacement for the substring
match that the OpenAI driver currently uses to decide how to handle
`reasoning_content` on historical assistant turns.
Three provider-specific behaviours that the driver must distinguish at
wire time, now expressed as catalog metadata:
* `strip` — DeepSeek R1 / deepseek-reasoner. The API rejects requests
that carry reasoning_content on previous assistant messages.
* `echo` — DeepSeek V4 Flash. Thinking mode is on by default and the
API rejects multi-turn requests when assistant turns containing
tool_calls don't echo back the original reasoning text. This is the
bug surfaced in librefang/librefang#4842.
* `empty_string` — Moonshot / Kimi K2 family. The field must be present
(empty string) on tool_calls turns, with thinking disabled wire-side
for multi-turn compatibility.
* `none` (default) — most providers; field is omitted entirely.
V4 Pro is intentionally NOT marked `echo` — librefang#4842 reports it
working out-of-the-box; flip when there's an empirical reproducer.
Marks affected models:
providers/deepseek.toml
deepseek-v4-flash → echo
deepseek-reasoner → strip
providers/moonshot.toml
kimi-k2.6, kimi-k2.5, kimi-k2 → empty_string
providers/kimi-coding.toml
kimi-for-coding → empty_string
providers/byteplus-coding.toml
kimi-k2.5 → empty_string
providers/novita.toml
moonshotai/kimi-k2-thinking → empty_string
Tooling:
* schema.toml registers the field with the four enum options and a
`none` default so existing TOML files keep parsing unchanged.
* scripts/validate.py rejects unknown enum values; verified with a
hand-crafted negative case (`reasoning_echo_policy = "bogus"` →
validation fails with the expected message).
* `python3 scripts/validate.py` passes (267 models).
The librefang side that consumes this field will land in a follow-up
PR — until then, registry consumers ignore the field via
`#[serde(default)]` and the existing substring fallback continues to
work, so this commit is safe to ship independently.
Video and music modality models are billed per generation rather than
per token. Without this field the librefang runtime metering layer
records every call as $0 and emits a warning. Sourced from
https://platform.minimax.io/docs/guides/pricing-paygo.
- Hailuo 2.3 Fast: $0.33 per call (1080P/6s upper bound)
- Hailuo 2.3: $0.56 per call (1080P/6s upper bound)
- Hailuo 02: $0.56 per call (1080P/6s upper bound)
- Music 2.6: $0.15 per up-to-5-minute track
- Lyrics gen: $0.01 per song
Also adds per_call_cost to schema.toml so the field is documented
alongside the other cost fields.
Extend modality enum to support video and music, then register the
non-text MiniMax models that were already declared in
media_capabilities but had no concrete entries:
- image-01 ($0.0035/image)
- speech-2.8/2.6 hd & turbo ($60-$100 per 1M chars)
- Hailuo 2.3 Fast / 2.3 / 02 video models ($0.10-$0.56 per video)
- music-2.6, lyrics_generation
Per-call pricing is documented in inline comments since the schema's
token-based cost fields don't naturally fit per-call billing.
schema.toml and scripts/validate.py both updated; the change is
additive (existing modality values remain valid).
Introduces image-generation models as a first-class [[models]] entry via
a new `modality` field on the model schema ("text" default, "image",
"audio"). When modality != "text", context_window / max_output_tokens
are optional since no conventional context gate exists — OpenAI's
gpt-image-2 docs omit them.
Adds `image_input_cost_per_m` / `image_output_cost_per_m` alongside
existing text token cost fields to cover the 4-price structure OpenAI
uses for image generation (text $5/$10, image $8/$30 per 1M tokens).
Validator updated to:
- accept any modality in {text, image, audio}
- require context_window/max_output_tokens only for modality=text
- range-check the two new cost fields
gpt-image-2 entry added to providers/openai.toml with pricing sourced
from https://developers.openai.com/api/docs/pricing. Snapshot
gpt-image-2-2026-04-21 listed as alias.
The provider creation form only collected api_key_env (the env var
name) but not the actual key value. New providers were always created
as "unconfigured" because no key was stored.
Add an optional secret api_key field so the dashboard can pass the
key value during creation. The backend strips it from the TOML and
saves it to secrets.env instead.
* feat: convert schema.toml to machine-parseable format
Replace comment-based documentation format with structured TOML that
can be deserialized into the RegistrySchema Rust type. All 6 content
types (provider, agent, hand, integration, skill, plugin) preserved
with every field, description, enum option, and nested section.
* fix: format options arrays in schema.toml for taplo compliance
* chore: remove router agent
builtin:router has been replaced by LLM intent routing in the kernel.
Assistant is now the sole entry point — see librefang/librefang#1336.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: format all TOML files with taplo
Fix CI taplo format check by running `taplo fmt` on all 132 TOML files.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Community-maintained TOML catalog for LibreFang. New models can be added
via PR without requiring a LibreFang binary release.
Includes validation script, bilingual docs, and GitHub templates.