mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 21:22:28 +03:00
fix(api): name working OpenRouter ids when Gemini embed creds are missing (#10565)
Native gemini-embedding-2 400s with a dead-end credentials error even though openrouter/google/gemini-embedding-2 already serves 3072-d vectors. Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1
changelog.d/fixes/embed-gemini-missing-creds-hint.md
Normal file
1
changelog.d/fixes/embed-gemini-missing-creds-hint.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(api):** `/v1/embeddings` 400s for native `gemini-embedding-2` now name the working OpenRouter ids (`openrouter/google/gemini-embedding-2` and the preview alias) instead of only `No credentials for embedding provider: gemini` — thanks @RaviTharuma
|
||||
50
src/lib/embeddings/errors.ts
Normal file
50
src/lib/embeddings/errors.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Operator-facing embedding credential / provider errors.
|
||||
*
|
||||
* Native `gemini-embedding-2` is registered, but a host without a `gemini`
|
||||
* Google AI Studio key still 400s. OpenRouter already serves the same model
|
||||
* under `openrouter/google/gemini-embedding-2` (live default 3072-d). Name
|
||||
* that working id in the 400 so Hindsight / Memorix operators are not stuck
|
||||
* on a dead native id.
|
||||
*/
|
||||
|
||||
export const OPENROUTER_GEMINI_EMBEDDING_2 = "openrouter/google/gemini-embedding-2";
|
||||
export const OPENROUTER_GEMINI_EMBEDDING_2_PREVIEW =
|
||||
"openrouter/google/gemini-embedding-2-preview";
|
||||
export const NATIVE_GEMINI_EMBEDDING_2 = "gemini/gemini-embedding-2";
|
||||
|
||||
const OPENROUTER_HINT =
|
||||
`use \`${OPENROUTER_GEMINI_EMBEDDING_2}\` or \`${OPENROUTER_GEMINI_EMBEDDING_2_PREVIEW}\` ` +
|
||||
"when OpenRouter is configured";
|
||||
|
||||
/**
|
||||
* 400 body when the resolved embedding provider has no usable credentials.
|
||||
*/
|
||||
export function formatMissingEmbeddingCredentialsError(provider: string): string {
|
||||
const base = `No credentials for embedding provider: ${provider}`;
|
||||
if (provider === "gemini") {
|
||||
return (
|
||||
`${base}. Native Gemini embeddings require a Google AI Studio API key on the \`gemini\` ` +
|
||||
`provider. Without that key, ${OPENROUTER_HINT}.`
|
||||
);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* 400 body when the first path segment is not a known embedding provider.
|
||||
*/
|
||||
export function formatUnknownEmbeddingProviderError(
|
||||
provider: string,
|
||||
model: string | null = null
|
||||
): string {
|
||||
const base =
|
||||
`Unknown embedding provider: ${provider}. No matching hardcoded or local provider found.`;
|
||||
const looksLikeGeminiEmbedding2 =
|
||||
provider === "google" || (typeof model === "string" && model.includes("gemini-embedding-2"));
|
||||
if (!looksLikeGeminiEmbedding2) return base;
|
||||
return (
|
||||
`${base} For Gemini Embedding 2, ${OPENROUTER_HINT}, or call \`${NATIVE_GEMINI_EMBEDDING_2}\` ` +
|
||||
"after adding a Google AI Studio key on the `gemini` provider."
|
||||
);
|
||||
}
|
||||
@@ -22,6 +22,10 @@ import { runWithProxyContext } from "@omniroute/open-sse/utils/proxyFetch.ts";
|
||||
import { handleComboChat } from "@omniroute/open-sse/services/combo.ts";
|
||||
import { resolveBareModelToConnectionDefault } from "@omniroute/open-sse/services/model.ts";
|
||||
import { findEmbeddingComboDimensionConflict } from "./familyGuard";
|
||||
import {
|
||||
formatMissingEmbeddingCredentialsError,
|
||||
formatUnknownEmbeddingProviderError,
|
||||
} from "./errors";
|
||||
import { isPrivateHost, isCloudMetadataHost } from "@/shared/network/outboundUrlGuard";
|
||||
import { calculateCost } from "@/lib/usage/costCalculator";
|
||||
import { attachOmniRouteMetaHeaders } from "@/domain/omnirouteResponseMeta";
|
||||
@@ -217,7 +221,7 @@ export async function createEmbeddingResponse(
|
||||
if (!providerConfig) {
|
||||
return errorResponse(
|
||||
HTTP_STATUS.BAD_REQUEST,
|
||||
`Unknown embedding provider: ${provider}. No matching hardcoded or local provider found.`
|
||||
formatUnknownEmbeddingProviderError(provider, resolvedModel)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -227,7 +231,7 @@ export async function createEmbeddingResponse(
|
||||
if (!credentials) {
|
||||
return errorResponse(
|
||||
HTTP_STATUS.BAD_REQUEST,
|
||||
`No credentials for embedding provider: ${provider}`
|
||||
formatMissingEmbeddingCredentialsError(provider)
|
||||
);
|
||||
}
|
||||
if ("allRateLimited" in credentials && credentials.allRateLimited) {
|
||||
|
||||
38
tests/unit/embeddings-gemini-creds-hint.test.ts
Normal file
38
tests/unit/embeddings-gemini-creds-hint.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
formatMissingEmbeddingCredentialsError,
|
||||
formatUnknownEmbeddingProviderError,
|
||||
OPENROUTER_GEMINI_EMBEDDING_2,
|
||||
OPENROUTER_GEMINI_EMBEDDING_2_PREVIEW,
|
||||
NATIVE_GEMINI_EMBEDDING_2,
|
||||
} from "../../src/lib/embeddings/errors.ts";
|
||||
|
||||
test("missing gemini embed credentials name the working OpenRouter ids", () => {
|
||||
const message = formatMissingEmbeddingCredentialsError("gemini");
|
||||
assert.match(message, /^No credentials for embedding provider: gemini/);
|
||||
assert.match(message, new RegExp(OPENROUTER_GEMINI_EMBEDDING_2));
|
||||
assert.match(message, new RegExp(OPENROUTER_GEMINI_EMBEDDING_2_PREVIEW));
|
||||
assert.match(message, /Google AI Studio/);
|
||||
});
|
||||
|
||||
test("missing credentials for other providers stay a short 400", () => {
|
||||
assert.equal(
|
||||
formatMissingEmbeddingCredentialsError("openai"),
|
||||
"No credentials for embedding provider: openai"
|
||||
);
|
||||
});
|
||||
|
||||
test("unknown google provider names Gemini Embedding 2 workarounds", () => {
|
||||
const message = formatUnknownEmbeddingProviderError("google", "gemini-embedding-2");
|
||||
assert.match(message, /^Unknown embedding provider: google/);
|
||||
assert.match(message, new RegExp(OPENROUTER_GEMINI_EMBEDDING_2));
|
||||
assert.match(message, new RegExp(NATIVE_GEMINI_EMBEDDING_2));
|
||||
});
|
||||
|
||||
test("unknown unrelated providers stay a short 400", () => {
|
||||
assert.equal(
|
||||
formatUnknownEmbeddingProviderError("not-a-provider", "text-embedding-3-small"),
|
||||
"Unknown embedding provider: not-a-provider. No matching hardcoded or local provider found."
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user