feat(config): harvest provider metadata registry
Add a metadata-only provider registry foundation from #2479. The registry exposes canonical lookup, alias-aware resolution, defaults, config table keys, and API-key env candidates without changing runtime routing or activating fallback providers. Co-authored-by: sximelon <62371427+sximelon@users.noreply.github.com>
This commit is contained in:
@@ -66,6 +66,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
were previously unpriced: `mimo-v2.5-pro` / `xiaomi/mimo-v2.5-pro` reuse the
|
||||
DeepSeek V4-Pro rate table and `mimo-v2.5` / `xiaomi/mimo-v2.5` reuse the
|
||||
DeepSeek V4-Flash rates. Existing DeepSeek pricing is unchanged (#2731, #2750).
|
||||
- Added a metadata-only `codewhale-config` provider registry with canonical
|
||||
lookup, alias-aware resolution, provider defaults, config-table keys, and
|
||||
API-key env candidates. Runtime routing remains unchanged and fallback
|
||||
providers stay dormant; this harvests the safe provider-trait foundation from
|
||||
#2479 toward #2075. Thanks @sximelon.
|
||||
- Added optional `[search].base_url` / `CODEWHALE_SEARCH_BASE_URL` support for
|
||||
DuckDuckGo-compatible private search endpoints, while keeping
|
||||
`DEEPSEEK_SEARCH_BASE_URL` as a legacy alias. Custom endpoints are gated by
|
||||
|
||||
@@ -642,8 +642,8 @@ Current v0.9 track credits:
|
||||
- **[shenjackyuanjie](https://github.com/shenjackyuanjie)** — HarmonyOS /
|
||||
OpenHarmony porting work and MatePad Edge validation trail (#2634)
|
||||
- **[sximelon](https://github.com/sximelon)** — saved-session resume footer
|
||||
hint work and provider-trait direction reviewed for the v0.9 track (#2758,
|
||||
#2760, #2479)
|
||||
hint work plus provider-trait metadata registry direction reviewed and
|
||||
harvested for the v0.9 track (#2758, #2760, #2479)
|
||||
- **[aboimpinto](https://github.com/aboimpinto)** — sidebar command polish and
|
||||
pausable custom-command lifecycle direction harvested into the v0.9 track
|
||||
(#2788, #2732)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod provider;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
#[cfg(unix)]
|
||||
@@ -134,6 +136,27 @@ pub enum ProviderKind {
|
||||
}
|
||||
|
||||
impl ProviderKind {
|
||||
pub const ALL: [Self; 18] = [
|
||||
Self::Deepseek,
|
||||
Self::NvidiaNim,
|
||||
Self::Openai,
|
||||
Self::Atlascloud,
|
||||
Self::WanjieArk,
|
||||
Self::Volcengine,
|
||||
Self::Openrouter,
|
||||
Self::XiaomiMimo,
|
||||
Self::Novita,
|
||||
Self::Fireworks,
|
||||
Self::Siliconflow,
|
||||
Self::SiliconflowCN,
|
||||
Self::Arcee,
|
||||
Self::Moonshot,
|
||||
Self::Sglang,
|
||||
Self::Vllm,
|
||||
Self::Ollama,
|
||||
Self::Huggingface,
|
||||
];
|
||||
|
||||
#[must_use]
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
@@ -192,6 +215,15 @@ impl ProviderKind {
|
||||
pub fn is_siliconflow(self) -> bool {
|
||||
matches!(self, Self::Siliconflow | Self::SiliconflowCN)
|
||||
}
|
||||
|
||||
/// Return the built-in metadata entry for this provider.
|
||||
///
|
||||
/// This is a metadata foundation only; runtime routing still resolves
|
||||
/// through [`ConfigToml::resolve_runtime_options`].
|
||||
#[must_use]
|
||||
pub fn provider(self) -> &'static dyn provider::Provider {
|
||||
provider::provider_for_kind(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
@@ -4401,6 +4433,92 @@ unix_socket_path = "/tmp/cw-hooks.sock"
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_metadata_registry_covers_every_provider_kind_once() {
|
||||
let providers = provider::all_providers();
|
||||
assert_eq!(providers.len(), ProviderKind::ALL.len());
|
||||
|
||||
for (kind, provider) in ProviderKind::ALL.iter().zip(providers.iter()) {
|
||||
assert_eq!(provider.kind(), *kind);
|
||||
assert_eq!(provider.id(), kind.as_str());
|
||||
assert_eq!(kind.provider().id(), kind.as_str());
|
||||
}
|
||||
|
||||
let mut ids = std::collections::BTreeSet::new();
|
||||
for provider in providers {
|
||||
assert!(ids.insert(provider.id()), "duplicate provider id");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_metadata_lookup_does_not_fall_back_to_deepseek() {
|
||||
assert!(provider::lookup_provider("not-a-provider").is_none());
|
||||
assert!(provider::resolve_provider("not-a-provider").is_none());
|
||||
assert!(provider::lookup_provider("deepseek-cn").is_none());
|
||||
assert_eq!(
|
||||
provider::resolve_provider("deepseek-cn")
|
||||
.expect("legacy alias resolves")
|
||||
.kind(),
|
||||
ProviderKind::Deepseek
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_metadata_preserves_alias_and_config_key_semantics() {
|
||||
assert_eq!(
|
||||
provider::resolve_provider("open_router")
|
||||
.expect("openrouter alias")
|
||||
.kind(),
|
||||
ProviderKind::Openrouter
|
||||
);
|
||||
assert_eq!(
|
||||
provider::resolve_provider("xiaomi")
|
||||
.expect("xiaomi alias")
|
||||
.kind(),
|
||||
ProviderKind::XiaomiMimo
|
||||
);
|
||||
assert_eq!(
|
||||
provider::resolve_provider("kimi")
|
||||
.expect("kimi alias")
|
||||
.kind(),
|
||||
ProviderKind::Moonshot
|
||||
);
|
||||
assert_eq!(
|
||||
provider::resolve_provider("hf")
|
||||
.expect("huggingface alias")
|
||||
.kind(),
|
||||
ProviderKind::Huggingface
|
||||
);
|
||||
|
||||
let siliconflow_cn =
|
||||
provider::resolve_provider("siliconflow-cn").expect("siliconflow-cn alias resolves");
|
||||
assert_eq!(siliconflow_cn.kind(), ProviderKind::SiliconflowCN);
|
||||
assert_eq!(siliconflow_cn.id(), "siliconflow-CN");
|
||||
assert_eq!(siliconflow_cn.provider_config_key(), "siliconflow");
|
||||
|
||||
let config = ProvidersToml::default();
|
||||
let shared_table = config.for_provider(ProviderKind::SiliconflowCN);
|
||||
assert!(std::ptr::eq(
|
||||
shared_table,
|
||||
config.for_provider(ProviderKind::Siliconflow)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn provider_metadata_defaults_match_runtime_helpers() {
|
||||
for kind in ProviderKind::ALL {
|
||||
let provider = kind.provider();
|
||||
assert_eq!(provider.default_model(), default_model_for_provider(kind));
|
||||
assert_eq!(
|
||||
provider.default_base_url(),
|
||||
default_base_url_for_provider(kind)
|
||||
);
|
||||
assert!(!provider.display_name().trim().is_empty());
|
||||
assert!(!provider.env_vars().is_empty());
|
||||
assert_eq!(provider.wire(), provider::WireFormat::ChatCompletions);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn openrouter_provider_defaults_to_canonical_endpoint_and_model() {
|
||||
let _lock = env_lock();
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
//! Built-in provider metadata.
|
||||
//!
|
||||
//! This module is a metadata foundation for collapsing provider drift over
|
||||
//! time. It deliberately does not mutate request bodies or choose fallback
|
||||
//! providers; runtime routing remains in `ConfigToml::resolve_runtime_options`.
|
||||
|
||||
use super::{
|
||||
DEFAULT_ARCEE_BASE_URL, DEFAULT_ARCEE_MODEL, DEFAULT_ATLASCLOUD_BASE_URL,
|
||||
DEFAULT_ATLASCLOUD_MODEL, DEFAULT_DEEPSEEK_BASE_URL, DEFAULT_DEEPSEEK_MODEL,
|
||||
DEFAULT_FIREWORKS_BASE_URL, DEFAULT_FIREWORKS_MODEL, DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
DEFAULT_HUGGINGFACE_MODEL, DEFAULT_MOONSHOT_BASE_URL, DEFAULT_MOONSHOT_MODEL,
|
||||
DEFAULT_NOVITA_BASE_URL, DEFAULT_NOVITA_MODEL, DEFAULT_NVIDIA_NIM_BASE_URL,
|
||||
DEFAULT_NVIDIA_NIM_MODEL, DEFAULT_OLLAMA_BASE_URL, DEFAULT_OLLAMA_MODEL,
|
||||
DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DEFAULT_OPENROUTER_BASE_URL,
|
||||
DEFAULT_OPENROUTER_MODEL, DEFAULT_SGLANG_BASE_URL, DEFAULT_SGLANG_MODEL,
|
||||
DEFAULT_SILICONFLOW_BASE_URL, DEFAULT_SILICONFLOW_CN_BASE_URL, DEFAULT_SILICONFLOW_MODEL,
|
||||
DEFAULT_VLLM_BASE_URL, DEFAULT_VLLM_MODEL, DEFAULT_VOLCENGINE_BASE_URL,
|
||||
DEFAULT_VOLCENGINE_MODEL, DEFAULT_WANJIE_ARK_BASE_URL, DEFAULT_WANJIE_ARK_MODEL,
|
||||
DEFAULT_XIAOMI_MIMO_BASE_URL, DEFAULT_XIAOMI_MIMO_MODEL, ProviderKind,
|
||||
};
|
||||
|
||||
/// Wire protocol spoken by a provider.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WireFormat {
|
||||
/// OpenAI-compatible `/v1/chat/completions` style payloads.
|
||||
ChatCompletions,
|
||||
}
|
||||
|
||||
/// Static metadata for a built-in model provider.
|
||||
pub trait Provider: Send + Sync {
|
||||
/// Provider enum variant represented by this entry.
|
||||
fn kind(&self) -> ProviderKind;
|
||||
|
||||
/// Canonical provider identifier.
|
||||
fn id(&self) -> &'static str {
|
||||
self.kind().as_str()
|
||||
}
|
||||
|
||||
/// Human-readable provider label for UIs and diagnostics.
|
||||
fn display_name(&self) -> &'static str;
|
||||
|
||||
/// Default base URL used when no config/env/CLI override is present.
|
||||
fn default_base_url(&self) -> &'static str;
|
||||
|
||||
/// Default model used when no config/env/CLI override is present.
|
||||
fn default_model(&self) -> &'static str;
|
||||
|
||||
/// Environment variable candidates used for this provider's API key.
|
||||
fn env_vars(&self) -> &'static [&'static str];
|
||||
|
||||
/// TOML table key under `[providers.<key>]`.
|
||||
fn provider_config_key(&self) -> &'static str;
|
||||
|
||||
/// Wire format used by the provider.
|
||||
fn wire(&self) -> WireFormat {
|
||||
WireFormat::ChatCompletions
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! provider {
|
||||
(
|
||||
$struct_name:ident,
|
||||
$kind:ident,
|
||||
$display_name:literal,
|
||||
$base_url:ident,
|
||||
$model:ident,
|
||||
[$($env_var:literal),* $(,)?],
|
||||
$config_key:literal
|
||||
) => {
|
||||
/// Zero-sized metadata entry for this built-in provider.
|
||||
pub struct $struct_name;
|
||||
|
||||
impl Provider for $struct_name {
|
||||
fn kind(&self) -> ProviderKind {
|
||||
ProviderKind::$kind
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &'static str {
|
||||
$display_name
|
||||
}
|
||||
|
||||
fn default_base_url(&self) -> &'static str {
|
||||
$base_url
|
||||
}
|
||||
|
||||
fn default_model(&self) -> &'static str {
|
||||
$model
|
||||
}
|
||||
|
||||
fn env_vars(&self) -> &'static [&'static str] {
|
||||
&[$($env_var),*]
|
||||
}
|
||||
|
||||
fn provider_config_key(&self) -> &'static str {
|
||||
$config_key
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
provider!(
|
||||
Deepseek,
|
||||
Deepseek,
|
||||
"DeepSeek",
|
||||
DEFAULT_DEEPSEEK_BASE_URL,
|
||||
DEFAULT_DEEPSEEK_MODEL,
|
||||
["DEEPSEEK_API_KEY"],
|
||||
"deepseek"
|
||||
);
|
||||
provider!(
|
||||
NvidiaNim,
|
||||
NvidiaNim,
|
||||
"NVIDIA NIM",
|
||||
DEFAULT_NVIDIA_NIM_BASE_URL,
|
||||
DEFAULT_NVIDIA_NIM_MODEL,
|
||||
["NVIDIA_API_KEY", "NVIDIA_NIM_API_KEY", "DEEPSEEK_API_KEY"],
|
||||
"nvidia_nim"
|
||||
);
|
||||
provider!(
|
||||
Openai,
|
||||
Openai,
|
||||
"OpenAI-compatible",
|
||||
DEFAULT_OPENAI_BASE_URL,
|
||||
DEFAULT_OPENAI_MODEL,
|
||||
["OPENAI_API_KEY"],
|
||||
"openai"
|
||||
);
|
||||
provider!(
|
||||
Atlascloud,
|
||||
Atlascloud,
|
||||
"AtlasCloud",
|
||||
DEFAULT_ATLASCLOUD_BASE_URL,
|
||||
DEFAULT_ATLASCLOUD_MODEL,
|
||||
["ATLASCLOUD_API_KEY"],
|
||||
"atlascloud"
|
||||
);
|
||||
provider!(
|
||||
WanjieArk,
|
||||
WanjieArk,
|
||||
"Wanjie Ark",
|
||||
DEFAULT_WANJIE_ARK_BASE_URL,
|
||||
DEFAULT_WANJIE_ARK_MODEL,
|
||||
[
|
||||
"WANJIE_ARK_API_KEY",
|
||||
"WANJIE_API_KEY",
|
||||
"WANJIE_MAAS_API_KEY"
|
||||
],
|
||||
"wanjie_ark"
|
||||
);
|
||||
provider!(
|
||||
Volcengine,
|
||||
Volcengine,
|
||||
"Volcengine Ark",
|
||||
DEFAULT_VOLCENGINE_BASE_URL,
|
||||
DEFAULT_VOLCENGINE_MODEL,
|
||||
[
|
||||
"VOLCENGINE_API_KEY",
|
||||
"VOLCENGINE_ARK_API_KEY",
|
||||
"ARK_API_KEY"
|
||||
],
|
||||
"volcengine"
|
||||
);
|
||||
provider!(
|
||||
Openrouter,
|
||||
Openrouter,
|
||||
"OpenRouter",
|
||||
DEFAULT_OPENROUTER_BASE_URL,
|
||||
DEFAULT_OPENROUTER_MODEL,
|
||||
["OPENROUTER_API_KEY"],
|
||||
"openrouter"
|
||||
);
|
||||
provider!(
|
||||
XiaomiMimo,
|
||||
XiaomiMimo,
|
||||
"Xiaomi MiMo",
|
||||
DEFAULT_XIAOMI_MIMO_BASE_URL,
|
||||
DEFAULT_XIAOMI_MIMO_MODEL,
|
||||
[
|
||||
"XIAOMI_MIMO_TOKEN_PLAN_API_KEY",
|
||||
"MIMO_TOKEN_PLAN_API_KEY",
|
||||
"XIAOMI_MIMO_API_KEY",
|
||||
"XIAOMI_API_KEY",
|
||||
"MIMO_API_KEY",
|
||||
],
|
||||
"xiaomi_mimo"
|
||||
);
|
||||
provider!(
|
||||
Novita,
|
||||
Novita,
|
||||
"Novita",
|
||||
DEFAULT_NOVITA_BASE_URL,
|
||||
DEFAULT_NOVITA_MODEL,
|
||||
["NOVITA_API_KEY"],
|
||||
"novita"
|
||||
);
|
||||
provider!(
|
||||
Fireworks,
|
||||
Fireworks,
|
||||
"Fireworks",
|
||||
DEFAULT_FIREWORKS_BASE_URL,
|
||||
DEFAULT_FIREWORKS_MODEL,
|
||||
["FIREWORKS_API_KEY"],
|
||||
"fireworks"
|
||||
);
|
||||
provider!(
|
||||
Siliconflow,
|
||||
Siliconflow,
|
||||
"SiliconFlow",
|
||||
DEFAULT_SILICONFLOW_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_MODEL,
|
||||
["SILICONFLOW_API_KEY"],
|
||||
"siliconflow"
|
||||
);
|
||||
provider!(
|
||||
SiliconflowCN,
|
||||
SiliconflowCN,
|
||||
"SiliconFlow CN",
|
||||
DEFAULT_SILICONFLOW_CN_BASE_URL,
|
||||
DEFAULT_SILICONFLOW_MODEL,
|
||||
["SILICONFLOW_API_KEY"],
|
||||
"siliconflow"
|
||||
);
|
||||
provider!(
|
||||
Arcee,
|
||||
Arcee,
|
||||
"Arcee",
|
||||
DEFAULT_ARCEE_BASE_URL,
|
||||
DEFAULT_ARCEE_MODEL,
|
||||
["ARCEE_API_KEY"],
|
||||
"arcee"
|
||||
);
|
||||
provider!(
|
||||
Moonshot,
|
||||
Moonshot,
|
||||
"Moonshot",
|
||||
DEFAULT_MOONSHOT_BASE_URL,
|
||||
DEFAULT_MOONSHOT_MODEL,
|
||||
["MOONSHOT_API_KEY", "KIMI_API_KEY"],
|
||||
"moonshot"
|
||||
);
|
||||
provider!(
|
||||
Sglang,
|
||||
Sglang,
|
||||
"SGLang",
|
||||
DEFAULT_SGLANG_BASE_URL,
|
||||
DEFAULT_SGLANG_MODEL,
|
||||
["SGLANG_API_KEY"],
|
||||
"sglang"
|
||||
);
|
||||
provider!(
|
||||
Vllm,
|
||||
Vllm,
|
||||
"vLLM",
|
||||
DEFAULT_VLLM_BASE_URL,
|
||||
DEFAULT_VLLM_MODEL,
|
||||
["VLLM_API_KEY"],
|
||||
"vllm"
|
||||
);
|
||||
provider!(
|
||||
Ollama,
|
||||
Ollama,
|
||||
"Ollama",
|
||||
DEFAULT_OLLAMA_BASE_URL,
|
||||
DEFAULT_OLLAMA_MODEL,
|
||||
["OLLAMA_API_KEY"],
|
||||
"ollama"
|
||||
);
|
||||
provider!(
|
||||
Huggingface,
|
||||
Huggingface,
|
||||
"Hugging Face",
|
||||
DEFAULT_HUGGINGFACE_BASE_URL,
|
||||
DEFAULT_HUGGINGFACE_MODEL,
|
||||
["HUGGINGFACE_API_KEY", "HF_TOKEN"],
|
||||
"huggingface"
|
||||
);
|
||||
|
||||
static DEEPSEEK: Deepseek = Deepseek;
|
||||
static NVIDIA_NIM: NvidiaNim = NvidiaNim;
|
||||
static OPENAI: Openai = Openai;
|
||||
static ATLASCLOUD: Atlascloud = Atlascloud;
|
||||
static WANJIE_ARK: WanjieArk = WanjieArk;
|
||||
static VOLCENGINE: Volcengine = Volcengine;
|
||||
static OPENROUTER: Openrouter = Openrouter;
|
||||
static XIAOMI_MIMO: XiaomiMimo = XiaomiMimo;
|
||||
static NOVITA: Novita = Novita;
|
||||
static FIREWORKS: Fireworks = Fireworks;
|
||||
static SILICONFLOW: Siliconflow = Siliconflow;
|
||||
static SILICONFLOW_CN: SiliconflowCN = SiliconflowCN;
|
||||
static ARCEE: Arcee = Arcee;
|
||||
static MOONSHOT: Moonshot = Moonshot;
|
||||
static SGLANG: Sglang = Sglang;
|
||||
static VLLM: Vllm = Vllm;
|
||||
static OLLAMA: Ollama = Ollama;
|
||||
static HUGGINGFACE: Huggingface = Huggingface;
|
||||
|
||||
static PROVIDER_REGISTRY: [&dyn Provider; 18] = [
|
||||
&DEEPSEEK,
|
||||
&NVIDIA_NIM,
|
||||
&OPENAI,
|
||||
&ATLASCLOUD,
|
||||
&WANJIE_ARK,
|
||||
&VOLCENGINE,
|
||||
&OPENROUTER,
|
||||
&XIAOMI_MIMO,
|
||||
&NOVITA,
|
||||
&FIREWORKS,
|
||||
&SILICONFLOW,
|
||||
&SILICONFLOW_CN,
|
||||
&ARCEE,
|
||||
&MOONSHOT,
|
||||
&SGLANG,
|
||||
&VLLM,
|
||||
&OLLAMA,
|
||||
&HUGGINGFACE,
|
||||
];
|
||||
|
||||
/// Return all built-in provider metadata entries in `ProviderKind::ALL` order.
|
||||
#[must_use]
|
||||
pub fn all_providers() -> &'static [&'static dyn Provider] {
|
||||
&PROVIDER_REGISTRY
|
||||
}
|
||||
|
||||
/// Find a provider by canonical id only.
|
||||
#[must_use]
|
||||
pub fn lookup_provider(id: &str) -> Option<&'static dyn Provider> {
|
||||
let id = id.trim();
|
||||
all_providers()
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|provider| provider.id() == id)
|
||||
}
|
||||
|
||||
/// Resolve a provider by canonical id or supported legacy alias.
|
||||
#[must_use]
|
||||
pub fn resolve_provider(id_or_alias: &str) -> Option<&'static dyn Provider> {
|
||||
ProviderKind::parse(id_or_alias).map(provider_for_kind)
|
||||
}
|
||||
|
||||
/// Return metadata for a known provider kind.
|
||||
#[must_use]
|
||||
pub fn provider_for_kind(kind: ProviderKind) -> &'static dyn Provider {
|
||||
match kind {
|
||||
ProviderKind::Deepseek => &DEEPSEEK,
|
||||
ProviderKind::NvidiaNim => &NVIDIA_NIM,
|
||||
ProviderKind::Openai => &OPENAI,
|
||||
ProviderKind::Atlascloud => &ATLASCLOUD,
|
||||
ProviderKind::WanjieArk => &WANJIE_ARK,
|
||||
ProviderKind::Volcengine => &VOLCENGINE,
|
||||
ProviderKind::Openrouter => &OPENROUTER,
|
||||
ProviderKind::XiaomiMimo => &XIAOMI_MIMO,
|
||||
ProviderKind::Novita => &NOVITA,
|
||||
ProviderKind::Fireworks => &FIREWORKS,
|
||||
ProviderKind::Siliconflow => &SILICONFLOW,
|
||||
ProviderKind::SiliconflowCN => &SILICONFLOW_CN,
|
||||
ProviderKind::Arcee => &ARCEE,
|
||||
ProviderKind::Moonshot => &MOONSHOT,
|
||||
ProviderKind::Sglang => &SGLANG,
|
||||
ProviderKind::Vllm => &VLLM,
|
||||
ProviderKind::Ollama => &OLLAMA,
|
||||
ProviderKind::Huggingface => &HUGGINGFACE,
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
were previously unpriced: `mimo-v2.5-pro` / `xiaomi/mimo-v2.5-pro` reuse the
|
||||
DeepSeek V4-Pro rate table and `mimo-v2.5` / `xiaomi/mimo-v2.5` reuse the
|
||||
DeepSeek V4-Flash rates. Existing DeepSeek pricing is unchanged (#2731, #2750).
|
||||
- Added a metadata-only `codewhale-config` provider registry with canonical
|
||||
lookup, alias-aware resolution, provider defaults, config-table keys, and
|
||||
API-key env candidates. Runtime routing remains unchanged and fallback
|
||||
providers stay dormant; this harvests the safe provider-trait foundation from
|
||||
#2479 toward #2075. Thanks @sximelon.
|
||||
- Added optional `[search].base_url` / `CODEWHALE_SEARCH_BASE_URL` support for
|
||||
DuckDuckGo-compatible private search endpoints, while keeping
|
||||
`DEEPSEEK_SEARCH_BASE_URL` as a legacy alias. Custom endpoints are gated by
|
||||
|
||||
Reference in New Issue
Block a user