- {tool.defaultModels.map((model) => (
+ {(tool.defaultModels || []).map((model) => (
{model.name}
diff --git a/src/app/api/v1/embeddings/route.ts b/src/app/api/v1/embeddings/route.ts
index 515cf2e28c..99c439cb05 100644
--- a/src/app/api/v1/embeddings/route.ts
+++ b/src/app/api/v1/embeddings/route.ts
@@ -12,6 +12,7 @@ import {
getEmbeddingProvider,
buildDynamicEmbeddingProvider,
type EmbeddingProviderNodeRow,
+ type EmbeddingProvider,
} from "@omniroute/open-sse/config/embeddingRegistry.ts";
import { errorResponse } from "@omniroute/open-sse/utils/error.ts";
import { HTTP_STATUS } from "@omniroute/open-sse/config/constants.ts";
@@ -116,9 +117,9 @@ export async function POST(request) {
// Load local provider_nodes for embedding routing (only localhost — prevents auth bypass/SSRF)
let dynamicProviders: ReturnType[] = [];
try {
- const nodes = await getProviderNodes();
+ const nodes = (await getProviderNodes()) as unknown as EmbeddingProviderNodeRow[];
dynamicProviders = (Array.isArray(nodes) ? nodes : [])
- .filter((n: EmbeddingProviderNodeRow) => {
+ .filter((n) => {
// provider_nodes apiType is "chat" or "responses" (not "embeddings") — local OpenAI-compatible
// backends expose /embeddings under the same base URL as chat, so we build the URL as baseUrl + /embeddings.
if (n.apiType !== "chat" && n.apiType !== "responses") return false;
@@ -157,9 +158,38 @@ export async function POST(request) {
}
// Resolve provider config — dynamic first (local override), then hardcoded
- const providerConfig =
+ let providerConfig: EmbeddingProvider | null =
dynamicProviders.find((dp) => dp.id === provider) || getEmbeddingProvider(provider) || null;
+ // #496: Fallback — resolve from ALL provider_nodes (not just localhost)
+ // This enables custom embedding models (e.g. google/gemini-embedding-001) whose
+ // providers have remote baseUrls. Safe because getProviderCredentials() authenticates.
+ if (!providerConfig) {
+ try {
+ const allNodes = (await getProviderNodes()) as unknown as EmbeddingProviderNodeRow[];
+ const matchingNode = (Array.isArray(allNodes) ? allNodes : []).find(
+ (n) =>
+ n.prefix === provider && (n.apiType === "chat" || n.apiType === "responses") && n.baseUrl
+ );
+ if (matchingNode) {
+ const baseUrl = String(matchingNode.baseUrl).replace(/\/+$/, "");
+ providerConfig = {
+ id: matchingNode.prefix,
+ baseUrl: `${baseUrl}/embeddings`,
+ authType: "apikey",
+ authHeader: "bearer",
+ models: [],
+ };
+ log.info(
+ "EMBED",
+ `Resolved custom embedding provider: ${provider} → ${providerConfig.baseUrl}`
+ );
+ }
+ } catch (err) {
+ log.error("EMBED", `Failed to resolve custom embedding provider ${provider}: ${err}`);
+ }
+ }
+
if (!providerConfig) {
return errorResponse(
HTTP_STATUS.BAD_REQUEST,