use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use crate::auth::oauth::{anthropic_oauth, github_copilot_oauth, openai_codex_oauth};
use crate::auth::{AuthResolveInput, AuthResult, ModelAuth, ProviderAuth, env_api_key_auth};
use crate::models::catalog::*;
use crate::models::{
CreateModelsOptions, CreateProviderOptions, MutableModels, Provider, ProviderApi, create_models, create_provider,
};
use crate::providers::adapter::{
anthropic_messages_api, azure_openai_responses_api, bedrock_converse_stream_api, google_generative_ai_api,
google_vertex_api, mistral_conversations_api, mixed_openai_apis, openai_codex_responses_api,
openai_completions_api, openai_responses_api,
};
use crate::providers::cloudflare_auth::{cloudflare_ai_gateway_auth, cloudflare_workers_ai_auth};
macro_rules! simple_provider {
($id:expr, $name:expr, $models:expr, $api:expr, $env:expr) => {
create_provider(CreateProviderOptions {
id: $id.to_string(),
name: Some($name.to_string()),
base_url: None,
headers: None,
auth: ProviderAuth {
api_key: Some(env_api_key_auth($env.1, $env.0.to_vec())),
oauth: None,
},
models: $models.to_vec(),
refresh_models: None,
api: ProviderApi::Single($api()),
})
};
}
pub fn amazon_bedrock_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "amazon-bedrock".to_string(),
name: Some("Amazon Bedrock".to_string()),
base_url: None,
headers: None,
auth: ProviderAuth {
api_key: Some(bedrock_auth()),
oauth: None,
},
models: AMAZON_BEDROCK_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Single(bedrock_converse_stream_api()),
})
}
fn bedrock_auth() -> crate::auth::ApiKeyAuth {
crate::auth::ApiKeyAuth {
name: "AWS credentials".to_string(),
resolve: Arc::new(|input: AuthResolveInput| {
Box::pin(async move {
if let Some(key) = input.credential.and_then(|c| c.key) {
return Some(AuthResult {
auth: ModelAuth {
api_key: Some(key),
headers: None,
base_url: None,
},
env: None,
source: Some("stored credential".to_string()),
});
}
let checks = [
"AWS_BEARER_TOKEN_BEDROCK",
"AWS_PROFILE",
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI",
"AWS_CONTAINER_CREDENTIALS_FULL_URI",
"AWS_WEB_IDENTITY_TOKEN_FILE",
];
for var in checks {
if input.ctx.env(var).await.is_some() {
return Some(AuthResult {
auth: ModelAuth {
api_key: None,
headers: None,
base_url: None,
},
env: None,
source: Some(var.to_string()),
});
}
}
let has_key = input.ctx.env("AWS_ACCESS_KEY_ID").await.is_some()
&& input.ctx.env("AWS_SECRET_ACCESS_KEY").await.is_some();
if has_key {
return Some(AuthResult {
auth: ModelAuth {
api_key: None,
headers: None,
base_url: None,
},
env: None,
source: Some("AWS access keys".to_string()),
});
}
None
}) as Pin<Box<dyn Future<Output = Option<AuthResult>> + Send>>
}),
login: None,
}
}
pub fn google_vertex_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "google-vertex".to_string(),
name: Some("Google Vertex AI".to_string()),
base_url: None,
headers: None,
auth: ProviderAuth {
api_key: Some(vertex_auth()),
oauth: None,
},
models: GOOGLE_VERTEX_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Single(google_vertex_api()),
})
}
fn vertex_auth() -> crate::auth::ApiKeyAuth {
const ADC_PATH: &str = "~/.config/gcloud/application_default_credentials.json";
crate::auth::ApiKeyAuth {
name: "Google Cloud credentials".to_string(),
resolve: Arc::new(|input: AuthResolveInput| {
Box::pin(async move {
let had_credential = input.credential.is_some();
let key = input
.credential
.and_then(|c| c.key)
.or(input.ctx.env("GOOGLE_CLOUD_API_KEY").await);
if let Some(key) = key {
return Some(AuthResult {
auth: ModelAuth {
api_key: Some(key),
headers: None,
base_url: None,
},
env: None,
source: Some(if had_credential {
"stored credential".to_string()
} else {
"GOOGLE_CLOUD_API_KEY".to_string()
}),
});
}
let adc = input
.ctx
.env("GOOGLE_APPLICATION_CREDENTIALS")
.await
.unwrap_or_else(|| ADC_PATH.to_string());
let has_credentials = input.ctx.file_exists(&adc).await;
let has_project = input.ctx.env("GOOGLE_CLOUD_PROJECT").await.is_some()
|| input.ctx.env("GCLOUD_PROJECT").await.is_some();
let has_location = input.ctx.env("GOOGLE_CLOUD_LOCATION").await.is_some();
if has_credentials && has_project && has_location {
return Some(AuthResult {
auth: ModelAuth {
api_key: None,
headers: None,
base_url: None,
},
env: None,
source: Some("gcloud application default credentials".to_string()),
});
}
None
}) as Pin<Box<dyn Future<Output = Option<AuthResult>> + Send>>
}),
login: None,
}
}
pub fn anthropic_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "anthropic".to_string(),
name: Some("Anthropic".to_string()),
base_url: Some("https://api.anthropic.com".to_string()),
headers: None,
auth: ProviderAuth {
api_key: Some(env_api_key_auth(
"Anthropic API key",
vec!["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
)),
oauth: Some(anthropic_oauth()),
},
models: ANTHROPIC_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Single(anthropic_messages_api()),
})
}
pub fn openai_codex_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "openai-codex".to_string(),
name: Some("OpenAI Codex".to_string()),
base_url: Some("https://chatgpt.com/backend-api".to_string()),
headers: None,
auth: ProviderAuth {
api_key: None,
oauth: Some(openai_codex_oauth()),
},
models: OPENAI_CODEX_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Single(openai_codex_responses_api()),
})
}
pub fn openai_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "openai".to_string(),
name: Some("OpenAI".to_string()),
base_url: Some("https://api.openai.com/v1".to_string()),
headers: None,
auth: ProviderAuth {
api_key: Some(env_api_key_auth("OpenAI API key", vec!["OPENAI_API_KEY"])),
oauth: None,
},
models: OPENAI_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Single(openai_responses_api()),
})
}
pub fn github_copilot_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "github-copilot".to_string(),
name: Some("GitHub Copilot".to_string()),
base_url: Some("https://api.individual.githubcopilot.com".to_string()),
headers: None,
auth: ProviderAuth {
api_key: Some(env_api_key_auth("GitHub Copilot token", vec!["COPILOT_GITHUB_TOKEN"])),
oauth: Some(github_copilot_oauth()),
},
models: GITHUB_COPILOT_MODELS.to_vec(),
refresh_models: None,
api: mixed_openai_apis(),
})
}
pub fn cloudflare_ai_gateway_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "cloudflare-ai-gateway".to_string(),
name: Some("Cloudflare AI Gateway".to_string()),
base_url: None,
headers: None,
auth: ProviderAuth {
api_key: Some(cloudflare_ai_gateway_auth()),
oauth: None,
},
models: CLOUDFLARE_AI_GATEWAY_MODELS.to_vec(),
refresh_models: None,
api: mixed_openai_apis(),
})
}
pub fn cloudflare_workers_ai_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "cloudflare-workers-ai".to_string(),
name: Some("Cloudflare Workers AI".to_string()),
base_url: None,
headers: None,
auth: ProviderAuth {
api_key: Some(cloudflare_workers_ai_auth()),
oauth: None,
},
models: CLOUDFLARE_WORKERS_AI_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Single(openai_completions_api()),
})
}
pub fn fireworks_provider() -> Provider {
let mut map = HashMap::new();
map.insert("anthropic-messages".to_string(), anthropic_messages_api());
map.insert("openai-completions".to_string(), openai_completions_api());
create_provider(CreateProviderOptions {
id: "fireworks".to_string(),
name: Some("Fireworks".to_string()),
base_url: None,
headers: None,
auth: ProviderAuth {
api_key: Some(env_api_key_auth("Fireworks API key", vec!["FIREWORKS_API_KEY"])),
oauth: None,
},
models: FIREWORKS_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Map(map),
})
}
pub fn kimi_coding_provider() -> Provider {
create_provider(CreateProviderOptions {
id: "kimi-coding".to_string(),
name: Some("Kimi For Coding".to_string()),
base_url: None,
headers: None,
auth: ProviderAuth {
api_key: Some(env_api_key_auth("Moonshot API key", vec!["MOONSHOT_API_KEY"])),
oauth: None,
},
models: KIMI_CODING_MODELS.to_vec(),
refresh_models: None,
api: ProviderApi::Single(anthropic_messages_api()),
})
}
pub fn builtin_providers() -> Vec<Provider> {
vec![
amazon_bedrock_provider(),
simple_provider!(
"ant-ling",
"Ant Ling",
ANT_LING_MODELS,
openai_completions_api,
(vec!["ANT_LING_API_KEY"], "Ant Ling API key")
),
anthropic_provider(),
simple_provider!(
"azure-openai-responses",
"Azure OpenAI",
AZURE_OPENAI_RESPONSES_MODELS,
azure_openai_responses_api,
(vec!["AZURE_OPENAI_API_KEY"], "Azure OpenAI API key")
),
simple_provider!(
"cerebras",
"Cerebras",
CEREBRAS_MODELS,
openai_completions_api,
(vec!["CEREBRAS_API_KEY"], "Cerebras API key")
),
cloudflare_ai_gateway_provider(),
cloudflare_workers_ai_provider(),
simple_provider!(
"deepseek",
"DeepSeek",
DEEPSEEK_MODELS,
openai_completions_api,
(vec!["DEEPSEEK_API_KEY"], "DeepSeek API key")
),
fireworks_provider(),
github_copilot_provider(),
simple_provider!(
"google",
"Google",
GOOGLE_MODELS,
google_generative_ai_api,
(vec!["GEMINI_API_KEY"], "Gemini API key")
),
google_vertex_provider(),
simple_provider!(
"groq",
"Groq",
GROQ_MODELS,
openai_completions_api,
(vec!["GROQ_API_KEY"], "Groq API key")
),
simple_provider!(
"huggingface",
"Hugging Face",
HUGGINGFACE_MODELS,
openai_completions_api,
(vec!["HF_TOKEN"], "Hugging Face token")
),
kimi_coding_provider(),
simple_provider!(
"minimax",
"MiniMax",
MINIMAX_MODELS,
openai_completions_api,
(vec!["MINIMAX_API_KEY"], "MiniMax API key")
),
simple_provider!(
"minimax-cn",
"MiniMax (China)",
MINIMAX_CN_MODELS,
openai_completions_api,
(vec!["MINIMAX_API_KEY"], "MiniMax API key")
),
simple_provider!(
"mistral",
"Mistral",
MISTRAL_MODELS,
mistral_conversations_api,
(vec!["MISTRAL_API_KEY"], "Mistral API key")
),
simple_provider!(
"moonshotai",
"Moonshot AI",
MOONSHOTAI_MODELS,
openai_completions_api,
(vec!["MOONSHOT_API_KEY"], "Moonshot API key")
),
simple_provider!(
"moonshotai-cn",
"Moonshot AI (China)",
MOONSHOTAI_CN_MODELS,
openai_completions_api,
(vec!["MOONSHOT_API_KEY"], "Moonshot API key")
),
simple_provider!(
"nvidia",
"NVIDIA NIM",
NVIDIA_MODELS,
openai_completions_api,
(vec!["NVIDIA_API_KEY"], "NVIDIA API key")
),
openai_provider(),
openai_codex_provider(),
simple_provider!(
"opencode",
"OpenCode Zen",
OPENCODE_MODELS,
openai_completions_api,
(vec!["OPENCODE_API_KEY"], "OpenCode API key")
),
simple_provider!(
"opencode-go",
"OpenCode Go",
OPENCODE_GO_MODELS,
openai_completions_api,
(vec!["OPENCODE_API_KEY"], "OpenCode API key")
),
simple_provider!(
"openrouter",
"OpenRouter",
OPENROUTER_MODELS,
openai_completions_api,
(vec!["OPENROUTER_API_KEY"], "OpenRouter API key")
),
simple_provider!(
"together",
"Together AI",
TOGETHER_MODELS,
openai_completions_api,
(vec!["TOGETHER_API_KEY"], "Together API key")
),
simple_provider!(
"vercel-ai-gateway",
"Vercel AI Gateway",
VERCEL_AI_GATEWAY_MODELS,
openai_completions_api,
(vec!["VERCEL_AI_GATEWAY_API_KEY"], "Vercel AI Gateway API key")
),
simple_provider!(
"xai",
"xAI",
XAI_MODELS,
openai_completions_api,
(vec!["XAI_API_KEY"], "xAI API key")
),
simple_provider!(
"xiaomi",
"Xiaomi MiMo",
XIAOMI_MODELS,
openai_completions_api,
(vec!["XIAOMI_API_KEY"], "Xiaomi API key")
),
simple_provider!(
"xiaomi-token-plan-ams",
"Xiaomi Token Plan (AMS)",
XIAOMI_TOKEN_PLAN_AMS_MODELS,
anthropic_messages_api,
(vec!["XIAOMI_API_KEY"], "Xiaomi API key")
),
simple_provider!(
"xiaomi-token-plan-cn",
"Xiaomi Token Plan (CN)",
XIAOMI_TOKEN_PLAN_CN_MODELS,
anthropic_messages_api,
(vec!["XIAOMI_API_KEY"], "Xiaomi API key")
),
simple_provider!(
"xiaomi-token-plan-sgp",
"Xiaomi Token Plan (SGP)",
XIAOMI_TOKEN_PLAN_SGP_MODELS,
anthropic_messages_api,
(vec!["XIAOMI_API_KEY"], "Xiaomi API key")
),
simple_provider!(
"zai",
"ZAI Coding Plan",
ZAI_MODELS,
openai_completions_api,
(vec!["ZAI_API_KEY"], "ZAI API key")
),
simple_provider!(
"zai-coding-cn",
"ZAI Coding Plan (China)",
ZAI_CODING_CN_MODELS,
openai_completions_api,
(vec!["ZAI_API_KEY"], "ZAI API key")
),
]
}
pub fn builtin_models(options: Option<CreateModelsOptions>) -> MutableModels {
let mut models = create_models(options);
for provider in builtin_providers() {
models.set_provider(provider);
}
models
}
pub use crate::models::catalog::{get_builtin_model, get_builtin_models, get_builtin_providers};