use std::env;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Flavor {
OpenAi,
Anthropic,
}
#[derive(Clone, Debug)]
pub struct PresetModel {
pub id: &'static str,
pub display_name: &'static str,
pub context_window: usize,
pub max_output_tokens: Option<u64>,
pub supports_images: bool,
}
#[derive(Clone, Debug)]
pub struct BackendModel {
pub upstream: String,
pub display: String,
pub context_window: usize,
pub max_output_tokens: Option<u64>,
pub supports_images: bool,
}
#[derive(Clone, Debug)]
pub struct Preset {
pub id: &'static str,
pub prefix: &'static str,
pub key_envs: &'static [&'static str],
pub base_envs: &'static [&'static str],
pub default_base_url: Option<&'static str>,
pub flavor: Flavor,
pub models: &'static [PresetModel],
pub no_auth: bool,
pub oauth_subscription: bool,
pub discover: bool,
}
pub const PRESETS: &[Preset] = &[
Preset {
id: "openai",
prefix: "OPENAI",
key_envs: &["OPENAI_API_KEY"],
base_envs: &["OPENAI_BASE_URL"],
default_base_url: Some("https://api.openai.com/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::OPENAI,
},
Preset {
id: "anthropic",
prefix: "ANTHROPIC",
key_envs: &[
"ANTHROPIC_API_KEY",
"ANTHROPIC_AUTH_TOKEN",
"ANTHROPIC_OAUTH_TOKEN",
],
base_envs: &["ANTHROPIC_BASE_URL"],
default_base_url: Some("https://api.anthropic.com/v1"),
flavor: Flavor::Anthropic,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::ANTHROPIC,
},
Preset {
id: "claude-oauth",
prefix: "CLAUDE_OAUTH",
key_envs: &[],
base_envs: &["CLAUDE_OAUTH_BASE_URL"],
default_base_url: Some("https://api.anthropic.com"),
flavor: Flavor::Anthropic,
no_auth: false,
oauth_subscription: true,
discover: false,
models: crate::catalog_generated::CLAUDE_OAUTH,
},
Preset {
id: "deepseek",
prefix: "DEEPSEEK",
key_envs: &["DEEPSEEK_API_KEY"],
base_envs: &["DEEPSEEK_BASE_URL"],
default_base_url: Some("https://api.deepseek.com/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::DEEPSEEK,
},
Preset {
id: "groq",
prefix: "GROQ",
key_envs: &["GROQ_API_KEY"],
base_envs: &["GROQ_BASE_URL"],
default_base_url: Some("https://api.groq.com/openai/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::GROQ,
},
Preset {
id: "cerebras",
prefix: "CEREBRAS",
key_envs: &["CEREBRAS_API_KEY"],
base_envs: &["CEREBRAS_BASE_URL"],
default_base_url: Some("https://api.cerebras.ai/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::CEREBRAS,
},
Preset {
id: "xai",
prefix: "XAI",
key_envs: &["XAI_API_KEY"],
base_envs: &["XAI_BASE_URL"],
default_base_url: Some("https://api.x.ai/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::XAI,
},
Preset {
id: "mistral",
prefix: "MISTRAL",
key_envs: &["MISTRAL_API_KEY"],
base_envs: &["MISTRAL_BASE_URL"],
default_base_url: Some("https://api.mistral.ai/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::MISTRAL,
},
Preset {
id: "gemini",
prefix: "GEMINI",
key_envs: &[
"GEMINI_API_KEY",
"GOOGLE_GENERATIVE_AI_API_KEY",
"GOOGLE_API_KEY",
],
base_envs: &["GEMINI_BASE_URL", "GOOGLE_GENERATIVE_AI_BASE_URL"],
default_base_url: Some("https://generativelanguage.googleapis.com/v1beta/openai/"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::GEMINI,
},
Preset {
id: "together",
prefix: "TOGETHER",
key_envs: &["TOGETHER_API_KEY"],
base_envs: &["TOGETHER_BASE_URL"],
default_base_url: Some("https://api.together.xyz/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::TOGETHER,
},
Preset {
id: "fireworks",
prefix: "FIREWORKS",
key_envs: &["FIREWORKS_API_KEY"],
base_envs: &["FIREWORKS_BASE_URL"],
default_base_url: Some("https://api.fireworks.ai/inference/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::FIREWORKS,
},
Preset {
id: "openrouter",
prefix: "OPENROUTER",
key_envs: &["OPENROUTER_API_KEY"],
base_envs: &["OPENROUTER_BASE_URL"],
default_base_url: Some("https://openrouter.ai/api/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::OPENROUTER,
},
Preset {
id: "nvidia",
prefix: "NVIDIA",
key_envs: &["NVIDIA_API_KEY"],
base_envs: &["NVIDIA_BASE_URL"],
default_base_url: Some("https://integrate.api.nvidia.com/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::NVIDIA,
},
Preset {
id: "moonshot",
prefix: "MOONSHOT",
key_envs: &["MOONSHOT_API_KEY", "KIMI_API_KEY"],
base_envs: &["MOONSHOT_BASE_URL", "KIMI_BASE_URL"],
default_base_url: Some("https://api.moonshot.ai/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::MOONSHOT,
},
Preset {
id: "zai",
prefix: "ZAI",
key_envs: &["ZAI_API_KEY"],
base_envs: &["ZAI_BASE_URL"],
default_base_url: Some("https://api.z.ai/api/coding/paas/v4"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::ZAI,
},
Preset {
id: "minimax",
prefix: "MINIMAX",
key_envs: &["MINIMAX_API_KEY", "MINIMAX_CN_API_KEY"],
base_envs: &["MINIMAX_BASE_URL"],
default_base_url: Some("https://api.minimax.io/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::MINIMAX,
},
Preset {
id: "qwen",
prefix: "QWEN",
key_envs: &[
"QWEN_TOKEN_PLAN_API_KEY",
"QWEN_TOKEN_PLAN_CN_API_KEY",
"DASHSCOPE_API_KEY",
],
base_envs: &["QWEN_BASE_URL", "DASHSCOPE_BASE_URL"],
default_base_url: Some("https://dashscope-intl.aliyuncs.com/compatible-mode/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::QWEN,
},
Preset {
id: "baseten",
prefix: "BASETEN",
key_envs: &["BASETEN_API_KEY"],
base_envs: &["BASETEN_BASE_URL"],
default_base_url: None, flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: &[],
},
Preset {
id: "huggingface",
prefix: "HF",
key_envs: &["HF_TOKEN", "HUGGINGFACE_API_KEY"],
base_envs: &["HF_BASE_URL"],
default_base_url: Some("https://router.huggingface.co/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::HUGGINGFACE,
},
Preset {
id: "vercel-gateway",
prefix: "AI_GATEWAY",
key_envs: &["AI_GATEWAY_API_KEY"],
base_envs: &["AI_GATEWAY_BASE_URL"],
default_base_url: Some("https://ai-gateway.vercel.sh/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::VERCEL_GATEWAY,
},
Preset {
id: "opencode",
prefix: "OPENCODE",
key_envs: &["OPENCODE_API_KEY"],
base_envs: &["OPENCODE_BASE_URL"],
default_base_url: Some("https://opencode.ai/inference/openai/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: true,
models: crate::catalog_generated::OPENCODE,
},
Preset {
id: "opencode-go",
prefix: "OPENCODE_GO",
key_envs: &["OPENCODE_API_KEY"],
base_envs: &["OPENCODE_GO_BASE_URL"],
default_base_url: Some("https://opencode.ai/inference/go/openai/v1"),
flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: true,
models: crate::catalog_generated::OPENCODE_GO,
},
Preset {
id: "azure",
prefix: "AZURE_OPENAI",
key_envs: &["AZURE_OPENAI_API_KEY", "AZURE_API_KEY"],
base_envs: &["AZURE_OPENAI_BASE_URL", "AZURE_OPENAI_ENDPOINT"],
default_base_url: None, flavor: Flavor::OpenAi,
no_auth: false,
oauth_subscription: false,
discover: false,
models: crate::catalog_generated::AZURE,
},
Preset {
id: "ollama",
prefix: "OLLAMA",
key_envs: &[],
base_envs: &["OLLAMA_HOST", "OLLAMA_BASE_URL"],
default_base_url: Some("http://127.0.0.1:11434/v1"),
flavor: Flavor::OpenAi,
models: &[PresetModel {
id: "qwen3-coder:32b",
display_name: "Qwen3 Coder 32B (Ollama)",
context_window: 128_000,
max_output_tokens: Some(32_000),
supports_images: true,
}],
no_auth: true,
oauth_subscription: false,
discover: true,
},
Preset {
id: "lmstudio",
prefix: "LMSTUDIO",
key_envs: &[],
base_envs: &["LMSTUDIO_BASE_URL"],
default_base_url: Some("http://127.0.0.1:1234/v1"),
flavor: Flavor::OpenAi,
models: &[],
no_auth: true,
oauth_subscription: false,
discover: true,
},
Preset {
id: "vllm",
prefix: "VLLM",
key_envs: &[],
base_envs: &["VLLM_BASE_URL"],
default_base_url: Some("http://127.0.0.1:8000/v1"),
flavor: Flavor::OpenAi,
models: &[],
no_auth: true,
oauth_subscription: false,
discover: true,
},
];
#[derive(Clone, Debug)]
pub struct ResolvedBackend {
pub preset_id: &'static str,
pub flavor: Flavor,
pub base_url: String,
pub api_key: String,
pub models: Vec<BackendModel>,
pub oauth: Option<crate::oauth::OAuthSessionHandle>,
pub discover: bool,
pub extra_headers: Vec<(String, String)>,
}
fn non_empty(var: &str) -> Option<String> {
env::var(var)
.ok()
.map(|v| v.trim().to_owned())
.filter(|v| !v.is_empty())
}
fn first_set(vars: &[&str]) -> Option<String> {
vars.iter().find_map(|v| non_empty(v))
}
fn model_overrides(prefix: &str) -> Option<Vec<String>> {
if let Some(list) = non_empty(&format!("{prefix}_MODELS")) {
let ids: Vec<String> = list
.split(',')
.map(|s| s.trim().to_owned())
.filter(|s| !s.is_empty())
.collect();
if !ids.is_empty() {
return Some(ids);
}
}
non_empty(&format!("{prefix}_MODEL")).map(|m| vec![m])
}
fn display_for(id: &str) -> String {
id.to_owned()
}
fn session_id() -> String {
if let Some(id) = non_empty("OPENCODE_SESSION_ID") {
return id;
}
let mut random = [0_u8; 16];
if getrandom::fill(&mut random).is_err() {
return "infinity-provider-models-dev".to_owned();
}
random
.iter()
.fold(String::with_capacity(32), |mut hex, byte| {
use std::fmt::Write as _;
let _ = write!(hex, "{byte:02x}");
hex
})
}
impl Preset {
fn resolve(&self) -> Option<ResolvedBackend> {
if self.oauth_subscription {
return self.resolve_oauth();
}
if self.no_auth {
let enabled = env::var("INFINITY_LOCAL").ok().as_deref() == Some("1")
|| self.base_envs.iter().any(|v| non_empty(v).is_some())
|| non_empty(&format!("{}_MODELS", self.prefix)).is_some()
|| non_empty(&format!("{}_MODEL", self.prefix)).is_some();
if !enabled {
return None;
}
let base_url =
first_set(self.base_envs).or_else(|| self.default_base_url.map(str::to_owned))?;
let models = resolve_models(self, &base_url);
if models.is_empty() {
return None;
}
return Some(ResolvedBackend {
preset_id: self.id,
flavor: self.flavor,
base_url: trim_trailing_slash(&base_url).to_owned(),
api_key: String::new(),
oauth: None,
discover: true,
models,
extra_headers: Vec::new(),
});
}
let api_key = first_set(self.key_envs)?;
let discover = self.discover
|| std::env::var(format!("{}_DISCOVER", self.prefix))
.ok()
.as_deref()
== Some("1");
let mut extra_headers = Vec::new();
if self.id == "opencode-go" {
extra_headers.push(("x-opencode-session".to_owned(), session_id()));
}
let mut base_url = first_set(self.base_envs)
.or_else(|| non_empty(&format!("{}_BASE_URL", self.prefix)))
.or_else(|| self.default_base_url.map(str::to_owned));
if self.id == "azure" && base_url.is_none() {
if let Some(resource) =
non_empty("AZURE_OPENAI_RESOURCE_NAME").or_else(|| non_empty("AZURE_RESOURCE_NAME"))
{
base_url = Some(format!(
"https://{}.openai.azure.com/openai/v1",
resource.trim()
));
}
}
let base_url = base_url?;
let models = resolve_models(self, &base_url);
if models.is_empty() && !discover {
return None;
}
if self.id == "openrouter" {
extra_headers.push((
"HTTP-Referer".to_owned(),
non_empty("OPENROUTER_APP_URL")
.unwrap_or_else(|| "https://github.com/hydro-project/infinity".to_owned()),
));
extra_headers.push((
"X-Title".to_owned(),
non_empty("OPENROUTER_APP_NAME").unwrap_or_else(|| "infinity".to_owned()),
));
}
Some(ResolvedBackend {
preset_id: self.id,
flavor: self.flavor,
base_url: trim_trailing_slash(&base_url).to_owned(),
api_key,
oauth: None,
discover,
models,
extra_headers,
})
}
}
impl Preset {
fn resolve_oauth(&self) -> Option<ResolvedBackend> {
use crate::oauth::{self, OAuthSession, OAuthSessionHandle};
use std::sync::{Arc, RwLock};
let static_token = non_empty(oauth::STATIC_TOKEN_ENV);
let opted_in =
std::env::var(oauth::OPT_IN_ENV).ok().as_deref() == Some("1") || static_token.is_some();
if !opted_in {
return None;
}
let session = if let Some(token) = static_token {
OAuthSession {
access_token: token,
refresh_token: String::new(),
expires_at_ms: i64::MAX,
persist_path: None,
}
} else if let Some(loaded) = oauth::load_credentials() {
if loaded.access_token.trim().is_empty() {
return None;
}
OAuthSession {
access_token: loaded.access_token,
refresh_token: loaded.refresh_token,
expires_at_ms: loaded.expires_at_ms,
persist_path: loaded.persist_path,
}
} else {
tracing::warn!(
"claude-oauth: opted in but no credentials found; run \
`infinity-provider-models-dev login`, `claude auth login`, \
or set ANTHROPIC_OAUTH_TOKEN"
);
return None;
};
if !session.refresh_token.trim().is_empty()
&& oauth::needs_refresh(session.expires_at_ms, oauth::now_ms())
{
tracing::info!("claude-oauth: stored bearer expired; will refresh on first use");
}
let base_url =
first_set(self.base_envs).or_else(|| self.default_base_url.map(str::to_owned))?;
let base_url = trim_trailing_slash(&base_url).to_owned();
let models = resolve_models(self, &base_url);
if models.is_empty() {
return None;
}
let handle: OAuthSessionHandle = Arc::new(RwLock::new(session));
Some(ResolvedBackend {
preset_id: self.id,
flavor: self.flavor,
base_url,
api_key: String::new(),
oauth: Some(handle),
discover: false,
models,
extra_headers: Vec::new(),
})
}
}
fn preset_to_backend(m: &PresetModel) -> BackendModel {
BackendModel {
upstream: m.id.to_owned(),
display: m.display_name.to_owned(),
context_window: m.context_window,
max_output_tokens: m.max_output_tokens,
supports_images: m.supports_images,
}
}
fn resolve_models(preset: &Preset, _base_url: &str) -> Vec<BackendModel> {
if let Some(ids) = model_overrides(preset.prefix) {
return ids
.into_iter()
.map(|id| {
let builtin = preset.models.iter().find(|m| m.id == id);
builtin.map_or_else(
|| BackendModel {
upstream: id.clone(),
display: display_for(&id),
context_window: 200_000,
max_output_tokens: Some(32_000),
supports_images: true,
},
preset_to_backend,
)
})
.collect();
}
preset.models.iter().map(preset_to_backend).collect()
}
pub fn resolve_backends() -> Vec<ResolvedBackend> {
let mut backends: Vec<ResolvedBackend> = PRESETS.iter().filter_map(Preset::resolve).collect();
if let Some(base_url) = non_empty("INFINITY_OPENAI_BASE_URL") {
let api_key = non_empty("INFINITY_OPENAI_API_KEY").unwrap_or_default();
let ids = non_empty("INFINITY_OPENAI_MODELS")
.map(|list| {
list.split(',')
.map(|s| s.trim().to_owned())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
})
.filter(|v| !v.is_empty())
.unwrap_or_default();
backends.push(ResolvedBackend {
preset_id: "custom",
flavor: Flavor::OpenAi,
base_url: trim_trailing_slash(&base_url).to_owned(),
api_key,
oauth: None,
discover: true,
models: ids
.into_iter()
.map(|id| BackendModel {
upstream: id.clone(),
display: display_for(&id),
context_window: 200_000,
max_output_tokens: Some(32_000),
supports_images: true,
})
.collect(),
extra_headers: Vec::new(),
});
}
backends
}
fn trim_trailing_slash(s: &str) -> &str {
s.trim_end_matches('/')
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn model_overrides_empty_without_env() {
assert!(model_overrides("INFINITY_PROVIDER_MODELS_DEV_UNSET_TEST_PREFIX").is_none());
}
#[test]
fn oauth_preset_opt_in_flow() {
let saved_token = std::env::var("ANTHROPIC_OAUTH_TOKEN").ok();
let saved_opt_in = std::env::var("INFINITY_CLAUDE_OAUTH").ok();
let restore = |saved_token: &Option<String>, saved_opt_in: &Option<String>| {
if let Some(token) = saved_token {
unsafe {
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", token);
}
} else {
unsafe {
std::env::remove_var("ANTHROPIC_OAUTH_TOKEN");
}
}
if let Some(opt_in) = saved_opt_in {
unsafe {
std::env::set_var("INFINITY_CLAUDE_OAUTH", opt_in);
}
} else {
unsafe {
std::env::remove_var("INFINITY_CLAUDE_OAUTH");
}
}
};
let preset = PRESETS
.iter()
.find(|p| p.id == "claude-oauth")
.expect("claude-oauth preset exists");
unsafe {
std::env::remove_var("ANTHROPIC_OAUTH_TOKEN");
std::env::remove_var("INFINITY_CLAUDE_OAUTH");
}
assert!(preset.resolve().is_none());
unsafe {
std::env::set_var("ANTHROPIC_OAUTH_TOKEN", "sk-ant-oauth01-test");
}
let backend = preset.resolve().expect("static token activates preset");
assert_eq!(backend.preset_id, "claude-oauth");
assert!(backend.oauth.is_some());
assert_eq!(backend.models.len(), 3);
restore(&saved_token, &saved_opt_in);
}
#[test]
fn opencode_presets_share_key_and_go_sends_session() {
let saved_key = std::env::var("OPENCODE_API_KEY").ok();
let saved_session = std::env::var("OPENCODE_SESSION_ID").ok();
let restore = |saved_key: &Option<String>, saved_session: &Option<String>| {
if let Some(key) = saved_key {
unsafe {
std::env::set_var("OPENCODE_API_KEY", key);
}
} else {
unsafe {
std::env::remove_var("OPENCODE_API_KEY");
}
}
if let Some(session) = saved_session {
unsafe {
std::env::set_var("OPENCODE_SESSION_ID", session);
}
} else {
unsafe {
std::env::remove_var("OPENCODE_SESSION_ID");
}
}
};
let find = |id: &str| {
PRESETS
.iter()
.find(|p| p.id == id)
.unwrap_or_else(|| panic!("{id} preset exists"))
};
unsafe {
std::env::remove_var("OPENCODE_API_KEY");
std::env::remove_var("OPENCODE_SESSION_ID");
}
assert!(find("opencode").resolve().is_none());
assert!(find("opencode-go").resolve().is_none());
unsafe {
std::env::set_var("OPENCODE_API_KEY", "ok-test");
}
let base = find("opencode").resolve().expect("opencode activates");
assert!(base.discover);
assert!(base.models.len() >= 2);
assert!(
base.extra_headers
.iter()
.all(|(k, _)| k != "x-opencode-session"),
"session header is Go-only"
);
let go = find("opencode-go")
.resolve()
.expect("opencode-go activates");
assert!(go.discover);
let session = go
.extra_headers
.iter()
.find(|(k, _)| k == "x-opencode-session")
.map(|(_, v)| v.clone());
assert!(session.is_some_and(|s| !s.is_empty()));
unsafe {
std::env::set_var("OPENCODE_SESSION_ID", "pinned-session");
}
let go = find("opencode-go")
.resolve()
.expect("opencode-go activates");
assert!(
go.extra_headers
.iter()
.any(|(k, v)| k == "x-opencode-session" && v == "pinned-session"),
"explicit session id wins"
);
restore(&saved_key, &saved_session);
}
#[test]
fn model_overrides_round_trip() {
let prefix = "INFINITY_PROVIDER_MODELS_DEV_TEST";
let var = format!("{prefix}_MODELS");
unsafe {
std::env::set_var(&var, "a-model,b-model");
}
assert_eq!(
model_overrides(prefix),
Some(vec!["a-model".to_owned(), "b-model".to_owned()])
);
unsafe {
std::env::remove_var(&var);
}
assert!(model_overrides(prefix).is_none());
}
}