Add Kimi K3 via OpenRouter

This commit is contained in:
Bryan Helmkamp 2026-07-22 16:59:25 -04:00
parent 03b1b790d1
commit 129c893c86
No known key found for this signature in database
3 changed files with 132 additions and 1 deletions

View file

@ -5,7 +5,10 @@
use std::sync::Arc;
use fabro_auth::ApiCredential;
use fabro_llm::client::Client;
use fabro_llm::error::ProviderErrorKind;
use fabro_llm::model_test::{ModelTestStatus, run_model_test};
use fabro_llm::provider::ProviderAdapter;
use fabro_llm::providers::{
AnthropicAdapter, BedrockAdapter, GeminiAdapter, OpenAiAdapter, OpenAiCompatibleAdapter,
@ -13,7 +16,8 @@ use fabro_llm::providers::{
use fabro_llm::types::{
CostSource, FinishReason, Message, ReasoningEffort, Request, ToolChoice, ToolDefinition,
};
use fabro_model::Catalog;
use fabro_model::catalog::{LlmCatalogSettings, ProviderCatalogSettings};
use fabro_model::{Catalog, ModelTestMode, ProviderId};
use fabro_static::EnvVars;
fn make_request(model: &str) -> Request {
@ -332,6 +336,42 @@ async fn openrouter_complete() {
assert_eq!(response.cost_source, Some(CostSource::Authoritative));
}
#[fabro_macros::e2e_test(live("OPENROUTER_API_KEY"))]
async fn openrouter_kimi_k3_deep_tool_round_trip() {
let api_key =
std::env::var(EnvVars::OPENROUTER_API_KEY).expect("OPENROUTER_API_KEY must be set");
let provider = ProviderId::new("openrouter");
let mut settings = LlmCatalogSettings::default();
settings
.providers
.insert(provider.to_string(), ProviderCatalogSettings {
enabled: Some(true),
..ProviderCatalogSettings::default()
});
let catalog = Arc::new(
Catalog::from_builtin_with_overrides(&settings)
.expect("enabled OpenRouter catalog should build"),
);
let credential = ApiCredential::from_api_key(provider, api_key, &catalog)
.expect("OpenRouter credential should resolve from the catalog");
let client = Arc::new(
Client::from_credentials(vec![credential], Arc::clone(&catalog))
.await
.expect("OpenRouter client should build from the catalog"),
);
let model = catalog
.get("moonshotai/kimi-k3")
.expect("OpenRouter Kimi K3 should be present");
let outcome = run_model_test(model, ModelTestMode::Deep, client).await;
assert_eq!(
outcome.status,
ModelTestStatus::Ok,
"OpenRouter Kimi K3 deep test failed: {:?}",
outcome.error_message
);
}
async fn run_multi_turn_cache_test(
adapter: &dyn ProviderAdapter,
model: &str,

View file

@ -2056,6 +2056,71 @@ enabled = true
);
}
#[test]
fn builtin_openrouter_includes_kimi_k3_when_enabled() {
let catalog = Catalog::from_builtin_with_overrides(&minimal_settings(
r"
[providers.openrouter]
enabled = true
",
))
.expect("enabled OpenRouter override should build from the built-in provider settings");
let model = catalog
.get("moonshotai/kimi-k3")
.expect("OpenRouter Kimi K3 should be present");
insta::assert_debug_snapshot!(model, @r#"
Model {
id: "moonshotai/kimi-k3",
provider: openrouter,
family: "kimi-k3",
display_name: "Kimi K3 (via OpenRouter)",
limits: ModelLimits {
context_window: 1048576,
max_output: Some(
131072,
),
},
training: None,
knowledge_cutoff: None,
features: ModelFeatures {
tools: true,
vision: true,
reasoning: true,
reasoning_effort: AlwaysAdaptive,
prompt_cache: true,
sampling_params: false,
},
costs: ModelCosts {
input_cost_per_mtok: Some(
3.0,
),
output_cost_per_mtok: Some(
15.0,
),
cache_input_cost_per_mtok: Some(
0.3,
),
},
estimated_output_tps: None,
aliases: [],
default: false,
small_default: false,
configured: false,
}
"#);
let settings = catalog
.model_settings("moonshotai/kimi-k3")
.expect("OpenRouter Kimi K3 settings should be present");
assert_eq!(settings.api_id, "moonshotai/kimi-k3");
assert_eq!(settings.controls.reasoning_effort, vec![
ReasoningEffort::Low,
ReasoningEffort::High,
ReasoningEffort::Max,
]);
}
#[test]
fn builtin_ollama_provider_is_opt_in() {
let ollama = ProviderId::new("ollama");

View file

@ -278,6 +278,32 @@ reasoning = false
input_cost_per_mtok = 0.73
output_cost_per_mtok = 3.49
[models."moonshotai/kimi-k3"]
provider = "openrouter"
api_id = "moonshotai/kimi-k3"
display_name = "Kimi K3 (via OpenRouter)"
family = "kimi-k3"
[models."moonshotai/kimi-k3".limits]
context_window = 1048576
max_output = 131072
[models."moonshotai/kimi-k3".features]
tools = true
vision = true
reasoning = true
reasoning_effort = "always_adaptive"
prompt_cache = true
sampling_params = false
[models."moonshotai/kimi-k3".controls]
reasoning_effort = ["low", "high", "max"]
[models."moonshotai/kimi-k3".costs]
input_cost_per_mtok = 3.0
output_cost_per_mtok = 15.0
cache_input_cost_per_mtok = 0.3
[models."qwen/qwen3-coder"]
provider = "openrouter"
api_id = "qwen/qwen3-coder"