diff --git a/crates/mem-llm/src/embeddings.rs b/crates/mem-llm/src/embeddings.rs index fa17741..0329ea6 100644 --- a/crates/mem-llm/src/embeddings.rs +++ b/crates/mem-llm/src/embeddings.rs @@ -64,9 +64,15 @@ impl EmbeddingsClient { /// - `LLM_API_BASE`: Gateway endpoint (default: https://api.riotpiao.com) /// - `LLM_API_KEY`: API key (optional) pub fn from_env() -> Result { - let base_url = env::var("LLM_API_BASE") + let mut base_url = env::var("LLM_API_BASE") .unwrap_or_else(|_| "https://api.riotpiao.com".to_string()); + // Strip trailing /v1 to avoid double /v1/v1/embeddings + base_url = base_url.trim_end_matches('/').to_string(); + if base_url.ends_with("/v1") { + base_url = base_url[..base_url.len() - 3].to_string(); + } + let model = env::var("EMBEDDINGS_MODEL") .unwrap_or_else(|_| "nomic-ai/nomic-embed-text-v2-moe".to_string()); @@ -161,10 +167,9 @@ impl EmbeddingsClient { let url = format!("{}/v1/embeddings", self.base_url); let mut builder = self.http.post(&url); - // Send apikey header even though route currently doesn't require auth - // This future-proofs for when the route's auth plugin gets enabled + // Send as Bearer token (gateway expects Authorization: Bearer ) if !self.api_key.is_empty() { - builder = builder.header("apikey", &self.api_key); + builder = builder.header("Authorization", format!("Bearer {}", &self.api_key)); } let resp = builder.json(&req).send().await?; @@ -215,6 +220,28 @@ mod tests { assert_eq!(EMBEDDINGS_DIM, 768); } + #[test] + fn test_strip_trailing_v1() { + // Simulates LLM_API_BASE=https://api.riotpiao.com/v1 + let mut base = "https://api.riotpiao.com/v1".to_string(); + base = base.trim_end_matches('/').to_string(); + if base.ends_with("/v1") { + base = base[..base.len() - 3].to_string(); + } + assert_eq!(base, "https://api.riotpiao.com"); + assert_eq!(format!("{}/v1/embeddings", base), "https://api.riotpiao.com/v1/embeddings"); + } + + #[test] + fn test_no_strip_when_no_v1() { + let mut base = "https://api.riotpiao.com".to_string(); + base = base.trim_end_matches('/').to_string(); + if base.ends_with("/v1") { + base = base[..base.len() - 3].to_string(); + } + assert_eq!(base, "https://api.riotpiao.com"); + } + #[test] fn test_parse_real_embedding_response() { // Exact format returned by embeddings-predictor service