feat(providers): add a Tsubasa descriptor on the compatible transport (#6695)

Tsubasa gets one bundled row in provider_descriptors.json on the existing
OpenAI-compatible Chat Completions path, as @cenab proposed and the founder
approved: base https://api.tsubasa.sh/v1, key env TSUBASA_API_KEY, bootstrap
model tsubasa-pro, docs https://tsubasa.sh/docs. No new ProviderKind, no
transport, no prices, capabilities or underlying-model claims.

Discovery stays `models_endpoint`, like every neighbouring row: Tsubasa's
published API contract (tsubasa.sh/docs) names GET /v1/models as the catalog
authority, and the live 404 is not specific to discovery — on 2026-09-28
POST /v1/chat/completions also returned 404 without a key, and the public
model page lists both models as Disabled.

The descriptor schema has no context or output fields, so the 32,768-token
window and the second public id (tsubasa-fast) reach the user through the
row's guidance and docs/PROVIDERS.md, with the exact
`codewhale config set providers.tsubasa.context_window 32768` command
(an unknown custom model otherwise budgets 128,000 tokens).

Pricing default-coverage ledger records the route as unknown billing basis
and the compatible-default breadth receipt moves from 7 to 8.

Tests (focused):
- cargo test -p codewhale-config --lib -- descriptors route::export:
  9 passed; 0 failed; 1 ignored (new tsubasa_is_a_compatible_row_with_its_own_key;
  golden_providers_export_matches_registry passes, golden unchanged)
- cargo test -p codewhale-tui --lib pricing::default_coverage_tests:
  1 passed; 0 failed
- cargo test -p codewhale-tui --lib provider_picker: 148 passed; 0 failed
- scripts/check-provider-registry.py: passed
- cargo fmt --all -- --check: clean

Closes #6695

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_014ZwqatxgVFxHvovngywnks
This commit is contained in:
Hunter BandClaude Opus 5.5 committed 2026-09-28 21:47:14 -07:00
1 parent 11b2c18777
commit 0466194c7e
6 files changed
+41 -4

No files matched your search

@@ -102,6 +102,17 @@
"meituan-sensenova",
"meituan-sensenova-cn"
]
},
{
"id": "tsubasa",
"label": "Tsubasa",
"wire": "openai-compatible",
"base_url": "https://api.tsubasa.sh/v1",
"api_key_env": "TSUBASA_API_KEY",
"default_model": "tsubasa-pro",
"discovery": "models_endpoint",
"docs_url": "https://tsubasa.sh/docs",
"guidance": "OpenAI-compatible host (Chat Completions + GET /v1/models only). Public model ids: tsubasa-pro and tsubasa-fast, both with a 32,768-token context, so set context_window = 32768 on [providers.tsubasa]. Store TSUBASA_API_KEY, not a raw key."
}
]
}
+20
View File
@@ -153,6 +153,26 @@ mod tests {
}
}
/// #6695: Tsubasa is a data row on the existing compatible transport with
/// its own key env. The row carries no context field, so the guidance is
/// where the 32K window and the second public model id reach the user.
#[test]
fn tsubasa_is_a_compatible_row_with_its_own_key() {
let tsubasa = provider_descriptor("tsubasa").expect("tsubasa");
assert_eq!(tsubasa.wire, DescriptorWire::OpenaiCompatible);
assert_eq!(tsubasa.base_url, "https://api.tsubasa.sh/v1");
assert_eq!(tsubasa.api_key_env, "TSUBASA_API_KEY");
assert_eq!(tsubasa.default_model, "tsubasa-pro");
let guidance = tsubasa.guidance.as_deref().expect("tsubasa guidance");
for needle in [
"tsubasa-fast",
"context_window = 32768",
"Store TSUBASA_API_KEY",
] {
assert!(guidance.contains(needle), "{needle}: {guidance}");
}
}
#[test]
fn descriptors_do_not_embed_model_rosters() {
let raw = DESCRIPTORS_JSON;
@@ -123,7 +123,7 @@ fn shipped_default_routes_have_reviewed_pricing_coverage() {
assert_eq!(built_in_count, 51, "review changed shipped-default breadth");
assert_eq!(
defaults.len() - built_in_count,
7,
8,
"review compatible-default breadth"
);
let recorded_at = Utc.with_ymd_and_hms(2026, 9, 8, 12, 0, 0).unwrap();
@@ -36,7 +36,7 @@
},
{
"reason": "unknown_billing_basis",
"review": "No mode/credential product is supplied. Both MiniMax dialects share a PAYG/plan endpoint that cannot prove product (route_billing.rs:253,402; pricing.rs:534); Ollama Cloud is unclassified possible spend, not local/free (pricing.rs:411). Generic Custom and six non-Baseten compatible defaults are transport descriptors without a reviewed product; production envelope refuses Unknown at cost_status.rs:239. AICraft is the sixth compatible default: dc170add3 restored its row from the closed PR #6171 with endpoint, credential env and bootstrap model only, and no billing product or pricing row was reviewed for it. Its default is claude-4.6-sonnet, which has no bundled/hand row either; that omission is recorded here on the shape of the descriptor, and a real source review of AICraft's billing basis is still owed.",
"review": "No mode/credential product is supplied. Both MiniMax dialects share a PAYG/plan endpoint that cannot prove product (route_billing.rs:253,402; pricing.rs:534); Ollama Cloud is unclassified possible spend, not local/free (pricing.rs:411). Generic Custom and seven non-Baseten compatible defaults are transport descriptors without a reviewed product; production envelope refuses Unknown at cost_status.rs:239. AICraft is the sixth compatible default: dc170add3 restored its row from the closed PR #6171 with endpoint, credential env and bootstrap model only, and no billing product or pricing row was reviewed for it. Its default is claude-4.6-sonnet, which has no bundled/hand row either; that omission is recorded here on the shape of the descriptor, and a real source review of AICraft's billing basis is still owed. Tsubasa (#6695) is the seventh: endpoint, credential env and bootstrap model tsubasa-pro only; its public site lists prepaid-credit per-token rates, but no pricing row was reviewed or frozen for it, so it stays Unknown here too.",
"routes": [
["aicraft", "claude-4.6-sonnet", "https://aicraftapi.com/v1", "chat_completions", "unknown"],
["cerebras", "llama-3.3-70b", "https://api.cerebras.ai/v1", "chat_completions", "unknown"],
@@ -47,7 +47,8 @@
["minimax", "MiniMax-M3", "https://api.minimax.io/v1", "chat_completions", "unknown"],
["minimax-anthropic", "MiniMax-M3", "https://api.minimax.io/anthropic", "anthropic_messages", "unknown"],
["ollama-cloud", "gpt-oss:120b", "https://ollama.com/v1", "chat_completions", "unknown"],
["sensenova", "deepseek-v4-flash", "https://token.sensenova.cn/v1", "chat_completions", "unknown"]
["sensenova", "deepseek-v4-flash", "https://token.sensenova.cn/v1", "chat_completions", "unknown"],
["tsubasa", "tsubasa-pro", "https://api.tsubasa.sh/v1", "chat_completions", "unknown"]
]
},
{
+5
View File
@@ -42,11 +42,16 @@ against the vendor's own docs before trusting any value here:
| Command Code | `https://api.commandcode.ai/provider/v1` | `deepseek/deepseek-v4-flash` | `COMMAND_CODE_API_KEY` |
| Alibaba Model Studio (DashScope) | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `qwen3.8-flash` | `DASHSCOPE_API_KEY` |
| AICraft | `https://aicraftapi.com/v1` | `claude-4.6-sonnet`; DeepSeek / Claude / Gemini / Qwen / GLM / MiniMax / Doubao families | `AICRAFT_API_KEY` |
| Tsubasa | `https://api.tsubasa.sh/v1` | `tsubasa-pro`, `tsubasa-fast` (32,768-token context) | `TSUBASA_API_KEY` |
AICraft's roster spans DeepSeek, Anthropic Claude, Google Gemini, Qwen, GLM,
MiniMax and Doubao ids on its OpenAI-compatible endpoint. The authority is
`GET https://aicraftapi.com/v1/models` with your key — pick a model from that
list, not from this table.
Tsubasa implements only `GET /v1/models` and Chat Completions. Its two public
model ids share a 32,768-token context, smaller than the 128,000 tokens Codewhale assumes
for an unknown model, so set it on the route after saving:
`codewhale config set providers.tsubasa.context_window 32768`.
OpenCode Zen and OpenCode Go are first-class provider routes, configured like
any other provider below; they are not part of this table. In `/provider`,
type to filter the list (letters not bound to a row action); `Ctrl+T` probes the
+1 -1
View File
@@ -6,7 +6,7 @@
DeepSeek 仍是默认提供商,但 `ProviderKind::ALL` 中的每个条目都是一等公民、可选的提供商路由。`ALL` 是目录/选择器表面——每个厂商一个身份。双线协议方言种类(`*Anthropic`,例如 `deepseek-anthropic`)和 Model Studio 套餐变体保留在枚举中用于 serde 和 `provider_for_kind`,但刻意**不**作为目录行:套餐是主提供商配置(`crates/config/src/provider_kind.rs:221-226`)上的 `mode`/`base_url`,方言则是 `wire = openai|anthropic`。托管路由、通用 OpenAI 兼容端点、OpenAI Codex/ChatGPT 路由、原生 Anthropic 以及本地运行时,都在所选提供商/模型/base URL 上运行同一个终端 harness。
经普通 Chat Completions 访问的主机是普通的具名 provider(`[providers.<name>]` 表:base URL、模型、密钥环境变量),而不是 `ProviderKind`;`/provider` 与 `/setup` 保留「粘贴 Base URL 和密钥」路径。英文版中的「已知可用主机」表列出 SenseNova、Baseten、Groq、Cerebras、Command Code、阿里云百炼(DashScope)与 AICraft 的 URL 和密钥变量;这些主机作为内置描述符行随附于 `crates/config/assets/provider_descriptors.json`(仅描述如何连接主机,模型 ID 以实时 `GET /v1/models` 与 Codewhale 目录为准),仅供参考,请以各厂商文档为准。AICraft 的模型列表涵盖 DeepSeek、Anthropic Claude、Google Gemini、Qwen、GLM、MiniMax 与 Doubao(例如 `claude-4.6-sonnet`),以带密钥请求 `GET https://aicraftapi.com/v1/models` 的结果为准。OpenCode Zen 与 OpenCode Go 是下方的一等路由。在 `/provider` 中直接输入即可筛选列表(已绑定行操作的字母除外);`Ctrl+T` 探测所选行的 `/models`,只记录可达性(2xx 并不代表模型可用)。
经普通 Chat Completions 访问的主机是普通的具名 provider(`[providers.<name>]` 表:base URL、模型、密钥环境变量),而不是 `ProviderKind`;`/provider` 与 `/setup` 保留「粘贴 Base URL 和密钥」路径。英文版中的「已知可用主机」表列出 SenseNova、Baseten、Groq、Cerebras、Command Code、阿里云百炼(DashScope)、AICraft 与 Tsubasa 的 URL 和密钥变量;这些主机作为内置描述符行随附于 `crates/config/assets/provider_descriptors.json`(仅描述如何连接主机,模型 ID 以实时 `GET /v1/models` 与 Codewhale 目录为准),仅供参考,请以各厂商文档为准。AICraft 的模型列表涵盖 DeepSeek、Anthropic Claude、Google Gemini、Qwen、GLM、MiniMax 与 Doubao(例如 `claude-4.6-sonnet`),以带密钥请求 `GET https://aicraftapi.com/v1/models` 的结果为准。Tsubasa(`https://api.tsubasa.sh/v1`,`TSUBASA_API_KEY`)仅实现 `GET /v1/models` 与 Chat Completions,公开模型 `tsubasa-pro` 与 `tsubasa-fast` 的上下文均为 32,768 tokens;保存后请执行 `codewhale config set providers.tsubasa.context_window 32768`。OpenCode Zen 与 OpenCode Go 是下方的一等路由。在 `/provider` 中直接输入即可筛选列表(已绑定行操作的字母除外);`Ctrl+T` 探测所选行的 `/models`,只记录可达性(2xx 并不代表模型可用)。
需要保持同步的来源: