diff --git a/src/lib.rs b/src/lib.rs index 419f2a1..72cb266 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,6 +63,9 @@ pub struct AppArgs { #[arg(long)] pub nvidia_nim_api_key: Vec, + #[arg(long)] + pub ollama_cloud_api_key: Vec, + #[arg(long)] pub openrouter_api_key: Vec, diff --git a/src/server/chat/completions.rs b/src/server/chat/completions.rs index 61ea5d8..b9d8478 100644 --- a/src/server/chat/completions.rs +++ b/src/server/chat/completions.rs @@ -1,7 +1,6 @@ use crate::CloudflareCredentials; use crate::openai; use crate::server::AppState; -use crate::server::Provider; use crate::server::Upstream; use crate::server::take_random_by_swapping; use axum::Json; @@ -86,6 +85,22 @@ impl Display for ShortcutAndMaybeReasoning { } } +#[derive(Debug, Clone)] +enum Provider { + CloudflareWorkersAi, + GoogleAiStudio, + Groq, + HuggingFace, + Kilo, + Mistral, + NovitaAi, + NvidiaNim, + Ollama, + OpenRouter, + VercelAiGateway, + ZAi, +} + impl Shortcut { fn suggestions(&self) -> Vec<(Provider, EcoString)> { use Provider as P; @@ -125,7 +140,9 @@ impl Shortcut { P::OpenRouter, "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free".into(), ), + (P::Ollama, "nemotron-3-nano:30b".into()), (P::Groq, "openai/gpt-oss-20b".into()), + (P::Ollama, "gpt-oss:20b".into()), (P::NvidiaNim, "openai/gpt-oss-20b".into()), (P::CloudflareWorkersAi, "@cf/openai/gpt-oss-20b".into()), (P::OpenRouter, "openai/gpt-oss-20b:free".into()), @@ -145,6 +162,7 @@ impl Shortcut { (P::NvidiaNim, "meta/muse-glimmer-30b".into()), (P::HuggingFace, "meta-models/Muse-Glimmer-30B".into()), (P::GoogleAiStudio, "gemma-4-31b-it".into()), + (P::Ollama, "gemma4:31b".into()), (P::NvidiaNim, "google/gemma-4-31b-it".into()), (P::OpenRouter, "google/gemma-4-31b-it:free".into()), (P::HuggingFace, "google/gemma-4-31B-it".into()), @@ -185,6 +203,7 @@ impl Shortcut { "inclusionai/ling-3.0-flash-sante".into(), ), (P::VercelAiGateway, "mistral/mistral-large-3".into()), + (P::Ollama, "nemotron-3-super".into()), (P::NvidiaNim, "nvidia/nemotron-3-super-120b-a12b".into()), (P::Kilo, "nvidia-nemotron-3-super-120b-a12b-free".into()), ( @@ -192,6 +211,7 @@ impl Shortcut { "nvidia/nemotron-3-super-120b-a12b:free".into(), ), (P::Groq, "openai/gpt-oss-120b".into()), + (P::Ollama, "gpt-oss:120b".into()), (P::CloudflareWorkersAi, "@cf/openai/gpt-oss-120b".into()), (P::OpenRouter, "openai/gpt-oss-120b:free".into()), (P::HuggingFace, "openai/gpt-oss-120b".into()), @@ -208,10 +228,11 @@ impl Shortcut { (P::VercelAiGateway, "openai/gpt-5.2".into()), (P::NvidiaNim, "deepseek-ai/deepseek-v4.1-flash".into()), (P::HuggingFace, "deepseek-ai/DeepSeek-V4.1-Flash".into()), - (P::VercelAiGateway, "xiaomi/mimo-v2.6-flash".into()), - (P::HuggingFace, "XiaomiMiMo/MiMo-V2.6-Flash-RL".into()), (P::VercelAiGateway, "inclusionai/ling-3.1-flash".into()), (P::HuggingFace, "inclusionAI/Ling-3.1-flash".into()), + (P::Ollama, "beam".into()), + (P::VercelAiGateway, "xiaomi/mimo-v2.6-flash".into()), + (P::HuggingFace, "XiaomiMiMo/MiMo-V2.6-Flash-RL".into()), (P::Kilo, "nex-agi-nex-n2-5-pro-free".into()), (P::VercelAiGateway, "xiaomi/mimo-v2.5".into()), (P::Kilo, "dots-studio-dots-3-note-preview-free".into()), @@ -223,6 +244,7 @@ impl Shortcut { (P::Kilo, "tencent-hy3-preview-free".into()), (P::OpenRouter, "thinkingmachines/inkling-small:free".into()), (P::HuggingFace, "thinkingmachines/Inkling-Small".into()), + (P::Ollama, "nemotron-3-ultra".into()), (P::NvidiaNim, "nvidia/nemotron-3-ultra-550b-a55b".into()), (P::Kilo, "nvidia-nemotron-3-ultra-550b-a55b-free".into()), ], @@ -232,11 +254,11 @@ impl Shortcut { (P::HuggingFace, "zai-org/GLM-5.3".into()), (P::CloudflareWorkersAi, "@cf/zai-org/glm-5.3".into()), (P::Mistral, "zai-glm-5-3".into()), + (P::NvidiaNim, "moonshotai/kimi-k3".into()), + (P::HuggingFace, "moonshotai/Kimi-K3".into()), (P::HuggingFace, "tencent/Hy4-preview".into()), (P::HuggingFace, "XiaomiMiMo/MiMo-V2.6-Pro-RL".into()), (P::Mistral, "mistral-large-4".into()), - (P::NvidiaNim, "moonshotai/kimi-k3".into()), - (P::HuggingFace, "moonshotai/Kimi-K3".into()), (P::HuggingFace, "Qwen/Qwen3.8-2.4T-A95B".into()), (P::VercelAiGateway, "xiaomi/mimo-v2.5-pro".into()), (P::OpenRouter, "thinkingmachines/inkling:free".into()), @@ -397,7 +419,6 @@ pub async fn create_chat_completion( .build() }) .collect(), - Provider::LiquidAi => unreachable!(), Provider::Mistral => state .app_args .mistral_api_key @@ -434,6 +455,18 @@ pub async fn create_chat_completion( .build() }) .collect(), + Provider::Ollama => state + .app_args + .ollama_cloud_api_key + .iter() + .cloned() + .map(|api_key| { + Upstream::builder() + .url("https://ollama.com/v1/chat/completions".parse().expect("TODO")) + .api_key(api_key) + .build() + }) + .collect(), Provider::OpenRouter => state .app_args .openrouter_api_key diff --git a/src/server/embeddings.rs b/src/server/embeddings.rs index d694acd..a8ab903 100644 --- a/src/server/embeddings.rs +++ b/src/server/embeddings.rs @@ -1,7 +1,6 @@ use crate::CloudflareCredentials; use crate::openai; use crate::server::AppState; -use crate::server::Provider; use crate::server::Upstream; use crate::server::take_random_by_swapping; use axum::Json; @@ -45,6 +44,13 @@ pub enum Shortcut { Nemotron3Embed1B, } +#[derive(Debug, Clone)] +enum Provider { + Mistral, + NvidiaNim, + OpenRouter, +} + impl Shortcut { fn suggestions(&self) -> Vec<(Provider, EcoString)> { use Provider as P; @@ -96,111 +102,56 @@ pub async fn create_embeddings( let mut model_upstreams = VecDeque::new(); for (provider, model) in shortcut.suggestions() { - let upstreams: Vec = - match provider { - Provider::CloudflareWorkersAi => state - .app_args - .cloudflare_workers_ai_credentials - .iter() - .cloned() - .map( - |CloudflareCredentials { - account_id, - api_key, - }| { - Upstream::builder() + let upstreams: Vec = match provider { + Provider::Mistral => state + .app_args + .mistral_api_key + .iter() + .cloned() + .map(|api_key| { + Upstream::builder() .url( - format!("https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1/embeddings") + "https://api.mistral.ai/v1/embeddings" .parse() .expect("TODO"), ) .api_key(api_key) .build() - }, - ) - .collect(), - Provider::GoogleAiStudio => state - .app_args - .google_ai_studio_api_key - .iter() - .cloned() - .map(|api_key| { - Upstream::builder() - .url( - "https://generativelanguage.googleapis.com/v1beta/openai/v1/embeddings" - .parse() - .expect("TODO"), - ) - .api_key(api_key) - .build() - }) - .collect(), - Provider::Groq => state - .app_args - .groq_api_key - .iter() - .cloned() - .map(|api_key| { - Upstream::builder() - .url("https://api.groq.com/openai/v1/embeddings".parse().expect("TODO")) - .api_key(api_key) - .build() - }) - .collect(), - Provider::HuggingFace => state - .app_args - .hugging_face_api_key - .iter() - .cloned() - .map(|api_key| { - Upstream::builder() - .url("https://router.huggingface.co/v1/embeddings".parse().expect("TODO")) - .api_key(api_key) - .build() - }) - .collect(), - Provider::Kilo => unreachable!(), - Provider::LiquidAi => unreachable!(), - Provider::Mistral => state - .app_args - .mistral_api_key - .iter() - .cloned() - .map(|api_key| { - Upstream::builder() - .url("https://api.mistral.ai/v1/embeddings".parse().expect("TODO")) - .api_key(api_key) - .build() - }) - .collect(), - Provider::NvidiaNim => state - .app_args - .nvidia_nim_api_key - .iter() - .cloned() - .map(|api_key| { - Upstream::builder() - .url("https://integrate.api.nvidia.com/v1/embeddings".parse().expect("TODO")) - .api_key(api_key) - .build() - }) - .collect(), - Provider::NovitaAi => unreachable!(), - Provider::OpenRouter => state - .app_args - .openrouter_api_key - .iter() - .cloned() - .map(|api_key| { - Upstream::builder() - .url("https://openrouter.ai/api/v1/embeddings".parse().expect("TODO")) - .api_key(api_key) - .build() - }) - .collect(), - Provider::VercelAiGateway => unreachable!(), - Provider::ZAi => unreachable!(), - }; + }) + .collect(), + Provider::NvidiaNim => state + .app_args + .nvidia_nim_api_key + .iter() + .cloned() + .map(|api_key| { + Upstream::builder() + .url( + "https://integrate.api.nvidia.com/v1/embeddings" + .parse() + .expect("TODO"), + ) + .api_key(api_key) + .build() + }) + .collect(), + Provider::OpenRouter => state + .app_args + .openrouter_api_key + .iter() + .cloned() + .map(|api_key| { + Upstream::builder() + .url( + "https://openrouter.ai/api/v1/embeddings" + .parse() + .expect("TODO"), + ) + .api_key(api_key) + .build() + }) + .collect(), + }; if let Ok(upstreams) = Vec1::try_from(upstreams) { model_upstreams.push_back((model, upstreams)); diff --git a/src/server/mod.rs b/src/server/mod.rs index ef90f25..fdcfcb8 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -19,23 +19,6 @@ mod system_one; pub struct AppState { app_args: Arc, } - -#[derive(Debug, Clone)] -enum Provider { - CloudflareWorkersAi, - GoogleAiStudio, - Groq, - HuggingFace, - Kilo, - LiquidAi, - Mistral, - NovitaAi, - NvidiaNim, - OpenRouter, - VercelAiGateway, - ZAi, -} - fn take_random_by_swapping(mut vec1: Vec1, mut rng: impl Rng) -> (T, Option>) { let end = vec1.len().get() - 1; let index = rng.random_range(0..=end); diff --git a/src/server/system_one.rs b/src/server/system_one.rs index 374e675..e7168e2 100644 --- a/src/server/system_one.rs +++ b/src/server/system_one.rs @@ -1,7 +1,6 @@ use crate::CloudflareCredentials; use crate::openai; use crate::server::AppState; -use crate::server::Provider; use crate::server::Upstream; use crate::server::take_random_by_swapping; use axum::Json; @@ -36,6 +35,14 @@ pub enum Shortcut { M, } +#[derive(Debug, Clone)] +enum Provider { + CloudflareWorkersAi, + LiquidAi, + OpenRouter, + VercelAiGateway, +} + impl Shortcut { fn suggestions(&self) -> Vec<(Provider, EcoString)> { use Provider as P; @@ -53,6 +60,7 @@ impl Shortcut { (P::VercelAiGateway, "convaiinnovations/laya".into()), ] } + S::M => { vec![ (P::CloudflareWorkersAi, "clef".into()), @@ -118,10 +126,6 @@ pub async fn create_system_one( }, ) .collect(), - Provider::GoogleAiStudio => unreachable!(), - Provider::Groq => unreachable!(), - Provider::HuggingFace => unreachable!(), - Provider::Kilo => unreachable!(), Provider::LiquidAi => state .app_args .liquid_ai_api_key @@ -134,9 +138,6 @@ pub async fn create_system_one( .build() }) .collect(), - Provider::Mistral => unreachable!(), - Provider::NovitaAi => unreachable!(), - Provider::NvidiaNim => unreachable!(), Provider::OpenRouter => state .app_args .openrouter_api_key @@ -161,7 +162,6 @@ pub async fn create_system_one( .build() }) .collect(), - Provider::ZAi => unreachable!(), }; if let Ok(upstreams) = Vec1::try_from(upstreams) {