From f2108ae35a3142262d2fd451ca6f7eea36ef29d7 Mon Sep 17 00:00:00 2001 From: Jacob Date: Fri, 9 Oct 2026 02:19:53 -0400 Subject: [PATCH] feat: z.ai + more models --- src/lib.rs | 3 ++ src/server/chat/completions.rs | 58 ++++++++++++++++++---------------- src/server/embeddings.rs | 47 +++++++++++++-------------- src/server/mod.rs | 3 +- 4 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cce761f..7f4ea4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,9 @@ pub struct AppArgs { #[arg(long)] pub openrouter_api_key: Vec, + + #[arg(long)] + pub z_ai_api_key: Vec, } #[derive(Parser)] diff --git a/src/server/chat/completions.rs b/src/server/chat/completions.rs index 4b6d2bd..079f4a9 100644 --- a/src/server/chat/completions.rs +++ b/src/server/chat/completions.rs @@ -96,11 +96,15 @@ impl Shortcut { (P::GoogleAiStudio, "gemini-3.5-flash-lite".into()), (P::OpenRouter, "apodex/apodex-1.1-mini:free".into()), (P::HuggingFace, "Qwen/Qwen3.6-35B-A3B".into()), - (P::OpenRouter, "poolside/laguna-xs-2.1:free".into()), - (P::OpenRouter, "cohere/north-mini-code:free".into()), - (P::OpenRouter, "nvidia/nemotron-3.5-lightning:free".into()), + (P::GoogleAiStudio, "gemma-4-26b-a4b-it".into()), (P::OpenRouter, "google/gemma-4-26b-a4b-it:free".into()), (P::HuggingFace, "google/gemma-4-26B-A4B-it".into()), + (P::OpenRouter, "poolside/laguna-xs-2.1:free".into()), + (P::ZAi, "glm-4.7-flash".into()), + (P::OpenRouter, "cohere/north-mini-code:free".into()), + (P::OpenRouter, "nvidia/nemotron-3.5-lightning:free".into()), + (P::ZAi, "glm-4.6v-flash".into()), + (P::ZAi, "glm-4.5-flash".into()), (P::OpenRouter, "poolside/laguna-xs.2:free".into()), ( P::OpenRouter, @@ -121,6 +125,7 @@ impl Shortcut { (P::Groq, "qwen/qwen3.8-27b".into()), (P::HuggingFace, "Qwen/Qwen3.8-27B".into()), (P::HuggingFace, "meta-models/Muse-Glimmer-30B".into()), + (P::GoogleAiStudio, "gemma-4-31b-it".into()), (P::OpenRouter, "google/gemma-4-31b-it:free".into()), (P::HuggingFace, "google/gemma-4-31B-it".into()), (P::Mistral, "mistral-small-2603".into()), @@ -172,6 +177,7 @@ impl Shortcut { (P::HuggingFace, "Qwen/Qwen3.8-2.4T-A95B".into()), (P::OpenRouter, "thinkingmachines/inkling:free".into()), (P::HuggingFace, "thinkingmachines/Inkling".into()), + (P::GoogleAiStudio, "gemini-3.1-pro-preview".into()), ], S::Experimental => vec![(P::OpenRouter, "space-bunny-alpha".into())], @@ -260,8 +266,8 @@ pub async fn create_chat_completion( api_key, }| { Upstream::builder() - .base_url( - format!("https://api.cloudflare.com/client/v4/accounts/{account_id}/ai") + .url( + format!("https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1/chat/completions") .parse() .expect("TODO"), ) @@ -277,8 +283,8 @@ pub async fn create_chat_completion( .cloned() .map(|api_key| { Upstream::builder() - .base_url( - "https://generativelanguage.googleapis.com/v1beta/openai" + .url( + "https://generativelanguage.googleapis.com/v1beta/openai/v1/chat/completions" .parse() .expect("TODO"), ) @@ -293,7 +299,7 @@ pub async fn create_chat_completion( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://api.groq.com/openai".parse().expect("TODO")) + .url("https://api.groq.com/openai/v1/chat/completions".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -305,7 +311,7 @@ pub async fn create_chat_completion( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://router.huggingface.co".parse().expect("TODO")) + .url("https://router.huggingface.co/v1/chat/completions".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -317,7 +323,7 @@ pub async fn create_chat_completion( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://api.mistral.ai".parse().expect("TODO")) + .url("https://api.mistral.ai/v1/chat/completions".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -329,7 +335,19 @@ pub async fn create_chat_completion( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://openrouter.ai/api".parse().expect("TODO")) + .url("https://openrouter.ai/api/v1/chat/completions".parse().expect("TODO")) + .api_key(api_key) + .build() + }) + .collect(), + Provider::ZAi => state + .app_args + .z_ai_api_key + .iter() + .cloned() + .map(|api_key| { + Upstream::builder() + .url("https://api.z.ai/api/paas/v4/chat/completions".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -441,23 +459,7 @@ pub async fn create_chat_completion( .expect("TODO"); let mut upstream_request = client - .post({ - let mut url = upstream.base_url; - - { - let mut path_segments = url.path_segments_mut().expect("TODO"); - - let s = V1ChatCompletionsPath.to_string(); - let s = s.strip_prefix("/").unwrap_or(&s); - let s = s.strip_suffix("/").unwrap_or(&s); - - path_segments.extend(s.split("/")); - } - - tracing::info!(?url); - - url - }) + .post(upstream.url) .json(&request); if let Some(api_key) = upstream.api_key { diff --git a/src/server/embeddings.rs b/src/server/embeddings.rs index d23b173..3319ce4 100644 --- a/src/server/embeddings.rs +++ b/src/server/embeddings.rs @@ -94,8 +94,8 @@ pub async fn create_embeddings( api_key, }| { Upstream::builder() - .base_url( - format!("https://api.cloudflare.com/client/v4/accounts/{account_id}/ai") + .url( + format!("https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1/embeddings") .parse() .expect("TODO"), ) @@ -111,8 +111,8 @@ pub async fn create_embeddings( .cloned() .map(|api_key| { Upstream::builder() - .base_url( - "https://generativelanguage.googleapis.com/v1beta/openai" + .url( + "https://generativelanguage.googleapis.com/v1beta/openai/v1/embeddings" .parse() .expect("TODO"), ) @@ -127,7 +127,7 @@ pub async fn create_embeddings( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://api.groq.com/openai".parse().expect("TODO")) + .url("https://api.groq.com/openai/v1/embeddings".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -139,7 +139,7 @@ pub async fn create_embeddings( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://router.huggingface.co".parse().expect("TODO")) + .url("https://router.huggingface.co/v1/embeddings".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -151,7 +151,7 @@ pub async fn create_embeddings( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://api.mistral.ai".parse().expect("TODO")) + .url("https://api.mistral.ai/v1/embeddings".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -163,7 +163,20 @@ pub async fn create_embeddings( .cloned() .map(|api_key| { Upstream::builder() - .base_url("https://openrouter.ai/api".parse().expect("TODO")) + .url("https://openrouter.ai/api/v1/embeddings".parse().expect("TODO")) + .api_key(api_key) + .build() + }) + .collect(), + Provider::ZAi => state + .app_args + .z_ai_api_key + .iter() + .cloned() + .map(|api_key| { + Upstream::builder() + // TODO: doesn't exist + .url("https://api.z.ai/api/paas/v4/embeddings".parse().expect("TODO")) .api_key(api_key) .build() }) @@ -210,23 +223,7 @@ pub async fn create_embeddings( .expect("TODO"); let mut upstream_request = client - .post({ - let mut url = upstream.base_url; - - { - let mut path_segments = url.path_segments_mut().expect("TODO"); - - let s = V1EmbeddingsPath.to_string(); - let s = s.strip_prefix("/").unwrap_or(&s); - let s = s.strip_suffix("/").unwrap_or(&s); - - path_segments.extend(s.split("/")); - } - - tracing::info!(?url); - - url - }) + .post(upstream.url) .json(&request); if let Some(api_key) = upstream.api_key { diff --git a/src/server/mod.rs b/src/server/mod.rs index 0f84bc6..f2ceb36 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -26,6 +26,7 @@ enum Provider { HuggingFace, Mistral, OpenRouter, + ZAi, } fn take_random_by_swapping(mut vec1: Vec1, mut rng: impl Rng) -> (T, Option>) { @@ -42,7 +43,7 @@ fn take_random_by_swapping(mut vec1: Vec1, mut rng: impl Rng) -> (T, Optio #[derive(Debug, TypedBuilder)] struct Upstream { - base_url: Url, + url: Url, #[builder(setter(strip_option))] api_key: Option,