fix: double /v1 in embeddings URL + wrong auth header
CI / CI (pull_request) Failing after 39m45s

Bug 1: LLM_API_BASE=https://api.riotpiao.com/v1 + code appends
/v1/embeddings = https://api.riotpiao.com/v1/v1/embeddings (404).
Fix: strip trailing /v1 from base URL in from_env().

Bug 2: embeddings client sent 'apikey' custom header, but gateway
expects 'Authorization: Bearer <token>'.
Fix: use Authorization Bearer header.

Caused: 'expected ident at line 1 column 2' error on /memory/query
(gateway returned HTML/text error, client tried to parse as JSON).
This commit is contained in:
2026-09-15 17:35:55 +09:00
parent fd59c6de11
commit 400b3cfaa2
+31 -4
View File
@@ -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<Self> {
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 <key>)
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