_embedWithFallback() ignores isOllamaNative, always tries /api/embed first even when known to be non-Ollama
Summary
OllamaService._embedWithFallback() (in app/app/services/ollama_service.js) always tries the native Ollama /api/embed endpoint first and only falls back to the OpenAI-compatible openai.embeddings.create() (/v1/embeddings) call in its catch block:
async _embedWithFallback(model, input) {
try {
const response = await axios.post(`${this.baseUrl}/api/embed`, {
model,
input,
truncate: true,
options: { num_ctx: 8192 },
}, { timeout: 60000 });
...
}
catch (err) {
if (OllamaService_1.isContextLengthError(err))
throw err;
logger.warn('[OllamaService] /api/embed failed, falling back to /v1/embeddings: %s', ...);
const results = await this.openai.embeddings.create({ ... });
...
}
}This never checks this.isOllamaNative, even though that flag already exists and is populated by getModels() (which probes /api/tags vs. falling back to /v1/models), and is already used elsewhere in this same file (e.g. _doDownloadModel skips /api/pull when isOllamaNative === false).
Impact
When NOMAD's "AI Assistant"/RAG embedding provider is a non-Ollama, OpenAI-compatible backend (ai.remoteOllamaUrl pointed at a proxy/router that only speaks the OpenAI embeddings API, not Ollama's native API), every single embedding call pays one guaranteed-failing round trip to /api/embed before falling back to /v1/embeddings. For a bulk file-embeddings indexing job processing a large corpus (in our case, several million chunks into Qdrant), this doubles the request volume and log noise against the embedding backend for the lifetime of the job, with zero benefit.
Suggested fix
Check this.isOllamaNative before attempting /api/embed, mirroring the pattern already used in _doDownloadModel:
async _embedWithFallback(model, input) {
if (this.isOllamaNative !== false) {
try {
const response = await axios.post(`${this.baseUrl}/api/embed`, { ... });
...
return { embeddings: response.data.embeddings };
} catch (err) {
if (OllamaService_1.isContextLengthError(err)) throw err;
logger.warn('[OllamaService] /api/embed failed, falling back to /v1/embeddings: %s', ...);
}
}
const results = await this.openai.embeddings.create({ ... });
return { embeddings: results.data.map((e) => e.embedding) };
}That way the native attempt is skipped entirely once isOllamaNative is known to be false, instead of being retried (and failing) on every single embed call.
Environment
- Image:
ghcr.io/crosstalk-solutions/project-nomad@sha256:832f7b1516593bc6a83f5554c98cf753a7b96aaa7e3d991b538ac4e5edc01146(:latestas of 2026-08-07) - Reproduced via
ai.remoteOllamaUrlpointed at a small local proxy that translates NOMAD's Ollama-style calls to an OpenAI-compatible embeddings backend — every embed request logged one/api/embedfailure followed immediately by a successful/v1/embeddingscall.
Worked around it on our end for now with a proxy-side shim that answers /api/embed directly, but figured this was worth reporting since isOllamaNative is clearly meant to prevent exactly this.
Source: Crosstalk-Solutions/project-nomad