Skip to content

fix: resolve embedding-model fallbacks from deployment env, never guess (backport of #2191) - #2192

Merged
edwinjosechittilappilly merged 2 commits into
mainfrom
backport/2191-embedding-model-fallback-not-validated-main
Jul 31, 2026
Merged

fix: resolve embedding-model fallbacks from deployment env, never guess (backport of #2191)#2192
edwinjosechittilappilly merged 2 commits into
mainfrom
backport/2191-embedding-model-fallback-not-validated-main

Conversation

@edwinjosechittilappilly

@edwinjosechittilappilly edwinjosechittilappilly commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Backport of #2191 to `main`. Clean cherry-pick, no conflicts.

Summary

  • `_default_embedding_model()` previously hardcoded a per-provider guess (e.g. `text-embedding-3-small` for `openai`) used whenever a provider removal left another provider as the embedding fallback. A provider name doesn't reliably say which models are actually deployed — it's often an internal OpenAI/watsonx-compatible gateway with a curated model subset. The hardcoded guess silently selected a model the gateway didn't serve, breaking every ingestion with no clear signal why (incident: gateway only served `text-embedding-3-large`; 25/25 files failed at the Langflow embedding step).
  • Added `get_declared_default_embedding_model()`, which resolves the deployment's own declared default from `EMBEDDING_MODEL` / `EMBEDDING_PROVIDER` (Helm values / operator ConfigMap) instead of a hardcoded string, and returns `""` when the deployment hasn't declared one for that provider. Read directly rather than through `ConfigManager`'s env-override path, which is skipped once `config.edited` is `True` — a protection meant for a user's own prior choices, not a reason to block resolving an unmade choice.
  • Wired this into every "guess an embedding model" call site, not just the settings-save path: `search_service.py`, `utils/embeddings.py`, and `langflow_file_service.py` all previously fell back to the same hardcoded constant when no model was configured; they now fall back to the deployment-declared default (or none) instead.

Test plan

  • `uv run pytest tests/unit/test_settings_provider_removal_defaults.py tests/unit/test_embedding_fields.py tests/unit/test_skip_os_security_setup_init_index.py -q` — 48 passed; 3 pre-existing failures in unrelated `_default_llm_model` tests, confirmed present identically on unmodified `main` before this change.

Summary by CodeRabbit

  • Bug Fixes

    • Embedding model selection now respects the deployment’s declared provider and model configuration.
    • Removed automatic fallback to a hardcoded OpenAI embedding model when no model is declared.
    • Search, indexing, and ingestion workflows now use consistent provider-specific embedding settings.
  • Tests

    • Added coverage for declared embedding defaults and provider-specific behavior.

_default_embedding_model() previously hardcoded a per-provider guess
(e.g. "text-embedding-3-small" for openai) used whenever a provider
removal left another provider as the embedding fallback. A provider
name doesn't reliably say which models are actually deployed — it's
often an internal OpenAI/watsonx-compatible gateway with a curated
model subset. The hardcoded guess silently selected a model the
gateway didn't serve, breaking every ingestion with no clear signal why
(incident: gateway only served "text-embedding-3-large"; 25/25 files
failed at the Langflow embedding step).

Add get_declared_default_embedding_model(), which resolves the
deployment's own declared default from EMBEDDING_MODEL /
EMBEDDING_PROVIDER (Helm values / operator ConfigMap) instead of a
hardcoded string, and returns "" when the deployment hasn't declared
one for that provider. Read directly rather than through
ConfigManager's env-override path, which is skipped once config.edited
is True — a protection meant for a user's own prior choices, not a
reason to block resolving an unmade choice.

Wire this into every "guess an embedding model" call site, not just
the settings-save path: search_service.py, utils/embeddings.py, and
langflow_file_service.py all previously fell back to the same
hardcoded constant when no model was configured; they now fall back to
the deployment-declared default (or none) instead.
@github-actions github-actions Bot added backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) tests bug 🔴 Something isn't working. labels Jul 31, 2026
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Embedding model fallback selection now uses deployment-declared provider and model environment variables. Explicit and configured models remain higher priority. Missing or mismatched declarations return an empty model.

Changes

Embedding default resolution

Layer / File(s) Summary
Declared default resolver
src/config/embedding_constants.py, src/api/settings/helpers.py
The configuration helper returns a declared model only when its provider matches. Settings fallback selection no longer uses provider-specific hardcoded models.
Provider-aware service fallbacks
src/services/search_service.py, src/services/langflow_file_service.py, src/utils/embeddings.py
Search, index preconfiguration, and index body creation resolve embedding models from explicit values, configured values, or the matching declared default.
Fallback behavior validation
tests/unit/test_settings_provider_removal_defaults.py
Tests cover empty OpenAI defaults, matching deployment declarations, mismatched providers, and provider-removal behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: lucaseduoli, phact

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states that embedding-model fallbacks now use deployment environment values and avoid guessing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch backport/2191-embedding-model-fallback-not-validated-main

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added bug 🔴 Something isn't working. and removed bug 🔴 Something isn't working. labels Jul 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/config/embedding_constants.py`:
- Around line 3-33: Move the EMBEDDING_PROVIDER and EMBEDDING_MODEL environment
reads out of get_declared_default_embedding_model in embedding_constants.py and
into a dedicated declared-default accessor in config/settings.py. Update
get_declared_default_embedding_model to call that accessor while preserving its
provider matching and empty-result behavior, including fallback resolution after
configuration edits.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b566ae4e-3da2-4973-9502-adfe5228a6f3

📥 Commits

Reviewing files that changed from the base of the PR and between 2ecf20f and 091dfd9.

📒 Files selected for processing (6)
  • src/api/settings/helpers.py
  • src/config/embedding_constants.py
  • src/services/langflow_file_service.py
  • src/services/search_service.py
  • src/utils/embeddings.py
  • tests/unit/test_settings_provider_removal_defaults.py

Comment on lines +3 to +33
import os

OPENAI_DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small"
OPENAI_EMBEDDING_MODEL_PREFIX = "text-embedding" No newline at end of file
OPENAI_EMBEDDING_MODEL_PREFIX = "text-embedding"


def get_declared_default_embedding_model(provider: str) -> str:
"""Return the deployment-declared default embedding model for `provider`.

Sourced directly from the EMBEDDING_MODEL / EMBEDDING_PROVIDER env vars
the deployment is expected to set (Helm values / operator ConfigMap) to
describe what this specific environment's embedding backend actually
serves. Read directly rather than through ConfigManager's env-override
path, which is skipped once the config has been manually edited — a
protection against clobbering a user's own choices that shouldn't also
block resolving a fallback when the user hasn't made one.

Returns "" when the deployment hasn't declared a default for this
provider. Callers MUST NOT substitute a hardcoded guess in that case —
"openai" (and any other provider name) can mean anything from the real
public API to an internal gateway with a curated model subset, so no
single hardcoded model name is safe across deployments. A hardcoded
guess here previously selected "text-embedding-3-small" in an
environment whose gateway only served "text-embedding-3-large",
silently failing every ingestion.
"""
declared_provider = os.environ.get("EMBEDDING_PROVIDER", "")
declared_model = os.environ.get("EMBEDDING_MODEL", "")
if declared_provider == provider and declared_model:
return declared_model
return ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Move environment reads to config/settings.py.

This module imports os and reads EMBEDDING_PROVIDER and EMBEDDING_MODEL directly. This violates the configuration-source contract. Add a dedicated declared-default accessor in config/settings.py, then call that accessor here. Preserve the required post-edit fallback behavior in that accessor.

As per path instructions, “Config values must come from config/settings.py (the only place os.environ is read); never access os.environ elsewhere in the codebase.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/config/embedding_constants.py` around lines 3 - 33, Move the
EMBEDDING_PROVIDER and EMBEDDING_MODEL environment reads out of
get_declared_default_embedding_model in embedding_constants.py and into a
dedicated declared-default accessor in config/settings.py. Update
get_declared_default_embedding_model to call that accessor while preserving its
provider matching and empty-result behavior, including fallback resolution after
configuration edits.

Source: Path instructions

@ricofurtado ricofurtado left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@github-actions github-actions Bot added the lgtm label Jul 31, 2026
@edwinjosechittilappilly
edwinjosechittilappilly merged commit 92f31cb into main Jul 31, 2026
37 checks passed
@github-actions
github-actions Bot deleted the backport/2191-embedding-model-fallback-not-validated-main branch July 31, 2026 20:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend 🔷 Issues related to backend services (OpenSearch, Langflow, APIs) bug 🔴 Something isn't working. lgtm tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants