fix: resolve embedding-model fallbacks from deployment env, never guess (backport of #2191) - #2192
Conversation
_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.
WalkthroughEmbedding 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. ChangesEmbedding default resolution
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (6)
src/api/settings/helpers.pysrc/config/embedding_constants.pysrc/services/langflow_file_service.pysrc/services/search_service.pysrc/utils/embeddings.pytests/unit/test_settings_provider_removal_defaults.py
| 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 "" |
There was a problem hiding this comment.
📐 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
Backport of #2191 to `main`. Clean cherry-pick, no conflicts.
Summary
Test plan
Summary by CodeRabbit
Bug Fixes
Tests