# @generated by `harn provider catalog generate`; do not edit directly.
# Edit crates/harn-vm/src/llm/capability_sources/**/*.toml instead.
# --- source: 00-base.toml ---
# Harn provider capability matrix source fragments.
#
# The files under capability_sources/ are the source of truth for Harn's
# built-in provider/model capability rules. `harn provider catalog generate`
# concatenates these fragments into llm/capabilities.toml and every checked-in
# provider catalog projection. The capability TOML is compiled into the VM with
# include_str!.
#
# One `[[provider.<name>]]` array entry per rule; first match wins per
# (provider, model). Place more specific `model_match` patterns before
# wildcards. `version_min = [major, minor]` narrows the match to a model
# ID whose `(major, minor)` version (parsed from the Anthropic / OpenAI
# naming schemes) is greater than or equal to the given tuple. Rules
# whose `version_min` is unparseable for the given model are skipped.
#
# `[provider_family]` declares the sibling providers that inherit rules
# from a canonical family when they have no rule of their own (OpenRouter
# et al. speak the same Responses API and forward `tool_search` /
# `defer_loading` unchanged — they fall through to `[[provider.openai]]`
# by default).
#
# Users override or extend this table per-project via
# `[[capabilities.provider.<name>]]` entries in `harn.toml`. Project
# overrides are checked before the built-in rules for the same provider
# name and are authoritative on overlap.
#
# Supported per-rule fields:
# model_match : glob pattern matched against the lowercased model ID.
# version_min : [major, minor] lower bound, provider-aware parse.
# native_tools : whether the model accepts native tool-call wire shape.
# message_wire_format:
# shared helper wire format: openai, anthropic, gemini, or ollama.
# native_tool_wire_format:
# native tool definition shape: openai or anthropic.
# defer_loading : whether `defer_loading: true` is honored on tool defs.
# tool_search : list of native tool-search variants, preferred first.
# Anthropic = ["bm25", "regex"];
# OpenAI = ["hosted", "client"].
# responses_api : whether Harn has a native provider path for OpenAI
# Responses semantics on this route.
# hosted_tools : provider-hosted tools Harn can pass through without
# local execution.
# remote_mcp : whether provider-hosted remote MCP connectors are
# available.
# conversation_state:
# whether previous_response_id-style provider state is
# available.
# compaction : whether provider-side truncation/compaction controls are
# available.
# background_mode : whether provider-side background jobs are available.
# batch_api : whether this route can be submitted through a
# provider-side asynchronous Batch API for offline,
# non-interactive work such as eval grading, corpus
# refreshes, distillation, or bulk analysis.
# batch_wire_format:
# provider batch request/result family. Known values:
# openai, anthropic_messages, gemini, mistral, fireworks,
# xai.
# batch_input_mode:
# how work is submitted: jsonl_file, inline_requests, or
# jsonl_or_inline.
# batch_discount_percent:
# published discount versus equivalent synchronous API
# traffic, when known.
# batch_turnaround_hours:
# published target or maximum turnaround window, in hours.
# batch_max_requests:
# published maximum request/item count per batch.
# batch_max_input_bytes:
# published maximum submitted request-file/body size in bytes.
# batch_result_retention_days:
# published provider-side result artifact retention window.
# batch_result_ordering:
# result ordering contract: custom_id_rejoin,
# provider_ordered, or unknown.
# batch_partial_failure:
# partial failure semantics: per_request, whole_batch, or
# unknown.
# batch_cancellation:
# cancellation support: supported, not_supported, or unknown.
# batch_security_notes:
# non-secret provider storage/security notes safe to surface
# in catalogs and receipts.
# batch_operational_notes:
# non-secret submit/retry/rejoin constraints safe to surface
# in catalogs and receipts.
# tool_approval_policy:
# approval policy story for provider-executed tools.
# max_tools : cap on tool-definition count the provider will accept.
# Used by harn-lint to warn about oversized registries.
# prompt_caching : whether provider-side prompt caching is available.
# prompt_cache_ttls:
# explicit prompt-cache TTL values Harn can request for
# this route. Known values: 5m, 1h. Empty means the route
# may cache, but Harn has no explicit TTL request knob.
# cache_breakpoint_style:
# explicit cache_control strategy: none, top_level, or last_block.
# vision : whether Harn can send visual input blocks on this route.
# audio_supported : whether Harn can send audio input blocks on this route.
# pdf_supported : whether Harn can send PDF/document input blocks on this route.
# video_supported : whether Harn can send video input blocks on this route.
# files_api_supported:
# whether file_id references from std/files::upload are accepted.
# file_upload_wire_format:
# file-upload API family for std/files.upload: anthropic or gemini.
# structured_output: structured-output transport: native, tool_use, format_kw, none.
# prefers_xml_scaffolding:
# prompt sections should use XML tags (`<task>`, `<examples>`).
# prefers_markdown_scaffolding:
# prompt sections should use Markdown headings (`## Task`).
# structured_output_mode:
# preferred logical output shape: native_json, delimited, xml_tagged, none.
# supports_assistant_prefill:
# whether assistant-role prefill turns are accepted.
# prefers_role_developer:
# whether durable instructions should use `developer` role.
# system_message_placement:
# how an interleaved `system`/`developer` message (an
# operator directive placed mid-conversation, not the
# leading system prompt) is carried on the wire. Known
# values: `inline` (OpenAI Chat/Responses, Ollama — verbatim
# at any position), `native_directive` (Anthropic Opus 4.8
# — a validly-placed `system` message rides natively, else
# folds), and `fold` (Gemini, Bedrock,
# older/other Claude —
# no positional system channel, so the directive folds into
# the adjacent user turn as a `<system-reminder>` block).
# Unset derives from `message_wire_format` (openai/ollama →
# inline, else fold), so only the native Anthropic model
# rows and Bedrock's non-`anthropic` catch-alls set it.
# prefers_xml_tools:
# whether text-rendered tool specs should use XML wrappers.
# thinking_block_style:
# preferred transcript thinking style: none, thinking_blocks,
# reasoning_summary, inline.
# thinking_modes : supported script-facing modes: enabled, adaptive, effort.
# interleaved_thinking_supported:
# whether `thinking` can opt Anthropic Messages API
# requests into the interleaved-thinking beta header.
# anthropic_beta_features:
# unconditional Anthropic beta feature names to request
# for this route.
# vision_supported: whether image content blocks are accepted.
# image_url_input_supported:
# whether image content blocks may reference remote URLs.
# preserve_thinking: whether prior <think> blocks should be carried forward.
# server_parser : server-side response parser that transforms model output.
# honors_chat_template_kwargs:
# whether provider-specific chat-template options are honored.
# chat_template_options_field:
# request field for those options; defaults to
# chat_template_kwargs, Baseten uses chat_template_args.
# requires_completion_tokens: whether to send max_completion_tokens instead of max_tokens.
# reasoning_effort_supported:
# whether the route accepts Harn's provider-neutral
# reasoning effort control. Providers project this to
# their native field (for example OpenAI
# reasoning_effort or Anthropic output_config.effort).
# reasoning_effort_levels:
# accepted effort values when the provider accepts only
# a subset of Harn's neutral enum.
# reasoning_none_supported:
# whether the effort surface accepts "none" as a true
# reasoning-off setting.
# reasoning_tools_require_responses:
# whether enabled reasoning plus function tools must use
# Responses even though reasoning-off Chat Completions works.
# max_thinking_budget:
# max thinkingBudget tokens for high/xhigh/max reasoning
# when the provider takes an explicit token budget
# (native Gemini API thinkingConfig). Differs per model
# (2.5 Flash 24576, 2.5 Pro 32768).
# reasoning_disable_supported:
# whether the route accepts an explicit disabled/off
# reasoning switch.
# reasoning_required_for_tools:
# whether the model calls tools *inside* its reasoning
# channel, so disabling reasoning breaks tool calling
# (the gpt-oss / Harmony quirk — opposite of Qwen3).
# When true, reasoning_policy never resolves the auto
# reasoning level to "off" for tool tasks (agent/code/
# verify); it floors to the lowest supported effort.
# reasoning_text_promotable:
# whether a reasoning-only clean stop may be promoted into
# visible text when the provider omits content. Defaults to
# false; routes must opt in explicitly because most
# reasoning fields are private trace, not committed answer.
# reasoning_wire_format:
# OpenAI-compatible non-standard reasoning transport:
# openrouter, enabled, minimax, or zai.
# recommended_endpoint: preferred endpoint family for this route.
# text_tool_wire_format_supported: whether Harn text tool calls survive.
# preferred_tool_format:
# default tool mode for this route: native or text.
# tool_mode_parity:
# empirical native/text interchangeability status:
# interchangeable, native_unreliable, text_unreliable,
# native_only, text_only, unknown.
# tool_mode_parity_notes:
# short explanation for known non-interchangeable modes.
# thinking_disable_directive:
# in-prompt directive (e.g. "/no_think" for Qwen3 chat
# templates) that disables the model's thinking mode.
# When set, Harn auto-prepends this to the system message
# whenever the resolved `thinking` config is `Disabled`,
# so script authors don't need to know provider-specific
# prompt directives. Idempotent — never injected twice.
# provider_route_denylist:
# [openrouter only] DENYLIST of upstream sub-providers to
# exclude for this route. Materialized into the request
# body's `provider.ignore`. Use when a SPECIFIC upstream is
# positively known to mis-serve a route while others are
# fine (e.g. Ambient billing reasoning tokens then finishing
# with empty tool_calls for qwen3.6). Prefer the allowlist
# (openrouter_provider_order) when the bad upstreams are
# intermittent/hard to enumerate.
# openrouter_provider_order:
# [openrouter only] ALLOWLIST of upstream sub-providers this
# route is PINNED to, in preference order. Materialized into
# `provider.order` + `allow_fallbacks:false`, so OpenRouter
# only ever routes to these known-clean upstreams. Use this
# for routes on OpenRouter's sub-provider lottery where the
# bad upstreams are intermittent (e.g. openai/gpt-oss-*,
# pinned to ["Cerebras","Groq"]). When both a pin and a
# denylist are set the pin wins (a closed allowlist already
# excludes everything else).
# serving_precision:
# serving-quality / precision trust verdict for the route.
# A provider can be live and fast yet serve a model at
# DEGRADED quality (undocumented quantization) or reject
# valid requests, silently contaminating any eval/meter that
# trusts its numbers. This is the data-driven sibling of the
# route-around fields above: instead of routing AROUND a bad
# upstream it LABELS the measured precision so tooling (the
# Burin meter precision canary) can refuse to trust a
# `degraded` route. Known values: `trusted` (full precision,
# reference-verified), `degraded` (proven reduced quality —
# e.g. SambaNova gpt-oss quantized: 0/5 vs OpenRouter 3/3),
# `throttled` (full precision but rate-limited to unusable
# timing — e.g. Cerebras gpt-oss), `unverified` (no verdict;
# same as unset). Defaults to `unverified`.
# requires_tool_result_adjacency:
# whether an assistant message with `tool_calls` must be
# followed immediately by `role=tool` messages for every
# emitted `tool_call_id`; strict OpenAI-compatible servers
# reject interleaved runtime feedback before the result.
# supports_parallel_tool_calls:
# whether one assistant history message may contain multiple
# `tool_calls[]` entries; strict OpenAI-compatible servers
# can reject replayed history with more than one tool call
# even when Harn originally parsed text-tool calls.
# tools_exclude_response_format:
# whether the route rejects `response_format` when native
# `tools` are present. Harn drops response_format for these
# routes only when tools are actually being sent.
#
# OPINIONATED PROVIDER/MODEL/CONFIG POLICY (enforced by the footgun gate in
# crate::llm::capability_audit, wired into `provider catalog generate
# --check` / `make check-provider-catalog`). Harn refuses to ship a matrix
# that declares a known footgun, so harness authors can't reach these states:
#
# * FORBIDDEN: reasoning_required_for_tools = true together with an
# auto_reasoning_overrides that forces a tool task (agent/code/verify) to
# "off". The two are contradictory — a model that calls tools inside its
# reasoning channel emits 0 tool_calls (billed-noncommittal) when reasoning
# is off. (The opposite Qwen quirk — reasoning-OFF-for-tools WITHOUT the
# required-for-tools flag — is legitimate and allowed.)
#
# * FORBIDDEN: an `openrouter` route with reasoning_required_for_tools = true
# (a Harmony-style tool route on the sub-provider lottery) that declares no
# openrouter_provider_order pin. Some OpenRouter upstreams mis-serialize the
# Harmony tool call even with reasoning ON, so such a route MUST pin a
# closed allowlist of known-clean upstreams.
#
# * BLESSED (live-probed 2026-06-13, openai/gpt-oss-120b, reasoning effort
# low): Cerebras and Groq serve Harmony tool calls cleanly (order-pinned
# requests gave 0 billed-noncommittal); Together was flaky (1/3); the
# free OpenRouter lottery fans out across ~17 upstreams. Hence the
# openai/gpt-oss-* OpenRouter row is pinned to ["Cerebras","Groq"].
# --- source: 10-defaults/provider-defaults.toml ---
[provider_defaults.anthropic]
message_wire_format = "anthropic"
native_tool_wire_format = "anthropic"
file_upload_wire_format = "anthropic"
files_api_supported = true
batch_wire_format = "anthropic_messages"
batch_input_mode = "inline_requests"
batch_discount_percent = 50
batch_turnaround_hours = 24
batch_max_requests = 100000
batch_max_input_bytes = 268435456
batch_result_retention_days = 29
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "per_request"
batch_cancellation = "supported"
batch_security_notes = [
"Message Batches are not eligible for Zero Data Retention.",
"Batch data is scoped to the workspace that owns the API key.",
"Request and response data is retained for up to 29 days unless deleted after processing.",
]
batch_operational_notes = [
"Most Message Batches complete within an hour, but callers must still budget for the published 24-hour window.",
"Prompt caching can be used with Message Batches, but cache hits are best effort because asynchronous requests may be processed concurrently and out of order.",
"Use a 1-hour prompt-cache duration for shared batch context when batches may run longer than the 5-minute default cache lifetime.",
"Message Batches do not support stream, speed, Threads state, routing hints, or max_tokens=0 cache prewarm requests.",
]
seed_supported = false
frequency_penalty_supported = false
presence_penalty_supported = false
prompt_cache_ttls = ["5m", "1h"]
[provider_defaults.openai]
batch_wire_format = "openai"
batch_input_mode = "jsonl_file"
batch_discount_percent = 50
batch_turnaround_hours = 24
batch_max_requests = 50000
batch_max_input_bytes = 209715200
batch_result_retention_days = 30
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "per_request"
batch_cancellation = "supported"
batch_security_notes = [
"Batch rate limits use a separate provider pool from synchronous model calls.",
"Output and error files can use a configurable expiration between 1 hour and 30 days.",
]
batch_operational_notes = [
"Input files must group requests for one model; Harn manifests group by provider, model, wire format, and tool format.",
"Use stable custom_id values to rejoin unordered output and error rows.",
]
top_k_supported = false
[provider_defaults.azure_openai]
batch_wire_format = "openai"
batch_input_mode = "jsonl_file"
batch_discount_percent = 50
batch_turnaround_hours = 24
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "per_request"
batch_cancellation = "supported"
batch_security_notes = [
"Global Batch and Data Zone Batch use a separate enqueued-token quota from online workloads.",
"Batch input bodies must name the Azure deployment, not the base model id.",
]
batch_operational_notes = [
"Azure OpenAI documents a 24-hour target turnaround and 50% lower cost than Global Standard, but jobs that run longer do not expire automatically.",
"Output expiration is configurable between 14 and 30 days; Harn does not encode a fixed retention day for Azure.",
"Fine-tuned models, Assistants, and On Your Data are not supported by Azure OpenAI Batch.",
"No fixed request-count cap is documented; capacity is governed by enqueued-token quota.",
]
[provider_defaults.openrouter]
reasoning_wire_format = "openrouter"
top_k_supported = false
[provider_defaults.huggingface]
top_k_supported = false
[provider_defaults.local]
top_k_supported = false
[provider_defaults.together]
batch_wire_format = "openai"
batch_input_mode = "jsonl_file"
batch_discount_percent = 50
batch_turnaround_hours = 24
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "per_request"
batch_cancellation = "unknown"
reasoning_wire_format = "enabled"
[provider_defaults.ollama]
message_wire_format = "ollama"
image_url_input_supported = false
frequency_penalty_supported = false
presence_penalty_supported = false
[provider_defaults.gemini]
message_wire_format = "gemini"
file_upload_wire_format = "gemini"
files_api_supported = true
batch_wire_format = "gemini"
batch_input_mode = "jsonl_or_inline"
batch_discount_percent = 50
batch_turnaround_hours = 24
batch_max_input_bytes = 2147483648
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "per_request"
batch_cancellation = "supported"
batch_operational_notes = [
"Batch creation is not idempotent; persist Harn manifests and receipts before retrying provider create requests.",
"Context caching can apply to batch requests, but Gemini cache and batch discounts do not stack; cache-hit discounts take precedence where documented.",
"Use File API JSONL input for large gauntlets; inline requests have a smaller provider body-size cap.",
]
[provider_defaults.mistral]
batch_wire_format = "mistral"
batch_input_mode = "jsonl_or_inline"
batch_discount_percent = 50
batch_max_requests = 100000
batch_max_input_bytes = 536870912
batch_result_retention_days = 1
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "unknown"
batch_cancellation = "unknown"
batch_operational_notes = [
"Batch jobs support chat completions, embeddings, FIM, moderation, OCR, classifications, conversations, and audio transcription endpoints.",
"Batch jobs use separate accounting from real-time rate limits.",
"The default timeout is 24 hours; results are downloadable for 24 hours after completion.",
]
top_k_supported = false
[provider_defaults.cohere]
top_k_supported = false
[provider_defaults.xai]
batch_wire_format = "xai"
batch_input_mode = "jsonl_or_inline"
batch_turnaround_hours = 24
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "unknown"
batch_cancellation = "unknown"
batch_operational_notes = [
"Most text batch jobs complete within 24 hours, but completion time is best effort and not guaranteed.",
"Batch discounts vary by model; do not assume a global 50% discount for xAI.",
"Official pricing distinguishes text/language batch discounts from image/video paths that may bill at standard rates.",
]
top_k_supported = false
[provider_defaults.groq]
batch_wire_format = "openai"
batch_input_mode = "jsonl_file"
batch_discount_percent = 50
batch_turnaround_hours = 24
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "per_request"
batch_cancellation = "unknown"
[provider_defaults.fireworks]
batch_wire_format = "fireworks"
batch_input_mode = "jsonl_file"
batch_discount_percent = 50
batch_max_input_bytes = 1073741824
batch_turnaround_hours = 24
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "unknown"
batch_cancellation = "unknown"
batch_operational_notes = [
"Batch support is model-specific; Harn only marks catalog rows that advertise provider batch capability.",
"Prompt caching can combine with eligible batch jobs for additional savings.",
"Provider docs currently mention selectable 12/24/48/72 hour expiration windows and also say expired jobs expire after 24h; Harn treats the exact expiration as provider-ambiguous.",
]
[provider_defaults.moonshot]
top_k_supported = false
[provider_defaults.zai]
reasoning_wire_format = "zai"
[provider_defaults.baseten]
top_k_supported = false
top_p_supported = false
[provider_defaults.deepinfra]
top_k_supported = false
[provider_defaults.sambanova]
top_k_supported = false
[provider_defaults.nvidia]
top_k_supported = false
[provider_defaults.nebius]
top_k_supported = false
[provider_defaults.flexai]
top_k_supported = false
[provider_defaults.hyperbolic]
top_k_supported = false
[provider_defaults.siliconflow]
top_k_supported = false
[provider_defaults.parasail]
batch_wire_format = "openai"
batch_input_mode = "jsonl_file"
batch_result_ordering = "custom_id_rejoin"
batch_partial_failure = "unknown"
batch_cancellation = "unknown"
top_k_supported = false
[provider_defaults.atlas]
top_k_supported = false
[provider_defaults.github_models]
top_k_supported = false
[provider_defaults.vercel_ai_gateway]
top_k_supported = false
[provider_defaults.cloudflare_ai_gateway]
top_k_supported = false
[provider_defaults.inception]
top_k_supported = false
[provider_defaults.friendli]
top_k_supported = false
[provider_defaults.volcengine_ark]
top_k_supported = false
[provider_defaults.hunyuan]
top_k_supported = false
[provider_defaults.qianfan]
top_k_supported = false
# --- source: 10-defaults/provider-limits.toml ---
# Adaptive rate/concurrency governor limits (Layer 1 of the LLM rate governor).
#
# `[provider_limits.<provider>]` rows configure the per-(provider, org_key)
# governor in `crate::llm::rate_governor`, consumed only when the
# `llm.rate_governor` flag (`HARN_LLM_RATE_GOVERNOR=1`) is on. Keeping limits in
# the catalog (not at call sites) makes them provider-decoupled and overridable
# by the same catalog/flags delivery path as every other capability fact.
#
# Fields (all optional; unset axes fall back to the conservative built-in
# default in rate_governor.rs):
# max_concurrency ceiling the AIMD limiter climbs toward on sustained success
# min_concurrency floor the AIMD limiter halves toward on a throttle signal
# rpm requests-per-minute token bucket (omit to disable)
# tpm tokens-per-minute token bucket (omit to disable)
# adaptive AIMD on/off; false pins the limit at max_concurrency
# [provider_limits.<p>.backoff] base_ms / max_ms / multiplier / jitter for the
# circuit-breaker OPEN window (exp backoff + full jitter,
# Retry-After always wins)
#
# Providers without an explicit row use the built-in default
# (max_concurrency=8, min=1, adaptive, 1s→60s x2 backoff with jitter).
[provider_limits.anthropic]
max_concurrency = 12
min_concurrency = 1
adaptive = true
[provider_limits.anthropic.backoff]
base_ms = 1000
max_ms = 60000
multiplier = 2.0
jitter = true
# --- source: 20-providers/00-anthropic.toml ---
# ---------- Anthropic (Claude) ------------------------------------------------
# Claude Fable 5 / Mythos 5 — the Mythos-class tier above Opus (launched
# 2026-06-09). Same surface as Opus 4.7+: adaptive thinking (and on
# this family thinking is always on — an explicit `disabled` is a 400, so
# the request builder must omit the field), effort control, interleaved
# thinking, no assistant prefill, sampling params rejected. No version_min:
# every fable/mythos id is Mythos-class. Without these rows the ids would
# fall through to the permissive `claude-*` catch-all at the bottom of this
# file, which wrongly grants `thinking_modes = ["enabled"]` and prefill.
[[provider.anthropic]]
model_match = "claude-fable-*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
reasoning_disable_supported = false
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "claude-mythos-*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
reasoning_disable_supported = false
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-fable-*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
reasoning_disable_supported = false
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-mythos-*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
reasoning_disable_supported = false
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Claude 4.7+ uses adaptive thinking instead of explicit budgets.
[[provider.anthropic]]
model_match = "claude-haiku-*"
version_min = [4, 7]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Opus 4.8 accepts a mid-conversation `system` section. Layer the native
# placement onto the 4.7 capability row below via `extends`; older Opus models
# keep folding the section to a `<system-reminder>` user block.
[[provider.anthropic]]
model_match = "claude-opus-*"
version_min = [4, 8]
extends = true
system_message_placement = "native_directive"
[[provider.anthropic]]
model_match = "claude-opus-*"
version_min = [4, 7]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "claude-sonnet-5*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
reasoning_disable_supported = true
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "claude-sonnet-*"
version_min = [4, 7]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Haiku 4.5+ supports server-side tool search.
[[provider.anthropic]]
model_match = "claude-haiku-*"
version_min = [4, 5]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Opus 4.6+ supports interleaved thinking but rejects assistant prefill.
[[provider.anthropic]]
model_match = "claude-opus-*"
version_min = [4, 6]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "claude-opus-*"
version_min = [4, 0]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Sonnet 4.6 rejects assistant prefill while keeping the 4.x tool surface.
[[provider.anthropic]]
model_match = "claude-sonnet-*"
version_min = [4, 6]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Sonnet 4.0+ supports tool search.
[[provider.anthropic]]
model_match = "claude-sonnet-*"
version_min = [4, 0]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# OpenRouter-style `anthropic/claude-...` prefixes.
[[provider.anthropic]]
model_match = "anthropic/claude-haiku-*"
version_min = [4, 7]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-opus-*"
version_min = [4, 7]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-sonnet-5*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high", "xhigh", "max"]
reasoning_disable_supported = true
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-sonnet-*"
version_min = [4, 7]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["adaptive"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-haiku-*"
version_min = [4, 5]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-opus-*"
version_min = [4, 6]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
interleaved_thinking_supported = true
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-opus-*"
version_min = [4, 0]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-sonnet-*"
version_min = [4, 6]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.anthropic]]
model_match = "anthropic/claude-sonnet-*"
version_min = [4, 0]
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
defer_loading = true
tool_search = ["bm25", "regex"]
max_tools = 10000
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Catch-all for older Claude models — native tools + prompt caching +
# thinking, but no progressive tool disclosure.
[[provider.anthropic]]
model_match = "claude-*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Prefixed-id catch-all mirroring the `claude-*` row above. Hosts sometimes
# hand this route an OpenRouter-style `anthropic/claude-...` id; the versioned
# prefix rows above only match known families with parseable versions, so
# without this row a new family name or unparseable version segment falls
# through to the global text-channel `json` default even though every Claude
# on the direct Anthropic route speaks first-class native tool calling.
[[provider.anthropic]]
model_match = "anthropic/claude-*"
native_tools = true
computer_use_style = "native_anthropic"
screenshot_scaling = "xga"
preferred_tool_format = "native"
prompt_caching = true
vision = true
audio_supported = true
pdf_supported = true
files_api_supported = true
structured_output = "tool_use"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# --- source: 20-providers/10-openai.toml ---
# ---------- OpenAI family -----------------------------------------------------
#
# `provider.openai` rules are inherited by the sibling providers declared
# in `[provider_family]` below (OpenRouter, Together, Groq, DeepSeek,
# Fireworks, HuggingFace, local vLLM/SGLang). Siblings may still add their
# own `[[provider.<name>]]` rules and those win over the openai fallback.
# ChatGPT-era GPT models use Markdown prompt sections and native JSON output.
[[provider.openai]]
model_match = "gpt-4o*"
native_tools = true
preferred_tool_format = "native"
vision = true
audio_supported = true
structured_output = "native"
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openai]]
model_match = "gpt-4.1*"
native_tools = true
preferred_tool_format = "native"
vision_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# OpenAI `*-codex` agentic-coding tunes (gpt-5-codex, gpt-5.1-codex,
# gpt-5.1-codex-max, gpt-5.1-codex-mini, gpt-5.2-codex, gpt-5.3-codex) are
# served ONLY by the Responses API; `/v1/chat/completions` returns HTTP 404
# "Use the v1/responses endpoint instead". Flag them so Harn auto-routes
# through the Responses provider and never silently 404s on the chat lane.
# Placed before the generic `gpt-*` version rows so it wins the first-match.
[[provider.openai]]
model_match = "gpt-*codex*"
native_tools = true
preferred_tool_format = "native"
responses_api = true
chat_completions_unsupported = true
requires_completion_tokens = true
defer_loading = true
tool_search = ["hosted", "client"]
hosted_tools = ["web_search", "web_search_preview", "file_search", "code_interpreter", "image_generation", "computer_use", "mcp", "remote_mcp"]
remote_mcp = true
conversation_state = true
compaction = true
background_mode = true
tool_approval_policy = "provider_or_harn"
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# GPT-5.6 accepts none/low/medium/high/xhigh/max, but not the `minimal`
# effort supported by earlier GPT-5 models. Keep this row ahead of the generic
# version rule so Harn rejects an invalid effort before spending an API call.
[[provider.openai]]
model_match = "gpt-5.6*"
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
defer_loading = true
tool_search = ["hosted", "client"]
responses_api = true
hosted_tools = ["web_search", "web_search_preview", "file_search", "code_interpreter", "image_generation", "computer_use", "mcp", "remote_mcp"]
computer_use_style = "native_openai"
screenshot_scaling = "original"
safety_ack_flow = true
remote_mcp = true
conversation_state = true
compaction = true
background_mode = true
tool_approval_policy = "provider_or_harn"
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
reasoning_effort_levels = ["none", "low", "medium", "high", "xhigh", "max"]
reasoning_tools_require_responses = true
# GPT-5.6 rejects `temperature` even when legacy presets supply a harmless
# deterministic default. Model capabilities strip it before either OpenAI API
# adapter constructs the request.
temperature_supported = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# gpt-5.4+ exposes native `tool_search` on the Responses API. gpt-5.x
# reasoning models reject the legacy `max_tokens` request field on
# `/v1/chat/completions` and require `max_completion_tokens` instead.
[[provider.openai]]
model_match = "gpt-*"
version_min = [5, 4]
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
defer_loading = true
tool_search = ["hosted", "client"]
responses_api = true
hosted_tools = ["web_search", "web_search_preview", "file_search", "code_interpreter", "image_generation", "computer_use", "mcp", "remote_mcp"]
computer_use_style = "native_openai"
screenshot_scaling = "original"
safety_ack_flow = true
remote_mcp = true
conversation_state = true
compaction = true
background_mode = true
tool_approval_policy = "provider_or_harn"
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "gpt-*"
version_min = [5, 1]
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "gpt-*"
version_min = [5, 0]
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Legacy GPT: native tool calls only.
[[provider.openai]]
model_match = "gpt-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# Reasoning family (o1, o3, o4, ...).
[[provider.openai]]
model_match = "o1*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
requires_completion_tokens = true
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "o3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
requires_completion_tokens = true
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "o4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
vision_supported = true
requires_completion_tokens = true
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# OpenRouter-style provider-prefixed IDs.
[[provider.openai]]
model_match = "openai/gpt-4o*"
native_tools = true
preferred_tool_format = "native"
vision = true
audio_supported = true
structured_output = "native"
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openai]]
model_match = "openai/gpt-4.1*"
native_tools = true
preferred_tool_format = "native"
vision_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# OpenRouter-style provider-prefixed codex ids (responses-only, chat 404).
[[provider.openai]]
model_match = "openai/gpt-*codex*"
native_tools = true
preferred_tool_format = "native"
responses_api = true
chat_completions_unsupported = true
requires_completion_tokens = true
defer_loading = true
tool_search = ["hosted", "client"]
hosted_tools = ["web_search", "web_search_preview", "file_search", "code_interpreter", "image_generation", "computer_use", "mcp", "remote_mcp"]
remote_mcp = true
conversation_state = true
compaction = true
background_mode = true
tool_approval_policy = "provider_or_harn"
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# OpenRouter's GPT-5.6 slugs use the OpenAI-compatible Chat transport while
# preserving the model family's exact reasoning and sampling constraints.
# Keep this ahead of the broad gateway GPT rule so `minimal` and temperature
# are rejected locally for both standard and provider-managed `-pro` routes.
[[provider.openai]]
model_match = "openai/gpt-5.6*"
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
reasoning_effort_levels = ["none", "low", "medium", "high", "xhigh", "max"]
temperature_supported = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "openai/gpt-*"
version_min = [5, 4]
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
defer_loading = true
tool_search = ["hosted", "client"]
responses_api = true
hosted_tools = ["web_search", "web_search_preview", "file_search", "code_interpreter", "image_generation", "computer_use", "mcp", "remote_mcp"]
computer_use_style = "native_openai"
screenshot_scaling = "original"
safety_ack_flow = true
remote_mcp = true
conversation_state = true
compaction = true
background_mode = true
tool_approval_policy = "provider_or_harn"
vision_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "openai/gpt-*"
version_min = [5, 1]
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "openai/gpt-*"
version_min = [5, 0]
native_tools = true
preferred_tool_format = "native"
requires_completion_tokens = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "openai/gpt-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openai]]
model_match = "openai/o1*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
requires_completion_tokens = true
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "openai/o3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
requires_completion_tokens = true
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openai]]
model_match = "openai/o4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
vision_supported = true
requires_completion_tokens = true
reasoning_effort_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# --- source: 20-providers/20-local-ollama.toml ---
# ---------- Local / Ollama ----------------------------------------------------
#
# Local providers don't advertise native tool_search or prompt caching.
# Native-tools coverage depends on the specific model. Qwen 3 / 3.5
# packages on Ollama expose native tool calling and thinking on the
# official docs + model pages. Qwen 3.6 adds `preserve_thinking` which
# Alibaba recommends for multi-turn agentic / coding workflows (the
# flagship use case we're building for) — keep it on by default.
[[provider.ollama]]
model_match = "llava*"
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.ollama]]
model_match = "bakllava*"
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
# Caption/vision model with no reasoning capability — match the llava sibling and
# avoid emitting a spurious "## Reasoning" scaffold.
thinking_block_style = "none"
[[provider.ollama]]
model_match = "llama3.2-vision*"
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
# Caption/vision model with no reasoning capability — match the llava sibling.
thinking_block_style = "none"
[[provider.ollama]]
model_match = "llama3.2*"
native_tools = true
preferred_tool_format = "native"
structured_output = "format_kw"
recommended_endpoint = "/api/chat"
text_tool_wire_format_supported = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.ollama]]
model_match = "gemma3*"
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
# Caption/vision model with no reasoning capability — match the llava sibling.
thinking_block_style = "none"
# The Ollama-published Gemma 4 12B quants are TEXT-ONLY (vision projector dropped
# in the conversion — verified via `ollama show gemma4:12b-mlx`), unlike the 26b/
# 31b builds. Keep this ahead of the `gemma4*` family rule (first match wins).
[[provider.ollama]]
model_match = "gemma4:12b*"
vision_supported = false
# Ollama honors the `format` kwarg for JSON/schema output (same as the ollama
# qwen rules). Without the transport field, JSON/schema requests were blocked.
native_tools = false
preferred_tool_format = "text"
structured_output = "format_kw"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
# The Ollama gemma4 quant ships thinking-off (no reasoning trace in this build).
thinking_block_style = "none"
[[provider.ollama]]
model_match = "gemma4*"
vision_supported = true
# Ollama honors the `format` kwarg for JSON/schema output (same as the ollama
# qwen rules). Without the transport field, JSON/schema requests were blocked.
native_tools = false
preferred_tool_format = "text"
structured_output = "format_kw"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
# The Ollama gemma4 quant ships thinking-off (no reasoning trace in this build).
thinking_block_style = "none"
[[provider.ollama]]
model_match = "qwen3.6*"
native_tools = false
# No `preferred_tool_format` pin: this text-channel route inherits the GLOBAL
# default, which is now `json` (fenced-JSON). json is strictly better here than
# heredoc — the json call opener is a ```tool fence, NOT the reserved
# <tool_call> token, so json sidesteps the opener-repetition collapse that
# forced this row onto a non-native text pin (see reserved_tool_call_token
# below), and json swept a clean 1.0/1.0/1.0 emission-fidelity bench for
# local-qwen3.6. To pin heredoc instead, set `preferred_tool_format = "text"`.
# Qwen3.x reserves <tool_call> as a single special token; serving the tagged
# (heredoc) text format without the wire remap re-exposes the opener-repetition
# collapse #2978 fixed — which is exactly why the json default is preferred for
# this route over heredoc text.
reserved_tool_call_token = true
structured_output = "format_kw"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/api/chat"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.ollama]]
model_match = "qwen3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "format_kw"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
server_parser = "ollama_qwen3coder"
honors_chat_template_kwargs = false
recommended_endpoint = "/api/generate-raw"
text_tool_wire_format_supported = false
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# llama.cpp / llama-server speaks OpenAI-compat but is a separate provider
# entry from Ollama. Broader Qwen/Devstral routes remain on the text-tool
# contract until their exact runtime/model/template combinations are probed.
[[provider.llamacpp]]
model_match = "*qwen3.6*"
native_tools = false
# Pinned to `json` (fenced-JSON), the GLOBAL text-channel default — same
# RESOLVED format as the ollama qwen3.6 sibling above. Pinned explicitly rather
# than left to inherit because the `llamacpp-qwen3.6` alias / catalogued routes
# trip the catalog invariant that every catalogued model/alias rule set
# native_tools + preferred_tool_format. json's call opener is a ```tool fence,
# NOT the reserved <tool_call> token, so it sidesteps the opener-repetition
# collapse that forced the old heredoc pin (see reserved_tool_call_token below).
preferred_tool_format = "json"
structured_output = "native"
# A single-tool probe must not promote one quant above its family. The 2026-07-17
# Burin dogfood trace showed constrained native emission omitting required fields
# from a 13-action edit schema, despite the old Q4 one-tool native smoke. Keep
# every llama.cpp Qwen3.6 variant on the measurable JSON text channel until a
# receipted forced-format family sweep proves otherwise.
tool_mode_parity = "text_only"
tool_mode_parity_notes = "2026-07-19 #5162 CUDA receipt: freshly restarted ghcr.io/ggml-org/llama.cpp:server-cuda servers used --jinja, --reasoning-format deepseek, --chat-template-kwargs {\"enable_thinking\":false}, -np 1, -fa on, and q8_0 K/V cache. Six coding-agent fixtures were forced through native and JSON text formats with two replicates per format on Q4_K_XL (native 2/12, text 8/12; -ngl 999, -c 65536), Q5_K_XL (native 0/12, text 8/12; -ngl 999, -c 65536), and Q8_0 base (native 2/12, text 8/12; -ngl 35, -c 16384 because the 36.9 GB model does not fit this 32 GiB GPU). All rows reached high classifier confidence; see .harn-runs/coding-agent-bench/latest/tool_mode_parity_overlay.toml and the per-quant receipts. Native remained unreliable across the family, so keep JSON text tooling; server_parser = none."
# Qwen3.6's tokenizer reserves <tool_call>/</tool_call> as single special
# tokens; reusing them as text-format delimiters collapses the agent loop into
# opener repetition. Remap to a non-special wire form (validated: 5/5 vs 0/5).
# Kept ON alongside native (matching the mlx sibling): the remap guards the
# text-channel fallback whenever a heredoc `text` pin re-selects the tagged
# format; native message.tool_calls never touch the reserved token.
reserved_tool_call_token = true
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.llamacpp]]
model_match = "*qwen3*"
native_tools = false
# Pinned to `json` (fenced-JSON), the GLOBAL text-channel default — same as the
# qwen3.6 rule above (explicit pin keeps the catalog invariant satisfied for any
# catalogued qwen3 route). json's ```tool fence sidesteps the reserved
# <tool_call> token and is delimiter-safe vs heredoc. To pin heredoc, set
# `preferred_tool_format = "text"`.
preferred_tool_format = "json"
structured_output = "native"
# Qwen3 family reserves <tool_call>/</tool_call> as special tokens; see the
# qwen3.6 rule above. Remapped to a non-special wire delimiter. Kept ON for the
# heredoc-pin path; json's ```tool fence already avoids the reserved token.
reserved_tool_call_token = true
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.llamacpp]]
model_match = "*devstral-small-2*"
native_tools = false
# Pinned to `json` (fenced-JSON), the GLOBAL text-channel default (explicit pin
# keeps the catalog invariant satisfied for the catalogued devstral routes).
# devstral has NO reserved-token constraint, so there was never a structural
# reason to stay on heredoc; json is delimiter-safe and root-cause-fixes
# heredoc's `<<EOF` -> `line 0: <<` content leak. To pin heredoc, set
# `preferred_tool_format = "text"`.
preferred_tool_format = "json"
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.ollama]]
model_match = "devstral-small-2*"
native_tools = false
# Pinned to `json` (fenced-JSON), the GLOBAL text-channel default, matching the
# llamacpp devstral sibling above (explicit pin keeps the catalog invariant
# satisfied for the catalogued `devstral-small-2:24b` model + aliases). devstral
# has NO reserved-token constraint, so there was no structural reason to stay on
# heredoc; json is delimiter-safe and fixes heredoc's `<<EOF` content leak. To
# pin heredoc, set `preferred_tool_format = "text"`.
preferred_tool_format = "json"
structured_output = "format_kw"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/api/chat"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# Local vLLM / SGLang — same story. The capability rules here gate
# chat_template_kwargs emission in openai_compat; the actual model has
# to have been launched with the matching --chat-template.
[[provider.local]]
model_match = "*qwen3.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.local]]
model_match = "*qwen3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.local]]
model_match = "gemma-4*"
# vLLM / SGLang serve Gemma 4 over the OpenAI-compatible surface, which exposes
# native function calling + JSON-schema structured output — same as every hosted
# gemma-4 sibling (gemini/openrouter/together). Without these explicit fields the
# route silently degraded to text tools. (DISTINCT from the Ollama-native gemma4
# quant rule, which ships text-only tools.)
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
# Gemma 4 is genuinely multimodal (text+image) on every served surface, so the
# local OpenAI-compat route exposes vision just like the gemini/openrouter/
# together siblings (see 50-gemini-gemma.toml). Without this the emitted
# `capability_tags` drop `vision` for the local route, contradicting the
# structured caps and the model's real multimodality (harn#3585).
vision_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# Apple Silicon MLX server. Burin's local MLX route is the coding-tuned
# Qwen3.6-35B-A3B MoE served via `mlx_lm.server` (text MoE, NOT mlx_vlm) — see
# burin #2717, which retired the never-downloaded dense vision model
# `unsloth/Qwen3.6-27B-UD-MLX-4bit`. OpenAI-compat; honors the same
# `chat_template_kwargs.preserve_thinking` knob as vLLM via the HF chat template.
# These are the SAME weights as the llama.cpp `qwen3.6-35b-a3b` route
# (shared equivalence_group), so the same tokenizer caveat applies:
# `reserved_tool_call_token = true` because Qwen3.6 reserves
# `<tool_call>`/`</tool_call>` as single special tokens — reusing them as
# heredoc-text delimiters collapses the loop into opener repetition. native is
# kept to mirror the probe-validated llama.cpp sibling
# (`qwen3.6-35b-a3b-ud-q4-k-xl`, validated native on 2026-06-05); the MLX
# runtime profile still requires a `tool_probe` before native is trusted, and if
# mlx_lm.server's OpenAI tool_calls come back empty the safe pin is fenced-json
# (`preferred_tool_format = "json"`), matching the generic `*qwen3.6*` routes.
[[provider.mlx]]
model_match = "*qwen3.6*"
native_tools = true
preferred_tool_format = "native"
reserved_tool_call_token = true
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.mlx]]
model_match = "*qwen3*"
native_tools = true
preferred_tool_format = "native"
vision = true
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# --- source: 20-providers/30-qwen-routed.toml ---
# ---------- Qwen3.6 family (routed providers) --------------------------------
#
# DashScope is the authoritative Qwen host; Fireworks and the local
# OpenAI-compatible servers honor Qwen `chat_template_kwargs`.
# OpenRouter forwards thinking through its `reasoning` field instead.
# The sibling fallthrough to `[[provider.openai]]` would match the gpt-*
# rules (which don't apply) so we declare Qwen explicitly here to prevent
# a silent fallthrough to empty capabilities.
# Catalog rows key DashScope models as `dashscope/<wire-id>` (collision
# safety with same-named routes elsewhere), so each rule below needs a
# prefixed twin — capability matching sees the catalog id, not the wire
# id.
[[provider.dashscope]]
model_match = "dashscope/qwen3.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.dashscope]]
model_match = "dashscope/qwen*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.dashscope]]
model_match = "qwen3.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.dashscope]]
model_match = "qwen*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# Fireworks-hosted GPT-OSS (`accounts/fireworks/models/gpt-oss-120b`) bills
# empty native completions with no dispatchable tool calls in eval JSONL, so it
# stays off the provider-native channel. Between the two TEXT-channel grammars
# (`json` fenced-block vs `text` heredoc), an empirical A/B (2026-06-21, real
# Fireworks calls, 3 samples per arm, task = author a Zig file heavy in `\\`
# multiline-string literals) showed `json` corrupts the file body in 3/3 runs
# (doubled `\\` -> `\\\\` multiline prefixes, escaped quotes `\"name\"`, and one
# run leaked literal `\n`/`\"` for the whole file) while `text`/heredoc emitted
# byte-clean Zig in 3/3 because a JSON string arg forces the model to escape
# the backslashes the Zig source already needs, and gpt-oss double-escapes them.
# Tool-call dispatch was 3/3 on BOTH channels (no heredoc-wrapper regression).
# Pin to `text` so escape-free heredoc bodies carry source verbatim.
[[provider.fireworks]]
model_match = "accounts/fireworks/models/gpt-oss-*"
native_tools = false
preferred_tool_format = "text"
text_tool_wire_format_supported = true
supports_parallel_tool_calls = false
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_required_for_tools = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Fireworks serves gpt-oss-120b at FULL precision (verified against the
# OpenRouter full-precision reference during the 2026-06 meter-convergence
# effort, once the harn #3319 reasoning-echo HTTP400 was fixed). Trusted.
serving_precision = "trusted"
[[provider.fireworks]]
model_match = "accounts/fireworks/models/glm-5p*"
native_tools = true
preferred_tool_format = "json"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke after parser fix: forced native/off emitted XML `<tool_use>` text instead of provider-native tool_calls. JSON tools completed the loop when auto reasoning was disabled; text tools did not."
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.fireworks]]
model_match = "accounts/fireworks/models/deepseek-v4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.fireworks]]
model_match = "accounts/fireworks/models/kimi-k2p*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# MiniMax M3 on Fireworks. Credentialed probe (2026-07-18, harn 0.10.23
# binary, direct Fireworks /v1, N=2) upgrades the prior un-probed verdict:
# with the backslash/quote/unicode large-string argument the native channel
# emitted byte-exact on BOTH `tool_choice: auto` and `tool_choice: required`
# (2/2), and the Harn text-tool channel parsed byte-exact 2/2 (finish=stop,
# no reasoning over-run) — native and text are interchangeable here.
[[provider.fireworks]]
model_match = "accounts/fireworks/models/minimax-m3*"
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "interchangeable"
tool_mode_parity_notes = "2026-07-18 credentialed probe (harn 0.10.23, direct Fireworks /v1, N=2): native carried the large backslash/quote/unicode string argument byte-exact on tool_choice auto and required (2/2); the Harn text-tool channel parsed byte-exact 2/2 (finish=stop, no reasoning over-run)."
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
vision = true
vision_supported = true
reasoning_text_promotable = false
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.fireworks]]
model_match = "*qwen3.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# Fireworks's own model IDs squash dots to `p` (`qwen3p6`, `qwen3p5`),
# so we need a second rule to catch their canonical naming.
[[provider.fireworks]]
model_match = "*qwen3p6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.fireworks]]
model_match = "*qwen*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# OpenRouter: forwards Qwen thinking via its `reasoning` field (not
# chat_template_kwargs — transform_request strips that). Preservation
# works transparently since thinking blocks round-trip as assistant
# content; the reasoning enablement lives in `openrouter_reasoning_config`.
#
# `thinking_modes` is intentionally `["enabled"]` only — OpenRouter
# silently drops `reasoning.effort` for Qwen models (effort is only
# honored for OpenAI o-series and Grok per their docs). Declaring
# "effort" support here caused medium-effort defaults to be sent on the
# wire, get ignored, and have Qwen fall back to its *unbounded* internal
# thinking budget — yielding 5+ minute single-turn finalize calls where
# the visible response was a small fraction of the (hidden) 27K-char
# reasoning trace. Routing through `reasoning.max_tokens` via
# ThinkingConfig::Enabled{budget_tokens} (which Qwen DOES honor as
# thinking_budget) gives bounded, predictable behavior instead.
#
# `reasoning_none_supported` enables the policy resolver to materialize
# "off" as `Effort{None}` → wire `{reasoning: {enabled: false}}`, which
# OpenRouter DOES honor for Qwen (empirically: 358ms vs 1300ms+ when
# effort is silently dropped).
#
# `auto_reasoning_overrides.agent = "off"` declares the documented Qwen3
# tool-call regression in the matrix itself: when a script registers
# tools (task = "agent"), force reasoning off regardless of scale. See
# https://github.com/QwenLM/Qwen3.6/issues/89 for the trained-behavior
# analysis and Qwen's own recommendation to disable reasoning when
# tool-calling.
#
# NOTE — two opposite tool-calling reasoning quirks live in this matrix:
# * Qwen3 = reasoning-OFF-for-tools (this row): Qwen narrates its
# tool-call intent inside the reasoning trace and emits zero structured
# `tool_calls`, so the override forces reasoning off for tool tasks.
# * gpt-oss / Harmony = reasoning-ON-for-tools (see the gpt-oss rows in
# 60-together / 70-...-groq / 80-cerebras / 99-openrouter-new-families):
# gpt-oss performs tool calls INSIDE the chain-of-thought channel, so
# disabling reasoning breaks tool calling. Those rows carry
# `reasoning_required_for_tools = true` and must NOT copy this Qwen-style
# `auto_reasoning_overrides = "off"`. Do not conflate the two.
# OpenRouter routes Anthropic models verbatim — same wire shape as
# direct anthropic, just wrapped in the OpenAI-compat chat-completions
# envelope at the OpenRouter edge. The provider_family chain takes
# `openrouter → openai` (because the request transport is
# OpenAI-compatible), so the `[[provider.anthropic]]` rules for
# `anthropic/claude-*` models above never get consulted for OpenRouter
# requests. These explicit rows wire the Anthropic capability matrix
# back in for `openrouter:anthropic/claude-*` so native tools, prompt
# caching, and the right preferred-format defaults all flow through. Do
# not inherit direct-Anthropic `thinking` controls here: OpenRouter's
# Anthropic pass-through rejects those wire options even when the direct
# Anthropic route supports them.
[[provider.openrouter]]
model_match = "anthropic/claude-fable-*"
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "anthropic/claude-mythos-*"
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "anthropic/claude-haiku-*"
version_min = [4, 7]
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "anthropic/claude-haiku-*"
version_min = [4, 5]
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "anthropic/claude-sonnet-*"
version_min = [4, 7]
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "anthropic/claude-sonnet-*"
version_min = [4, 6]
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "anthropic/claude-opus-*"
version_min = [4, 7]
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "anthropic/claude-opus-*"
version_min = [4, 6]
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
# Family-level catch-all: every Claude that OpenRouter serves round-trips
# first-class native tool calls, so an `anthropic/claude-*` id the versioned
# rows above don't match (a new family name, an unparseable version segment,
# a dated slug, or a pre-4.x model) still defaults to `native` instead of
# falling through to the global text-channel `json` default. This mirrors the
# direct-Anthropic `claude-*` catch-all and Bedrock's `*claude*` fallback;
# host alias tables previously compensated with per-alias `tool_format` pins.
[[provider.openrouter]]
model_match = "anthropic/claude-*"
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
cache_breakpoint_style = "top_level"
structured_output = "tool_use"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = true
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.openrouter]]
model_match = "qwen/qwen3.7-plus"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_none_supported = true
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
prompt_caching = true
cache_breakpoint_style = "last_block"
vision_supported = true
vision = true
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "qwen/qwen3.7-max"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_none_supported = true
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
prompt_caching = true
cache_breakpoint_style = "last_block"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "qwen/qwen3.6-plus"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_none_supported = true
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
prompt_caching = true
cache_breakpoint_style = "last_block"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# Keep this exact Plus row aligned with the wildcard Qwen3.6 rule below. The
# exact row matches first, so it must carry the OpenRouter Ambient denylist
# directly rather than relying on wildcard inheritance.
provider_route_denylist = ["Ambient"]
[[provider.openrouter]]
model_match = "qwen/qwen3.6-flash"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_none_supported = true
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
prompt_caching = true
cache_breakpoint_style = "last_block"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# OpenRouter reports input_cache_write pricing for Flash specifically. Keep
# this exact row separate so the broader Qwen3.6 open-weight routes do not
# inherit prompt-caching metadata that OpenRouter does not advertise for them.
provider_route_denylist = ["Ambient"]
[[provider.openrouter]]
model_match = "qwen/qwen3.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_none_supported = true
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# Route around OpenRouter's `Ambient` upstream for this row. With raw SSE
# captures (evidence: /tmp/qwen36_*), Ambient narrates the tool call on the
# `reasoning` channel, emits a tiny `content` preamble, then finishes with
# `finish_reason: "stop"` and EMPTY `tool_calls` — billing ~2800 reasoning
# tokens while the structured call is never serialized to any wire field. The
# `Parasail` / `AtlasCloud` / `AkashML` upstreams serve the IDENTICAL request
# natively (`finish: tool_calls`, valid calls), so native WORKS — only Ambient
# is broken. This denies the broken upstream rather than downgrading the tool
# format (KEEP native_tools / preferred_tool_format = "native" above): the
# proven workaround is the OpenRouter `provider.ignore` body knob (the
# `require_parameters` knob does NOT help — Ambient advertises tools, stays
# selected). Materialized into `provider.ignore` by
# `apply_openrouter_route_denylist`.
provider_route_denylist = ["Ambient"]
[[provider.openrouter]]
model_match = "qwen/qwen3-coder-plus"
native_tools = true
preferred_tool_format = "text"
reserved_tool_call_token = true
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "OpenRouter Qwen3-Coder Flash native tools exhausted the coding-agent fixture while text tools completed; default to Harn text tools for preset parity."
prompt_caching = true
cache_breakpoint_style = "last_block"
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "qwen/qwen3-coder-flash"
native_tools = true
preferred_tool_format = "text"
reserved_tool_call_token = true
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "OpenRouter Qwen3-Coder Flash native tools exhausted the coding-agent fixture while text tools completed; default to Harn text tools for preset parity."
prompt_caching = true
cache_breakpoint_style = "last_block"
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "qwen/qwen3-coder*"
native_tools = true
preferred_tool_format = "text"
# Qwen3-Coder reserves <tool_call> as a special token and emits the wrapper on
# most turns (see tools/parse/bare.rs); remap it on the wire like the other
# Qwen3 routes so the text format doesn't collapse.
reserved_tool_call_token = true
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "OpenRouter Qwen3-Coder Flash native tools exhausted the coding-agent fixture while text tools completed; default to Harn text tools for preset parity."
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "qwen/qwen3-max"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
prompt_caching = true
cache_breakpoint_style = "last_block"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "qwen/qwen-plus"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
prompt_caching = true
cache_breakpoint_style = "last_block"
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "qwen/*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_none_supported = true
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# HuggingFace router + Together pass Qwen chat_template_kwargs through
# unchanged. Match the qwen3.6 model card shape (`Qwen/Qwen3.6-*`).
[[provider.huggingface]]
model_match = "qwen/qwen3.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.huggingface]]
model_match = "qwen/qwen3-coder*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.huggingface]]
model_match = "qwen/*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.together]]
model_match = "Qwen/Qwen3.7-Max"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
prompt_caching = true
requires_streaming = true
server_parser = "none"
honors_chat_template_kwargs = false
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.together]]
model_match = "qwen/qwen3.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
preserve_thinking = true
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.together]]
model_match = "qwen/qwen3-coder*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.together]]
model_match = "qwen/*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
server_parser = "none"
honors_chat_template_kwargs = true
recommended_endpoint = "/v1/chat/completions"
text_tool_wire_format_supported = true
thinking_disable_directive = "/no_think"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# --- source: 20-providers/35-moonshot.toml ---
# ---------- Moonshot AI — direct Kimi K2 (OpenAI-compatible /v1) -------------
# Moonshot's first-party API serves the Kimi K2 family with native tool calls,
# an inline thinking mode, prompt caching, and multimodal input. K2.7 Code
# additionally accepts video. Catalog keys are `moonshot/<wire>` (e.g.
# `moonshot/kimi-k2.6`), so the patterns match the `*kimi*` substring to cover
# both the prefixed catalog key and the bare wire id.
#
# TOOL-CHANNEL CAVEAT (credentialed probe 2026-07-18, harn 0.10.23 binary,
# direct Moonshot /v1, N=2 per case). The whole Kimi family reasons, and that
# interacts with tool calling two ways:
# 1. FORCED tool_choice (`required` or a named function) returns HTTP 400
# "tool_choice '...' is incompatible with thinking enabled". This is the
# likely cause of the "empty completion, end_turn, 0 tokens, $0" agent-loop
# drops reported on Kimi (a 400 bills nothing) — NOT a native-emission
# drop. Keep `allowed_tool_choice_modes` to `["auto", "none"]` for every
# Kimi route; never advertise `required`.
# 2. With `tool_choice: auto` the native channel is reliable — k2.6 and k3
# both carried a backslash/quote/unicode large-string argument byte-exact
# 2/2. The TEXT channel is model-dependent: k3 parsed byte-exact, but k2.6
# burned its whole budget in the reasoning channel (finish_reason=length,
# empty content) and never emitted the `<tool_call>` — so do NOT flip the
# general (k2.5/k2.6) Kimi rule to json/text; native+auto is its reliable
# path.
[[provider.moonshot]]
model_match = "*kimi-k3*"
# K3 credentialed probe (2026-07-18, harn 0.10.23 binary, direct Moonshot
# /v1, N=2): with `tool_choice: auto` the native channel carried the
# echo_marker call's backslash/quote/unicode large-string argument byte-exact
# (2/2), and the Harn text-tool channel emitted a parseable `<tool_call>`
# block byte-exact (2/2) with no reasoning-channel over-run — native and text
# are interchangeable here. FORCED tool_choice is the footgun: K3 always
# reasons (`reasoning_effort: max`), and Moonshot rejects `tool_choice:
# "required"` / a named function with HTTP 400 "tool_choice '...' is
# incompatible with thinking enabled". So `required` is dropped from
# allowed_tool_choice_modes below; only `auto`/`none` are real modes.
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "interchangeable"
tool_mode_parity_notes = "2026-07-18 credentialed probe (harn 0.10.23, direct Moonshot /v1, tool_choice=auto, N=2): native carried the large backslash/quote/unicode string argument byte-exact 2/2, and the Harn text-tool channel parsed byte-exact 2/2 with no reasoning over-run. Forced tool_choice (required/specified) returns HTTP 400 'incompatible with thinking enabled' and is not an available mode."
structured_output = "native"
# K3 always reasons through the OpenAI-compatible `reasoning_effort` field;
# it accepts only the `max` effort below, rather than a generic enabled mode.
thinking_modes = ["effort"]
vision = true
vision_supported = true
prompt_caching = true
# Forced tool_choice (`required` / a named function) returns HTTP 400
# "incompatible with thinking enabled" on K3's mandatory reasoning (measured
# 2026-07-18); only `auto`/`none` are real modes.
allowed_tool_choice_modes = ["auto", "none"]
requires_tool_result_adjacency = true
# K3 always reasons, accepts only `reasoning_effort: max`, requires
# `max_completion_tokens`, and rejects fixed sampling/penalty knobs.
requires_completion_tokens = true
reasoning_effort_supported = true
reasoning_effort_levels = ["max"]
reasoning_none_supported = false
reasoning_disable_supported = false
reasoning_history_wire_field = "reasoning_content"
temperature_supported = false
top_p_supported = false
frequency_penalty_supported = false
presence_penalty_supported = false
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.moonshot]]
model_match = "*kimi-k2.7-code*"
native_tools = true
preferred_tool_format = "json"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke after parser fix: forced native/off emitted no dispatchable tool_calls and claimed the tool was unavailable. Harn JSON tools completed the loop; text tools also pass after fixing text-mode history projection."
structured_output = "native"
thinking_modes = ["enabled"]
vision = true
vision_supported = true
video_supported = true
prompt_caching = true
allowed_tool_choice_modes = ["auto", "none"]
requires_tool_result_adjacency = true
temperature_supported = false
top_p_supported = false
frequency_penalty_supported = false
presence_penalty_supported = false
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.moonshot]]
model_match = "*kimi*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
vision = true
vision_supported = true
prompt_caching = true
requires_tool_result_adjacency = true
allowed_tool_choice_modes = ["auto", "none"]
# Every Kimi model pins these sampling fields to a single server-side value and
# rejects any other with HTTP 400 ("invalid temperature: only 1 is allowed for
# this model" / "invalid top_p: only 0.95 is allowed"); confirmed by live probe
# on kimi-k2.5, k2.6, k2.7-code, and k2.7-code-highspeed (2026-07-14). Gating
# them here strips the fields so the server default (the sole allowed value)
# applies. The more specific k2.7-code rule sets these too; rules are
# first-match, so this general rule is what covers k2.5/k2.6. The non-kimi
# moonshot-v1-* models accept both and are correctly not matched here.
temperature_supported = false
top_p_supported = false
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# --- source: 20-providers/36-deepinfra.toml ---
# ---------- DeepInfra — open-weight OpenAI-compatible host -------------------
# DeepInfra serves open weights (DeepSeek, Qwen, Llama, Kimi, GPT-OSS) on a
# standard OpenAI chat-completions surface with native tool calls. Reasoning
# families expose an inline thinking trace; DeepSeek routes honor prompt
# caching. Catalog keys are `deepinfra/<hf-id>`, so patterns match the
# family substring.
[[provider.deepinfra]]
model_match = "*deepseek*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.deepinfra]]
model_match = "*glm-5*"
native_tools = true
preferred_tool_format = "json"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke after parser fix: forced native/off emitted a malformed native call whose function name was the whole JSON payload; fenced JSON text-channel tools completed the loop."
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.deepinfra]]
model_match = "*qwen3.7*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# probed 2026-06-24 (docs/eval/provider-tool-mode-sweep-2026-06-24.md, N=5,
# forced-format single-tool authoring of a backslash-heavy Zig body): native
# 1/5 fidelity (bills empty completions), json 2/5 (flaky parse), text 5/5
# byte-clean. The provider-native channel cannot carry backslash-heavy code, so
# steer to the escape-free heredoc text channel.
[[provider.deepinfra]]
model_match = "*qwen3.6*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-24 forced-format sweep (N=5): DeepInfra Qwen3.6-35B-A3B native bills empty completions (1/5) and fenced-JSON is flaky (2/5); heredoc text carried a backslash-heavy Zig body byte-clean 5/5."
structured_output = "native"
thinking_modes = ["enabled"]
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.deepinfra]]
model_match = "*kimi-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
vision = true
vision_supported = true
video_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# DeepInfra-hosted GPT-OSS is the same Harmony model as cerebras/fireworks
# gpt-oss-120b. Placed BEFORE the catch-all `*` rule (first match wins) so it
# does not fall through to the non-reasoning default — without it
# `reasoning_required_for_tools` resolves OFF and the eval loop bills a
# noncommittal because gpt-oss calls tools INSIDE the chain-of-thought channel.
# Reasoning-effort thinking {low, medium, high}, `reasoning_required_for_tools =
# true`. Must NOT carry a Qwen-style `auto_reasoning_overrides = "off"`.
#
# TOOL CHANNEL — PINNED TO TEXT (matches the Fireworks #3505 gpt-oss precedent).
# DeepInfra's gpt-oss native channel is a Harmony footgun: a 2026-06-24 Harn
# agent-loop run (gpt-oss-120b, zig-feat, tool grounding present) saw DeepInfra
# native bill a non-empty completion (completion_tokens=86) with NO dispatchable
# tool call and NO answer — the action was serialized only in the private
# reasoning/commentary channel of the Harmony format, never emitted as a
# provider-native `tool_calls` entry. Repeated ~10x -> the run was unusable. This
# is the same class of defect reported across the open-source engines DeepInfra
# runs: vLLM #22578 (chat-completions tool_calls empty / missing arguments),
# vLLM #44216 (tool_choice="required" ignored, empty tool_calls + finish_reason
# stop), SGLang #8976 (commentary-channel crash) and #10738 (tool parser not
# working), openai/harmony #68 (tool intent stuck in commentary, never
# dispatched), and HuggingFace openai/gpt-oss-20b discussion #80 (function in
# reasoning_content with no tool_call structure). The clean+reliable channel on
# pay-per-token gpt-oss routes is TEXT (heredoc). `tool_mode_parity =
# "native_unreliable"` makes a `native` pin auto-correct to `text` via
# `validate_tool_format` with an explanatory `correction`, so no alias pin or
# `--tool-format native` can silently re-introduce the empty tool stream.
[[provider.deepinfra]]
model_match = "*gpt-oss*"
native_tools = false
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-24 Harn agent-loop (gpt-oss-120b, zig-feat, tool grounding present): DeepInfra native billed completion_tokens=86 with no dispatchable tool call or answer (Harmony reasoning-channel-only / upstream contract violation), repeated ~10x -> run unusable. Text/heredoc is the clean pay-per-token channel. See vLLM #22578/#44216, SGLang #8976/#10738, openai/harmony #68."
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_required_for_tools = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.deepinfra]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/37-sambanova.toml ---
# ---------- SambaNova Cloud — fast RDU OpenAI-compatible inference -----------
# SambaNova serves large open weights (DeepSeek, Llama 4, Qwen) at high
# throughput on an OpenAI chat-completions surface with native tool calls.
# DeepSeek reasoning routes expose an inline thinking trace; Llama 4 routes
# are non-thinking multimodal. Catalog keys are `sambanova/<wire>`.
[[provider.sambanova]]
model_match = "*deepseek-v3.2*"
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "interchangeable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke: SambaNova DeepSeek-V3.2 completed both native and JSON tool loops with reasoning disabled. Earlier native false negatives were caused by Harn's parser treating terse final answers as billed no-ops; keep native preferred."
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.sambanova]]
model_match = "*deepseek*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.sambanova]]
model_match = "*llama*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# probed 2026-06-24 (docs/eval/provider-tool-mode-sweep-2026-06-24.md, N=5,
# forced-format single-tool authoring of a backslash-heavy Zig body): native
# 0/5 fidelity AND json 0/5 fidelity (both mangle backslashes — the `\\` Zig
# multiline prefix collapses to `\`), while heredoc text was 5/5 byte-clean. The
# escape-free text channel is the only reliable carrier for code on this route.
[[provider.sambanova]]
model_match = "*minimax*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-24 forced-format sweep (N=5): SambaNova MiniMax-M2.7 corrupted a backslash-heavy body on BOTH native (0/5) and fenced-JSON (0/5); heredoc text carried it byte-clean 5/5."
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.sambanova]]
model_match = "*gemma-4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# SambaNova-hosted GPT-OSS is the same Harmony model as cerebras/fireworks
# gpt-oss-120b. Placed BEFORE the catch-all `*` rule (first match wins) so it
# does not fall through to the non-reasoning default — without it
# `reasoning_required_for_tools` resolves OFF and the eval loop bills a
# noncommittal because gpt-oss calls tools INSIDE the chain-of-thought channel.
# Reasoning-effort thinking {low, medium, high}, `reasoning_required_for_tools =
# true`. Must NOT carry a Qwen-style `auto_reasoning_overrides = "off"`.
#
# TOOL CHANNEL — PINNED TO TEXT (matches the Fireworks #3505 gpt-oss precedent).
# SambaNova's gpt-oss native channel is a Harmony footgun: a 2026-06-24 Harn
# agent-loop run (gpt-oss-120b, zig-feat, tool grounding present) ended with a
# provider/tool-protocol failure on the native channel — the same empty
# `tool_calls` / reasoning-channel-only class reported across the open-source
# engines (vLLM #22578, vLLM #44216, SGLang #8976/#10738, openai/harmony #68,
# HuggingFace openai/gpt-oss-20b discussion #80). The clean+reliable channel on
# pay-per-token gpt-oss routes is TEXT (heredoc). `tool_mode_parity =
# "native_unreliable"` makes a `native` pin auto-correct to `text` via
# `validate_tool_format` with an explanatory `correction`, so no alias pin or
# `--tool-format native` can silently re-introduce the protocol failure.
# NOTE: this route is ALSO `serving_precision = "degraded"` (below) — even with
# the text-channel pin it must not back a meter baseline.
[[provider.sambanova]]
model_match = "*gpt-oss*"
native_tools = false
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-24 Harn agent-loop (gpt-oss-120b, zig-feat, tool grounding present): SambaNova native ended with a provider/tool-protocol failure (Harmony empty tool_calls / reasoning-channel-only class). Text/heredoc is the clean pay-per-token channel. See vLLM #22578/#44216, SGLang #8976/#10738, openai/harmony #68."
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_required_for_tools = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# SambaNova serves gpt-oss at DEGRADED quality (quantized). PROVEN during the
# 2026-06 meter-convergence effort: c-feat 0/5 and cpp-storage 0/5 on SambaNova
# vs 3/3 and 1/1 on the OpenRouter full-precision reference. The numbers looked
# like a capability collapse but were provider contamination. The precision
# canary MUST refuse to ledger a meter baseline on this route.
serving_precision = "degraded"
[[provider.sambanova]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/38-nvidia.toml ---
# ---------- NVIDIA NIM — OpenAI-compatible API Catalog -----------------------
# NVIDIA NIM exposes these routes on /v1/chat/completions. Keep family
# semantics explicit because the provider mixes Nemotron, DeepSeek, MiniMax,
# Kimi, Mistral, GPT-OSS, GLM, and Step models behind one OpenAI-compatible
# endpoint.
[[provider.nvidia]]
model_match = "*gpt-oss-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_required_for_tools = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.nvidia]]
model_match = "*nemotron-3-nano-omni*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
vision = true
vision_supported = true
video = true
audio = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*nemotron-3-nano-30b-a3b*"
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "interchangeable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke: NVIDIA NIM Nemotron 3 Nano completed both native and JSON tool loops with reasoning disabled. Earlier native false negatives were caused by Harn's parser treating terse final answers as billed no-ops; keep native preferred."
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*nemotron-3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*deepseek-v4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*minimax-m3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "delimited"
thinking_modes = ["adaptive"]
vision = true
vision_supported = true
video = true
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*minimax-m2.7*"
native_tools = true
preferred_tool_format = "native"
structured_output = "delimited"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*kimi-k2.6*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*glm-5*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "GLM-5.x's native channel emits `<tool_call><arg_key>...` markup as content instead of OpenAI message.tool_calls — a weight-intrinsic behavior reproduced across every GLM-5 host probed (zai/Baseten live, Together + OpenRouter agent-loop smoke, DeepInfra, Fireworks glm-5p*). NVIDIA serves the same weights, so the same leak applies; pin the clean heredoc text channel for family parity. Flagged by the native_unreliable-family consistency gate."
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*step-3.7-flash*"
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "interchangeable"
prompt_caching = true
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_disable_supported = false
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*mistral*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.nvidia]]
model_match = "*gemma-4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.nvidia]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/39-baseten.toml ---
# ---------- Baseten Model APIs -----------------------------------------------
#
# Baseten serves current open-weight frontier models through an
# OpenAI-compatible /v1/chat/completions surface. The endpoint advertises
# tools/JSON/structured-output broadly, but live Harn probes on 2026-06-23
# found GLM-5.2 returns visible `<tool_call><arg_key>...` content instead of
# OpenAI `message.tool_calls` on the native channel. Harn's text-tool grammar
# did produce parseable calls, so pin GLM to text while keeping native tools for
# the Kimi, DeepSeek, GPT-OSS, and Nemotron routes that returned clean native
# calls in the same probe set.
[[provider.baseten]]
model_match = "*glm-5*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-23 live Baseten probe: forced native/off emitted visible `<tool_call><arg_key>...` content with no provider-native tool_calls; Harn heredoc text tools emitted parseable `<tool_call>\\nadd({ ... })\\n</tool_call>`."
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
prompt_caching = true
honors_chat_template_kwargs = true
chat_template_options_field = "chat_template_args"
reasoning_text_promotable = false
top_p_supported = true
top_k_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.baseten]]
model_match = "*kimi-k2*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
honors_chat_template_kwargs = true
chat_template_options_field = "chat_template_args"
vision = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.baseten]]
model_match = "*deepseek-v4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.baseten]]
model_match = "*gpt-oss*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_required_for_tools = true
prompt_caching = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.baseten]]
model_match = "*nemotron*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
honors_chat_template_kwargs = true
chat_template_options_field = "chat_template_args"
top_p_supported = true
top_k_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.baseten]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/40-deepseek-reasoning.toml ---
# ---------- DeepSeek reasoning (across host providers) -----------------------
#
# DeepSeek V3.1+ exposes reasoning via `chat_template_kwargs.enable_thinking`
# when routed through vLLM-style hosts (HuggingFace router, Together). On
# OpenRouter it surfaces as the `reasoning` field like other models on
# that platform. Prompt caching is DeepSeek-native (automatic, billed as
# cache_read tokens) so declare it wherever the host forwards the usage.
[[provider.together]]
model_match = "deepseek-ai/deepseek-v3*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled"]
prompt_caching = true
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.huggingface]]
model_match = "deepseek-ai/deepseek-v3*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled"]
prompt_caching = true
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "deepseek/deepseek-v4*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "effort"]
prompt_caching = true
structured_output = "native"
top_k_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "deepseek/deepseek-v3.2*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "OpenRouter DeepSeek V3.2 advertises native tools, but coding-agent runs observed provider-native failures; default to Harn text tools and recover DSML markers."
thinking_modes = ["enabled", "effort"]
prompt_caching = true
cache_breakpoint_style = "last_block"
structured_output = "native"
top_k_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "deepseek/deepseek-v3*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "effort"]
prompt_caching = true
structured_output = "native"
top_k_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "deepseek/deepseek-chat-v3.1*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "effort"]
prompt_caching = true
structured_output = "native"
top_k_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "deepseek/deepseek-chat*"
native_tools = true
preferred_tool_format = "native"
prompt_caching = true
structured_output = "native"
top_k_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "deepseek/deepseek-r1-0528*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "effort"]
prompt_caching = true
structured_output = "native"
top_k_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "deepseek/deepseek-r1"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "effort"]
structured_output = "native"
top_k_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "mistralai/mistral-medium-3-5*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
vision_supported = true
vision = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "mistralai/mistral*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "mistralai/devstral*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# probed 2026-06-24 (docs/eval/provider-tool-mode-sweep-2026-06-24.md, N=5,
# forced-format single-tool authoring of a backslash-heavy Zig body): native
# dispatched 5/5 but only 1/5 byte-clean (double-escaped the body), and the
# fenced-JSON channel produced 0/5 parseable Harn calls (the model emits
# provider-native tool_calls, not the ```tool {name,args} contract). Heredoc
# text was 5/5 byte-clean. Steer to text so backslash-heavy code round-trips.
# NOTE: Kimi-K2.7-Code on DeepInfra and Baseten native IS byte-clean (5/5) — the
# defect is OpenRouter-route-specific, hence this explicit row stays on text
# while the broader moonshotai/kimi-k2* fallthrough keeps native.
[[provider.openrouter]]
model_match = "moonshotai/kimi-k2.7-code"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-24 forced-format sweep (N=5): OpenRouter Kimi-K2.7-Code native dispatched 5/5 but double-escaped backslash bodies (1/5 fidelity); fenced-JSON emitted no parseable Harn call (0/5). Heredoc text carried the body byte-clean 5/5."
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
vision_supported = true
video_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
temperature_supported = false
top_p_supported = false
frequency_penalty_supported = false
presence_penalty_supported = false
allowed_tool_choice_modes = ["auto", "none"]
[[provider.openrouter]]
model_match = "moonshotai/kimi-k2*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
vision_supported = true
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# --- source: 20-providers/40-openai-compatible-gap-providers.toml ---
# OpenAI-compatible providers whose per-model catalogs are refreshed live when
# callers provide API keys. These wildcard rules are conservative transport
# defaults: model-family overrides can still be added as empirical probes find
# provider-specific reasoning/tool quirks.
[[provider.nebius]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Nebius documents function calling and structured JSON output; Harn has not yet run model-family probes for every Token Factory route."
[[provider.flexai]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "FlexAI exposes model-level support flags through discovery; use live refresh/probes to tighten route-specific tool preferences."
[[provider.hyperbolic]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Hyperbolic documents tool calling for selected Qwen/Llama routes; model-specific probes should override this wildcard if needed."
[[provider.siliconflow]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "SiliconFlow is OpenAI-compatible, but thinking controls are provider-specific on some model families; prefer live model probes before pinning reasoning-heavy agents."
[[provider.parasail]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Parasail exposes an OpenAI-compatible serverless/batch API; use model-specific probes before relying on native tools for long agent loops."
[[provider.atlas]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Atlas is a coding-plan OpenAI-compatible route; Harn has no route-specific tool parity probes yet."
[[provider.github_models]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "GitHub Models publishes capability flags per catalog model; Harn needs route-specific probes before assuming native/text tool parity."
[[provider.vercel_ai_gateway]]
model_match = "google/gemini-3.1*"
extends = true
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "adaptive"]
prompt_caching = true
cache_breakpoint_style = "last_block"
vision_supported = true
vision = true
audio_supported = true
pdf_supported = true
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.vercel_ai_gateway]]
model_match = "*"
extends = true
responses_api = true
[[provider.cloudflare_ai_gateway]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Cloudflare exposes OpenAI-compatible Workers AI and AI Gateway routes, but capabilities depend on the routed model."
[[provider.inception]]
model_match = "mercury-2"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
structured_output = "native"
structured_output_mode = "native_json"
thinking_block_style = "reasoning_summary"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Inception documents OpenAI-compatible tool use for Mercury 2; no Harn parity probe has run yet."
[[provider.inception]]
model_match = "mercury-coder-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "delimited"
structured_output_mode = "delimited"
thinking_block_style = "none"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Mercury Coder Small supports tool use on the OpenAI-compatible API; structured object generation was not documented as GA in the source pass."
[[provider.friendli]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Friendli serverless endpoints are OpenAI-compatible; model-specific capability probes should narrow this wildcard."
[[provider.volcengine_ark]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Volcengine Ark exposes OpenAI-compatible chat completions; routed model families still need native-vs-text tool probes."
[[provider.hunyuan]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Tencent Hunyuan documents OpenAI-compatible function calling; Harn has not yet probed every Hunyuan model family."
[[provider.qianfan]]
model_match = "*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
structured_output_mode = "native_json"
text_tool_wire_format_supported = true
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Baidu Qianfan v2 documents OpenAI-compatible tool calls; model-specific parity remains unprobed."
# --- source: 20-providers/50-gemini-gemma.toml ---
# ---------- Google Gemini + Gemma 4 (via OpenRouter) -------------------------
#
# Gemini 2.5 exposes thinking budgets through OpenRouter's `reasoning`
# field. Gemma 4 does not; it's a plain instruction-tuned dense / MoE
# model with native tool calling.
[[provider.openrouter]]
model_match = "google/gemini-2.5*"
native_tools = true
# This row pins the NATIVE channel. `json` (fenced-JSON) is now the GLOBAL
# text-channel default, so no per-model json pin is needed to get json; this
# `native` pin is only kept to select the native channel. If this model is run
# on text instead, fenced-json is the default and root-cause-fixes its
# documented heredoc `<<EOF` -> `line 0: <<` leak (json swept a clean 1.0/1.0/1.0
# emission-fidelity bench). To force text formats: `preferred_tool_format =
# "json"` (fenced) or `"text"` (heredoc). gpt-oss UNMEASURED.
preferred_tool_format = "native"
thinking_modes = ["enabled", "effort"]
prompt_caching = true
cache_breakpoint_style = "last_block"
vision_supported = true
vision = true
audio_supported = true
pdf_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "google/gemini-3.5*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "adaptive"]
prompt_caching = true
cache_breakpoint_style = "last_block"
vision_supported = true
vision = true
audio_supported = true
pdf_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Gemma 4 (26B MoE / 31B) served directly by the Gemini API. Multimodal
# text+image with native function calling and thinking; no Gemini-only adaptive
# effort modes. Both the bare id and the `models/` REST resource name resolve to
# the same model. Ahead of the broader gemini rules (first match wins).
[[provider.gemini]]
model_match = "gemma-4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.gemini]]
model_match = "models/gemma-4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# Native Gemini API thinking budgets (generationConfig.thinkingConfig.
# thinkingBudget) are declared here instead of hard-coded model.contains()
# branches in gemini.rs. Flash caps at 24576 and CAN disable thinking
# (thinkingBudget:0); Pro caps at 32768 and CANNOT disable. Flash rows are
# listed first (first match wins). Effort levels map to budgets in the
# provider: minimal/low -> 1024, medium -> 8192, high/xhigh -> this ceiling.
[[provider.gemini]]
model_match = "gemini-2.5-flash*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive", "effort"]
max_thinking_budget = 24576
reasoning_disable_supported = true
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.gemini]]
model_match = "models/gemini-2.5-flash*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive", "effort"]
max_thinking_budget = 24576
reasoning_disable_supported = true
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.gemini]]
model_match = "gemini-2.5*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive", "effort"]
max_thinking_budget = 32768
reasoning_disable_supported = false
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.gemini]]
model_match = "models/gemini-2.5*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive", "effort"]
max_thinking_budget = 32768
reasoning_disable_supported = false
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.gemini]]
model_match = "gemini-3.5*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive"]
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prompt_caching = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.gemini]]
model_match = "models/gemini-3.5*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive"]
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prompt_caching = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Gemini 3.1 (Pro Preview / Flash-Lite) — same surface as 3.5:
# thinking_level control (adaptive) plus implicit/explicit caching.
[[provider.gemini]]
model_match = "gemini-3.1*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive"]
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prompt_caching = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.gemini]]
model_match = "models/gemini-3.1*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled", "adaptive"]
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prompt_caching = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.gemini]]
model_match = "gemini-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.gemini]]
model_match = "models/gemini-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
vision_supported = true
audio_supported = true
pdf_supported = true
files_api_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "google/gemma-4*"
native_tools = true
vision_supported = true
preferred_tool_format = "native"
structured_output = "native"
top_k_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# Meta Llama 3+ routes through OpenRouter to providers (Together, Fireworks,
# Cloudflare, etc.) that all expose OpenAI-compatible function-calling and
# JSON-schema structured output. Surfacing the capability lets eval harnesses
# and downstream callers use these models for structured-output workloads
# without per-call workarounds.
[[provider.openrouter]]
model_match = "meta-llama/llama-4*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
top_k_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.openrouter]]
model_match = "meta-llama/llama-3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/60-together.toml ---
# ---------- Together reasoning models ----------------------------------------
#
# Together exposes three reasoning surfaces today:
# - Qwen uses `chat_template_kwargs.thinking` / `enable_thinking`.
# - Hybrid Kimi/GLM/Gemma models use `reasoning.enabled`.
# - GPT-OSS uses top-level `reasoning_effort`.
# Keep each host-specific rule explicit so the generic OpenAI fallback does
# not accidentally claim the wrong wire format.
# gpt-oss on Together uses NATIVE tool calls. The prior `json`/text pin was a
# defensive workaround for an "empty native streaming payload" defect that no
# longer reproduces: 2026-06-12 live probes (raw curl + Harn's streaming path)
# confirm gpt-oss-120b returns clean OpenAI-shape `tool_calls` in both
# non-streaming and SSE modes, and Harn's index-keyed SSE reassembler
# (transport.rs `oai_tool_map`) accumulates the arg-fragment deltas correctly.
# Conversely, BOTH `json` and `text` formats yield ZERO parsed calls because
# gpt-oss does not emit Harn's ```tool/name/args contract — it emits a bare
# `{"tool":..,"arguments":..}` dialect that the fenced-JSON parser does not
# recognize. native is therefore the measured-best (and only working) channel.
# Pinned EXPLICITLY (not inherited) because `openai/gpt-oss-120b` is a
# catalogued model and the catalog invariant requires the rule to set both
# fields. To change, set `preferred_tool_format` ("native" | "json" | "text").
#
# `reasoning_required_for_tools = true`: gpt-oss (Harmony) performs tool
# calls INSIDE the chain-of-thought channel, so reasoning-off breaks tool
# calling (billed-noncommittal: 0 tool_calls + tiny completion). This is the
# OPPOSITE of the Qwen3 quirk — gpt-oss must NOT carry an
# `auto_reasoning_overrides = { agent/verify/code = "off" }` override; the
# flag makes reasoning_policy refuse to floor tool tasks to off.
[[provider.together]]
model_match = "openai/gpt-oss-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_required_for_tools = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.together]]
model_match = "meta-llama/Llama-3.3-70B-Instruct-Turbo"
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "unknown"
tool_mode_parity_notes = "Together documents native tool calls for this serverless sample route; add live parity probes before broadening to all Together-hosted Llama variants."
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.together]]
model_match = "deepseek-ai/deepseek-v4*"
native_tools = true
preferred_tool_format = "native"
thinking_modes = ["enabled", "effort"]
reasoning_effort_supported = true
prompt_caching = true
structured_output = "native"
server_parser = "none"
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.together]]
model_match = "moonshotai/kimi-k2*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.together]]
model_match = "zai-org/glm-5*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke after parser fix: forced native/off emitted no dispatchable tool_calls and hallucinated a result token; heredoc text tools completed the loop."
structured_output = "native"
thinking_modes = ["enabled"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
prompt_caching = true
honors_chat_template_kwargs = false
reasoning_text_promotable = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# MiniMax M3 on Together — un-probed route. Host precedent says caution:
# the probed Together MiniMax-M2.7 route mis-serves the native channel
# (see the m2.7 rule below), so M3 inherits the text pin as a
# family-on-host consistency verdict until a forced-format probe on this
# route says otherwise.
[[provider.together]]
model_match = "minimaxai/minimax-m3*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "Family-on-host consistency pin: Together MiniMax-M2.7 native was 1/5 fidelity in the 2026-06-24 sweep; no M3-on-Together probe yet, so inherit text rather than an optimistic native pin."
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
honors_chat_template_kwargs = false
reasoning_text_promotable = false
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# probed 2026-06-24 (docs/eval/provider-tool-mode-sweep-2026-06-24.md, N=5,
# forced-format single-tool authoring of a backslash-heavy Zig body): heredoc
# text beat fenced-JSON on BOTH dispatch (4/5 vs 3/5) and fidelity (4/5 vs 2/5);
# native was 1/5 fidelity. The prior `json` pin still rode the escaping-prone
# channel — flip to escape-free heredoc text so backslash-heavy code round-trips.
[[provider.together]]
model_match = "minimaxai/minimax-m2.7*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-24 forced-format sweep (N=5): Together MiniMax-M2.7 native 1/5 fidelity, fenced-JSON 2/5; heredoc text 4/5 (best on both dispatch and fidelity). Backslash-heavy bodies only round-trip on the escape-free text channel. Supersedes the 2026-06-20 json pin."
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
honors_chat_template_kwargs = false
reasoning_text_promotable = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.together]]
model_match = "google/gemma-4*"
native_tools = true
vision_supported = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
honors_chat_template_kwargs = false
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.together]]
model_match = "together/nvidia/nemotron-3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.together]]
model_match = "moonshotai/*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/70-mistral-cohere-xai-groq.toml ---
# ---------- Mistral / Cohere / xAI / Groq OpenAI-compatible routes ------------
[[provider.mistral]]
model_match = "mistral-medium-3-5*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
vision_supported = true
vision = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.mistral]]
model_match = "mistral-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.mistral]]
model_match = "codestral-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.mistral]]
model_match = "devstral-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.cohere]]
model_match = "command-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
thinking_modes = ["adaptive"]
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.xai]]
model_match = "grok-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
thinking_modes = ["adaptive"]
prompt_caching = true
vision_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# xAI rejects these OpenAI sampling fields on every Grok model with HTTP 400
# ("Model grok-* does not support parameter ..."); confirmed by live probe on
# grok-4.5, grok-4.3, and grok-build-0.1 (2026-07-14). Gating them here keeps
# the quirk in Harn's compat layer so no caller ever sends an invalid request.
frequency_penalty_supported = false
presence_penalty_supported = false
stop_supported = false
# Groq Compound systems route through the chat-completions endpoint but reject
# OpenAI `tools` outright (`tool calling is not supported with this model`,
# 2026-07-09 live Harn tool-probe). A text-tool smoke with the runtime contract
# returned a pseudo call in provider reasoning text, not an executable visible
# tool call, so treat them as fast agentic chat systems rather than Harn
# tool-calling executors.
[[provider.groq]]
model_match = "groq/compound*"
native_tools = false
preferred_tool_format = "none"
text_tool_wire_format_supported = false
structured_output = "none"
structured_output_mode = "none"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# gpt-oss on Groq uses NATIVE tool calls. The prior `json`/text pin was a
# defensive workaround for an "empty native streaming payload" defect that no
# longer reproduces: 2026-06-12 live probes (raw curl + Harn's streaming path)
# confirm gpt-oss-120b returns clean OpenAI-shape `tool_calls` in both
# non-streaming and SSE modes, and Harn's index-keyed SSE reassembler
# (transport.rs `oai_tool_map`) accumulates the arg-fragment deltas correctly.
# Conversely, BOTH `json` and `text` formats yield ZERO parsed calls because
# gpt-oss does not emit Harn's ```tool/name/args contract — it emits a bare
# `{"tool":..,"arguments":..}` dialect that the fenced-JSON parser does not
# recognize. native is therefore the measured-best (and only working) channel.
# Pinned EXPLICITLY (not inherited) because `groq/openai/gpt-oss-120b` is a
# catalogued model and the catalog invariant requires every catalogued model's
# rule to set native_tools + preferred_tool_format. To change, set
# `preferred_tool_format` ("native" | "json" | "text").
#
# `reasoning_required_for_tools = true`: gpt-oss (Harmony) calls tools INSIDE
# the chain-of-thought channel, so reasoning-off breaks tool calling. Live
# Groq probe (2026-06-13): reasoning_effort=low -> 1 clean tool call;
# reasoning_effort=none -> rejected ("must be one of low/medium/high"). So
# this row only ever sends {low,medium,high} (reasoning_effort_levels) and
# never floors to off for tool tasks. Must NOT carry a Qwen-style
# `auto_reasoning_overrides = "off"`.
[[provider.groq]]
model_match = "*gpt-oss-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_required_for_tools = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Qwen 3.6 on Groq speaks clean OpenAI-shape native tool calls (Groq
# parses the Qwen tool format server-side); no reported parity issues.
[[provider.groq]]
model_match = "qwen/qwen3.6*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.groq]]
model_match = "llama-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/chat/completions"
text_tool_wire_format_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/80-cerebras.toml ---
# ---------- Cerebras ----------------------------------------------------------
# Cerebras-hosted GPT-OSS accepts top-level `reasoning_effort` only for
# {low, medium, high}. It rejects `none` and `minimal` with
# wrong_api_format. Harn's provider-neutral `reasoning_policy: "off"` therefore
# floors to the lowest accepted effort (`low`) instead of sending a value the
# endpoint cannot template.
#
# gpt-oss on Cerebras uses NATIVE tool calls. The prior `json`/text pin was a
# defensive workaround for an "empty native streaming payload" defect that no
# longer reproduces: 2026-06-12 live probes (raw curl + Harn's streaming path)
# confirm Cerebras gpt-oss-120b returns clean OpenAI-shape `tool_calls` in both
# non-streaming and SSE modes, and Harn's index-keyed SSE reassembler
# (transport.rs `oai_tool_map`) accumulates the arg-fragment deltas correctly
# (3/3 tool_calls=1 end-to-end). Conversely, BOTH `json` and `text` formats
# yield ZERO parsed calls because gpt-oss does not emit Harn's ```tool/name/args
# contract — it emits a bare `{"tool":..,"arguments":..}` dialect that the
# fenced-JSON parser does not recognize. native is therefore the measured-best
# (and only working) channel. Pinned EXPLICITLY because `gpt-oss-120b` is a
# catalogued model and the catalog invariant requires every catalogued model's
# rule to set native_tools + preferred_tool_format. (groq / together gpt-oss
# rows are now native too.)
# To change, set `preferred_tool_format` ("native" | "json" | "text").
#
# `reasoning_required_for_tools = true`: gpt-oss (Harmony) calls tools INSIDE
# the chain-of-thought channel, so reasoning-off breaks tool calling. Combined
# with reasoning_effort_levels above, reasoning_policy floors an auto "off" to
# the lowest accepted effort ("low") for tool tasks instead of disabling
# reasoning. Must NOT carry a Qwen-style `auto_reasoning_overrides = "off"`.
#
# Cerebras accepts native tools and structured output independently, but rejects
# `tools` + `response_format` in the same chat-completions request. Prefer the
# interactive tool loop when both are requested, and drop response_format at the
# OpenAI-compatible wire boundary.
[[provider.cerebras]]
model_match = "gpt-oss-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
tools_exclude_response_format = true
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_required_for_tools = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Cerebras serves gpt-oss-120b at FULL precision, but request-rate throttling
# (~57K tok/req capping to ~4.4 req/min during the 2026-06 effort) makes the
# *timing* unusable for a durable N>=5 meter baseline even though the OUTPUTS
# are trustworthy. The precision canary should not reject this route on quality
# grounds, but the meter must budget for the throughput ceiling.
serving_precision = "throttled"
# Cerebras GLM 4.7 is a public preview model with native tools, native
# structured output, and top-level `reasoning_effort`; `none` is the documented
# no-thinking value, while the older `disable_reasoning` knob is deprecated.
[[provider.cerebras]]
model_match = "zai-glm-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
tools_exclude_response_format = true
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_none_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.cerebras]]
model_match = "llama-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
tools_exclude_response_format = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.cerebras]]
model_match = "qwen-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
tools_exclude_response_format = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/90-enterprise.toml ---
# ---------- Enterprise cloud providers ---------------------------------------
# Bedrock Converse normalizes native tool calling across Anthropic, Meta,
# Amazon, Mistral, and other Bedrock-hosted model families. Tool-search and
# prompt-cache semantics remain provider/model-specific, so don't advertise
# those through the generic Bedrock route yet.
# Bedrock-hosted Claude keeps Anthropic's prompt-format preferences while
# using Bedrock Converse transport. The `anthropic.claude-*` rule must
# come before the broader `*claude*` fallback so the canonical Bedrock
# model identifiers pick up the anthropic wire-format fields.
[[provider.bedrock]]
model_match = "anthropic.claude-*"
native_tools = true
batch_api = true
preferred_tool_format = "native"
message_wire_format = "anthropic"
native_tool_wire_format = "anthropic"
recommended_endpoint = "/model/{model}/converse"
# Bedrock Converse has only a top-level `system[]` field — no positional system
# channel — so an interleaved directive folds into the adjacent user turn rather
# than mis-deriving to `inline` from the non-`anthropic` catch-all wire format.
system_message_placement = "fold"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.bedrock]]
model_match = "*claude*"
native_tools = true
batch_api = true
preferred_tool_format = "native"
recommended_endpoint = "/model/{model}/converse"
# Bedrock Converse has only a top-level `system[]` field — no positional system
# channel — so an interleaved directive folds into the adjacent user turn rather
# than mis-deriving to `inline` from the non-`anthropic` catch-all wire format.
system_message_placement = "fold"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = true
prefers_markdown_scaffolding = false
structured_output_mode = "xml_tagged"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = true
thinking_block_style = "thinking_blocks"
[[provider.bedrock]]
model_match = "*"
native_tools = true
batch_api = true
preferred_tool_format = "native"
recommended_endpoint = "/model/{model}/converse"
# Bedrock Converse has only a top-level `system[]` field — no positional system
# channel — so an interleaved directive folds into the adjacent user turn rather
# than mis-deriving to `inline` from the non-`anthropic` catch-all wire format.
system_message_placement = "fold"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# Azure OpenAI uses deployment-name routing but keeps the OpenAI chat
# completions tool-call body. Do not inherit OpenAI Responses API
# tool_search flags: this shim targets Azure's chat-completions endpoint.
[[provider.azure_openai]]
model_match = "gpt-*"
native_tools = true
batch_api = true
preferred_tool_format = "native"
recommended_endpoint = "/openai/deployments/{deployment}/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
[[provider.azure_openai]]
model_match = "o1*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/openai/deployments/{deployment}/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.azure_openai]]
model_match = "o3*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/openai/deployments/{deployment}/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.azure_openai]]
model_match = "o4*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/openai/deployments/{deployment}/chat/completions"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = true
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Vertex Gemini exposes function declarations in generateContent. Harn maps
# native tool schemas to that shape, but leaves provider-specific cached
# content / thinking flags off until they are wired as first-class options.
[[provider.vertex]]
model_match = "gemini-*"
native_tools = true
preferred_tool_format = "native"
recommended_endpoint = "/projects/{project}/locations/{location}/publishers/google/models/{model}:generateContent"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# --- source: 20-providers/95-mock.toml ---
# ---------- Mock --------------------------------------------------------------
#
# Mock spoofs either Anthropic or OpenAI shape depending on the model ID.
# Handled specially in the loader (see `capabilities::lookup`): Claude-
# shape model strings route to the `anthropic` rule list first, otherwise
# fall through to the `openai` rule list.
# ---------- provider_family aliases ------------------------------------------
# --- source: 20-providers/96-deepseek-direct.toml ---
# ---------- DeepSeek V4 direct (OpenAI-compatible /v1) ------------------------
# V4-Flash + V4-Pro support native tool calls, thinking mode, prompt
# caching, and JSON output. DeepSeek's V4 notice retires
# `deepseek-chat`/`deepseek-reasoner` on 2026-07-24 15:59 UTC, so the
# direct catalog intentionally exposes only V4 model IDs.
[[provider.deepseek]]
model_match = "deepseek-v4-pro*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
prompt_caching = true
[[provider.deepseek]]
model_match = "deepseek-v4-flash*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
prompt_caching = true
# --- source: 20-providers/97-minimax.toml ---
# ---------- MiniMax (OpenAI-compatible /v1) ----------------------------------
# MiniMax M3 adds adaptive thinking, 1M context, native tools, prompt
# caching, and text/image/video input. The M2 family remains open-weight:
# 230B/10B MoE with native tool calls + thinking mode. Prompt caching is
# honored on M2.7+ at 20% of input. Highspeed variants share the same wire
# shape, just skip the thinking-mode budget.
[[provider.minimax]]
model_match = "minimax-m3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "delimited"
thinking_modes = ["adaptive"]
reasoning_wire_format = "minimax"
requires_completion_tokens = true
vision = true
vision_supported = true
video_supported = true
prompt_caching = true
reasoning_text_promotable = false
requires_tool_result_adjacency = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.minimax]]
model_match = "minimax-m2.7*"
native_tools = true
preferred_tool_format = "json"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke after parser fix: forced native/off emitted no dispatchable tool_calls and prose claiming the tool had run; fenced JSON text-channel tools completed the loop."
structured_output = "delimited"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
prompt_caching = true
reasoning_text_promotable = false
requires_tool_result_adjacency = true
[[provider.minimax]]
model_match = "minimax-m2.5*"
native_tools = true
preferred_tool_format = "native"
structured_output = "delimited"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
reasoning_text_promotable = false
requires_tool_result_adjacency = true
[[provider.minimax]]
model_match = "minimax-m2*"
native_tools = true
preferred_tool_format = "native"
structured_output = "delimited"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
reasoning_text_promotable = false
requires_tool_result_adjacency = true
[[provider.minimax]]
model_match = "minimax-text-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "delimited"
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
requires_tool_result_adjacency = true
# --- source: 20-providers/98-zai.toml ---
# ---------- Z.AI / GLM-5 family (open-weight, OpenAI-compatible /v1) ---------
# GLM-5.1 (754B open weights, April 2026) sustains long-horizon agentic
# tasks; prompt caching is honored, structured output uses JSON mode
# (delimited fallback).
#
# TOOL CHANNEL — PINNED TO TEXT (`native_unreliable`). The GLM-5.x native
# channel is the same `<tool_call>`-markup footgun Harn already pins around on
# Baseten/Fireworks/DeepInfra: the 2026-06-23 live Baseten probe found GLM-5.2
# emits visible `<tool_call><arg_key>...` content instead of OpenAI
# `message.tool_calls` on the native channel, while Harn's heredoc text-tool
# grammar parsed the same intent cleanly (see 39-baseten.toml). The Fireworks
# (`glm-5p*`) and DeepInfra (`*glm-5*`) rows already carry
# `tool_mode_parity = "native_unreliable"` for this family. The zai-direct (and
# OpenRouter) GLM-5 routes serve the identical weights, so the prior optimistic
# `native` pin here risked the same vanishing/markup tool stream. Pin to TEXT so
# a `native` pin or `--tool-format native` auto-corrects to `text` with an
# explanatory `correction` via `validate_tool_format`, instead of a silent
# markup-as-content tool call. `text_tool_wire_format_supported = true` keeps the
# text channel viable (so the no-viable-channel fail-fast guard never fires).
# TODO(zai-glm-native-probe): if a forced-format native probe against a specific
# zai-direct GLM-5.x route ever returns clean `message.tool_calls`, narrow this
# back to `native` for that route with the probe transcript as evidence.
#
# THINKING WIRE — Z.AI is OpenAI-compatible for chat, but not for reasoning
# controls. Official GLM-5.2 docs show `thinking = {type = "enabled"}` plus
# top-level `reasoning_effort` (https://docs.z.ai/guides/llm/glm-5.2), and
# external bug reports caught clients still sending stale `enable_thinking`
# booleans (https://github.com/openclaw/openclaw/issues/58680). Keep the wire
# shape in Harn's capability matrix via provider_defaults.zai instead of making
# each host or Burin learn that quirk.
[[provider.zai]]
model_match = "glm-5.2*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "GLM-5.x native channel emits `<tool_call><arg_key>...` markup as content instead of OpenAI message.tool_calls (2026-06-23 live Baseten probe, see 39-baseten.toml); heredoc text tools parse cleanly. Same family pinned native_unreliable on Fireworks (glm-5p*) and DeepInfra (*glm-5*)."
structured_output = "native"
thinking_modes = ["enabled", "effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["none", "minimal", "low", "medium", "high", "xhigh", "max"]
reasoning_none_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
prompt_caching = true
[[provider.zai]]
model_match = "glm-5.1*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "GLM-5.x native channel emits `<tool_call><arg_key>...` markup as content instead of OpenAI message.tool_calls (2026-06-23 live Baseten probe, see 39-baseten.toml); heredoc text tools parse cleanly. Same family pinned native_unreliable on Fireworks (glm-5p*) and DeepInfra (*glm-5*)."
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
prompt_caching = true
# GLM-4.x family (glm-4.5, glm-4.5-air, glm-4.6, glm-4.7) — same wire
# format and `<tool_call>` markup lineage as GLM-5; pinned text for family
# consistency (mirrors the NVIDIA glm-5 family-consistency gate) until a
# clean zai-direct native probe says otherwise. Without this rule the
# catalogued glm-4.x rows would fall through to the OpenAI-family gpt-*
# rules and resolve empty capabilities.
[[provider.zai]]
model_match = "glm-4*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "Family-consistency pin: GLM native channels emit `<tool_call>` markup as content (2026-06-23 Baseten GLM-5.2 probe); no zai-direct GLM-4.7 probe yet, so inherit the family verdict rather than an optimistic native pin."
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.zai]]
model_match = "glm-5*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "GLM-5.x native channel emits `<tool_call><arg_key>...` markup as content instead of OpenAI message.tool_calls (2026-06-23 live Baseten probe, see 39-baseten.toml); heredoc text tools parse cleanly. Same family pinned native_unreliable on Fireworks (glm-5p*) and DeepInfra (*glm-5*)."
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# --- source: 20-providers/99-openrouter-new-families.toml ---
# ---------- OpenRouter slugs for the new families -----------------------------
# OpenRouter is OpenAI-compatible at the wire level, so model-specific
# overrides only need to set `native_tools` + `preferred_tool_format`.
# The audit test trips on missing entries because provider_family
# inheritance does not cover specific OR slugs by itself.
[[provider.openrouter]]
model_match = "minimax/minimax-m3*"
native_tools = true
preferred_tool_format = "native"
text_tool_wire_format_supported = true
structured_output = "delimited"
thinking_modes = ["adaptive"]
vision = true
vision_supported = true
video_supported = true
prompt_caching = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "delimited"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "minimax/minimax-m2.7*"
native_tools = true
preferred_tool_format = "native"
text_tool_wire_format_supported = true
structured_output = "delimited"
[[provider.openrouter]]
model_match = "minimax/minimax-m2*"
native_tools = true
preferred_tool_format = "native"
text_tool_wire_format_supported = true
structured_output = "delimited"
[[provider.openrouter]]
model_match = "z-ai/glm-5*"
native_tools = true
preferred_tool_format = "text"
tool_mode_parity = "native_unreliable"
tool_mode_parity_notes = "2026-06-20 Harn agent-loop smoke after parser fix: forced native/off emitted no dispatchable tool_calls and hallucinated a result token; heredoc text tools completed the loop. OpenRouter reasoning-on text tools emitted malformed tool markup, so auto reasoning is off for agent/code/verify tasks."
text_tool_wire_format_supported = true
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_effort_supported = true
reasoning_effort_levels = ["high", "xhigh", "max"]
auto_reasoning_overrides = { agent = "off", verify = "off", code = "off" }
prompt_caching = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "kwaipilot/kat-coder-pro-v2"
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "interchangeable"
tool_mode_parity_notes = "Live OpenRouter probe on 2026-06-12 returned a valid provider-native tool call for KAT-Coder-Pro V2."
prompt_caching = true
text_tool_wire_format_supported = true
structured_output = "native"
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "none"
# OpenAI gpt-oss (Harmony) served via OpenRouter. Without an explicit row this
# slug falls through provider_family openrouter -> openai, matches no `gpt-*`
# rule (the `openai/` prefix and the unparseable `oss` "version" both fail the
# match), and lands on a capability-less catch-all that declares NO reasoning
# support — so the policy resolver cannot even materialize reasoning.
#
# `reasoning_required_for_tools = true`: gpt-oss performs tool calls INSIDE the
# Harmony chain-of-thought channel, so disabling reasoning breaks tool calling.
# Live OpenRouter probe (openai/gpt-oss-120b, 2026-06-13, identical tool
# request): reasoning {enabled:false} -> 0 tool_calls + null completion_tokens
# (the billed-noncommittal failure); reasoning {effort:low} -> 1 clean native
# tool call; no reasoning field (provider default) -> 1 clean native tool call.
# `reasoning_disable_supported = false` keeps Harn from ever emitting the
# `reasoning:{enabled:false}` disable directive on the wire, and the
# required-for-tools flag keeps the auto policy from flooring tool tasks to off.
# Must NOT carry a Qwen-style `auto_reasoning_overrides = "off"`.
#
# OpenRouter's provider-native tool surface is not stable enough to expose as
# the default abstraction for this aggregate route: forced native finishes
# billed-noncommittal (0 dispatchable tool_calls). Between the two TEXT-channel
# grammars, an empirical A/B (2026-06-21, real OpenRouter calls, task = author a
# Zig file heavy in `\\` multiline-string literals) showed `text`/heredoc beats
# `json` on BOTH axes: dispatch 3/3 vs 2/3, and byte-fidelity 3/3 clean vs 0/3
# clean. The `json` arm corrupted source bodies the same way every GPT-OSS
# serving stack does — a JSON string arg forces the model to escape the
# backslashes the Zig source already needs, and gpt-oss double-escapes them
# (`\\` -> `\\\\` prefixes, escaped quotes, and a run that collapsed into invalid
# Zig) — while the escape-free heredoc body carried the source verbatim. Pin to
# `text`. (Same class as the Fireworks GPT-OSS json->text flip; direct
# Cerebras/Groq/DeepInfra GPT-OSS rows keep `native`, whose provider-native
# channel does dispatch cleanly on those stacks.)
#
# The OpenRouter sub-provider lottery still matters. `openai/gpt-oss-120b` fans
# out across many upstreams; even with reasoning ON, some mis-serialize Harmony
# turns and finish billed-noncommittal. Keep the known-clean upstream allowlist:
# `openrouter_provider_order = ["Cerebras", "Groq"]` materializes to
# `provider: { order: ["Cerebras","Groq"], allow_fallbacks: false }`.
[[provider.openrouter]]
model_match = "openai/gpt-oss-*"
native_tools = false
preferred_tool_format = "text"
structured_output = "native"
thinking_modes = ["effort"]
reasoning_effort_supported = true
reasoning_effort_levels = ["low", "medium", "high"]
reasoning_disable_supported = false
reasoning_required_for_tools = true
openrouter_provider_order = ["Cerebras", "Groq"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
# Full-precision route: the Cerebras/Groq upstream allowlist above only ever
# lands on full-precision upstreams, so OpenRouter openai/gpt-oss-120b is the
# trusted-precision REFERENCE the meter precision canary pairs candidates
# against. Verified clean across the 2026-06-13 live probes documented above.
serving_precision = "trusted"
[[provider.openrouter]]
model_match = "stepfun/step-3.7-flash"
native_tools = true
preferred_tool_format = "native"
tool_mode_parity = "interchangeable"
tool_mode_parity_notes = "Live OpenRouter probe on 2026-06-12 returned a valid provider-native tool call for Step 3.7 Flash with reasoning enabled."
prompt_caching = true
structured_output = "native"
thinking_modes = ["enabled"]
reasoning_disable_supported = false
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# July 2026 OpenRouter catalog deltas whose slugs do not inherit useful
# family-specific capability rows through the OpenAI-compatible transport
# fallback. Keep them explicit so the catalog audit can catch future rows that
# need a real tool contract decision.
[[provider.openrouter]]
model_match = "x-ai/grok-*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["adaptive"]
prompt_caching = true
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "reasoning_summary"
[[provider.openrouter]]
model_match = "bytedance-seed/*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
prompt_caching = true
vision_supported = true
video_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "cohere/north-mini-code:free"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "nvidia/nemotron-3*"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
[[provider.openrouter]]
model_match = "openrouter/free"
native_tools = true
preferred_tool_format = "native"
structured_output = "native"
thinking_modes = ["enabled"]
vision_supported = true
text_tool_wire_format_supported = true
prefers_xml_scaffolding = false
prefers_markdown_scaffolding = true
structured_output_mode = "native_json"
supports_assistant_prefill = false
prefers_role_developer = false
prefers_xml_tools = false
thinking_block_style = "inline"
# --- source: 90-families/provider-family.toml ---
[provider_family]
openrouter = "openai"
together = "openai"
groq = "openai"
cerebras = "openai"
deepseek = "openai"
fireworks = "openai"
huggingface = "openai"
local = "openai"
# New April 2026 routes for Qwen3.6:
dashscope = "openai"
llamacpp = "openai"
mlx = "openai"
# New 2026-05 routes (open-weight frontier additions). MiniMax + Z.AI
# both speak the OpenAI chat-completions wire format on their
# advertised compat endpoints, so the openai family rules cover the
# baseline. Model-specific tweaks live in `[[provider.minimax]]` /
# `[[provider.zai]]` blocks above.
minimax = "openai"
zai = "openai"
nvidia = "openai"
nebius = "openai"
flexai = "openai"
hyperbolic = "openai"
siliconflow = "openai"
parasail = "openai"
atlas = "openai"
github_models = "openai"
# Creator/model slugs need the same routed-model capability rows as OpenRouter
# before falling through to the shared OpenAI-compatible transport defaults.
vercel_ai_gateway = "openrouter"
cloudflare_ai_gateway = "openai"
inception = "openai"
friendli = "openai"
volcengine_ark = "openai"
hunyuan = "openai"
qianfan = "openai"