Repair sentence-transformer embedding config aliases

Patch stale sentence-transformer embedding provider/model aliases at the _model_config read/build boundary so memory embeddings use the intended local HuggingFace provider without adding provider-specific logic to models.py. Cover stale alias forms, the bare default alias, normal OpenAI embedding pass-through, and missing API-key detection with focused regression tests.

Alessandro committed Jun 17, 2026 at 02:53 UTC 630bfc16f2cb8b7f918c4ac05ca158bc4a216d30
3 files changed +85 -2
plugins/_model_config/AGENTS.md
+1
@@ -17,6 +17,7 @@
17 - Keep provider metadata and API-key checks safe around secrets.
18 - Coordinate OAuth-backed providers with `_oauth` instead of hardcoding provider-specific auth here.
19 - Applying a model preset may inherit durable tuning such as context windows and rate limits, but must replace or clear per-slot `kwargs` so provider-specific extra params never leak across model providers.
20 +- Repair provider-specific model-config aliases at the model-config read/build boundary; keep provider-specific repairs out of provider-agnostic core wrappers such as `models.py`.
21
22 ## Work Guidance
23
plugins/_model_config/helpers/model_config.py
+20 -2
@@ -450,7 +450,25 @@ def get_utility_model_config(agent=None) -> dict:
450 def get_embedding_model_config(agent=None) -> dict:
451 """Get embedding model config."""
452 cfg = get_config(agent)
453 - return cfg.get("embedding_model", {})
453 + model_cfg = deepcopy(cfg.get("embedding_model", {}))
454 + provider = str(model_cfg.get("provider") or "").strip().lower()
455 + name = str(model_cfg.get("name") or "").strip().strip('"').strip("'")
456 +
457 + if provider:
458 + model_cfg["provider"] = provider
459 + if name:
460 + model_cfg["name"] = name
461 +
462 + if name.startswith("huggingface/sentence-transformers/"):
463 + model_cfg["provider"] = "huggingface"
464 + model_cfg["name"] = name.removeprefix("huggingface/")
465 + elif name.startswith("sentence-transformers/") and provider in {"", "openai", "other"}:
466 + model_cfg["provider"] = "huggingface"
467 + elif provider == "huggingface" and name == "all-MiniLM-L6-v2":
468 + model_cfg["name"] = "sentence-transformers/all-MiniLM-L6-v2"
469 +
470 + return model_cfg
471 +
472
473 def is_chat_override_allowed(agent=None) -> bool:
474 """Check if per-chat model override is enabled."""
@@ -566,7 +584,7 @@ def get_missing_api_key_providers(agent=None) -> list[dict]:
584 checks = [
585 ("Chat Model", cfg.get("chat_model", {})),
586 ("Utility Model", cfg.get("utility_model", {})),
569 - ("Embedding Model", cfg.get("embedding_model", {})),
587 + ("Embedding Model", get_embedding_model_config(agent)),
588 ]
589
590 for label, model_cfg in checks:
tests/test_model_config_api_keys.py
+64
@@ -430,6 +430,70 @@ def test_local_provider_runtime_defaults_and_overrides(monkeypatch):
430 assert custom_vllm_chat.kwargs["api_key"] == "real-local-key"
431
432
433 +def test_embedding_config_repairs_sentence_transformer_aliases(monkeypatch):
434 + from plugins._model_config.helpers import model_config
435 +
436 + cases = [
437 + (
438 + {"provider": "", "name": "sentence-transformers/all-MiniLM-L6-v2"},
439 + ("huggingface", "sentence-transformers/all-MiniLM-L6-v2"),
440 + ),
441 + (
442 + {"provider": "openai", "name": "sentence-transformers/all-MiniLM-L6-v2"},
443 + ("huggingface", "sentence-transformers/all-MiniLM-L6-v2"),
444 + ),
445 + (
446 + {
447 + "provider": "other",
448 + "name": "huggingface/sentence-transformers/all-MiniLM-L6-v2",
449 + },
450 + ("huggingface", "sentence-transformers/all-MiniLM-L6-v2"),
451 + ),
452 + (
453 + {"provider": "huggingface", "name": "all-MiniLM-L6-v2"},
454 + ("huggingface", "sentence-transformers/all-MiniLM-L6-v2"),
455 + ),
456 + ]
457 +
458 + for raw_embedding, expected in cases:
459 + monkeypatch.setattr(
460 + model_config,
461 + "get_config",
462 + lambda *args, raw_embedding=raw_embedding, **kwargs: {
463 + "embedding_model": raw_embedding
464 + },
465 + )
466 + cfg = model_config.get_embedding_model_config_object()
467 +
468 + assert (cfg.provider, cfg.name) == expected
469 +
470 + monkeypatch.setattr(
471 + model_config,
472 + "get_config",
473 + lambda *args, **kwargs: {
474 + "embedding_model": {
475 + "provider": "openai",
476 + "name": "text-embedding-3-small",
477 + }
478 + },
479 + )
480 + cfg = model_config.get_embedding_model_config_object()
481 +
482 + assert (cfg.provider, cfg.name) == ("openai", "text-embedding-3-small")
483 +
484 + monkeypatch.setattr(
485 + model_config,
486 + "get_config",
487 + lambda *args, **kwargs: {
488 + "embedding_model": {
489 + "provider": "openai",
490 + "name": "sentence-transformers/all-MiniLM-L6-v2",
491 + }
492 + },
493 + )
494 + assert model_config.get_missing_api_key_providers() == []
495 +
496 +
497 def test_docker_compose_maps_host_docker_internal_for_local_models():
498 import yaml
499