Files
Jiangzhou 9700655aa6 refactor(embedder-params): drop dimensions knob; pass indexing kwargs into LiteLLM ctor (#151)
- Remove `dimensions` from the litellm whitelist in `_ACCEPTED_KWARGS`.
  Output dimension must be identical for indexing and query for vectors to
  be comparable, so it's a model-wide setting, not a per-side knob —
  exposing it under `indexing_params` / `query_params` invited
  misconfiguration. Updated comment template, README, design doc, and
  testing plan accordingly.
- Plumb `indexing_params` into `create_embedder` and pass them as
  constructor kwargs to `PacedLiteLLMEmbedder`. The values land in
  `self._kwargs` and become defaults forwarded into every
  `litellm.aembedding` call — including paths that don't go through the
  `INDEXING_EMBED_PARAMS` context var (e.g. the dim probe in `_get_dim`).
  Per-call overrides (`query_params` spread at query time) still win
  because `_embed` overlays kwargs on top of `self._kwargs`. Sentence-
  transformers ignores `indexing_params` (its constructor doesn't accept
  arbitrary kwargs; `prompt_name` is per-call only).
2026-04-25 09:33:24 -07:00

97 lines
3.5 KiB
Python

"""Validation and resolution of embedder ``indexing_params`` / ``query_params``.
Runtime entry point is :func:`resolve_embedder_params`. The curated defaults
table lives in :mod:`embedder_defaults` and is used only by ``ccc init`` —
this module does not consult it.
"""
from __future__ import annotations
from typing import Any, NamedTuple
from .embedder_defaults import LEGACY_QUERY_PROMPT_MODELS
from .settings import EmbeddingSettings
__all__ = [
"EmbedderParams",
"accepted_kwargs_for",
"resolve_embedder_params",
"validate_params",
]
# Accepted kwargs per provider. Intentionally minimal — we only expose knobs
# that users have reason to tune AND that make sense per-side (indexing vs
# query). Excluded keys:
# - ``normalize_embeddings`` (sentence-transformers): query._l2_to_score
# assumes unit vectors.
# - ``encoding_format`` (litellm): litellm_embedder hardcodes "float".
_ACCEPTED_KWARGS: dict[str, frozenset[str]] = {
"sentence-transformers": frozenset({"prompt_name"}),
"litellm": frozenset({"input_type"}),
}
def accepted_kwargs_for(provider: str) -> frozenset[str]:
"""Return the set of accepted kwarg names for *provider*.
Raises ``ValueError`` on unknown providers.
"""
try:
return _ACCEPTED_KWARGS[provider]
except KeyError as e:
raise ValueError(f"Unknown provider: {provider!r}") from e
def validate_params(
provider: str,
indexing_params: dict[str, Any] | None,
query_params: dict[str, Any] | None,
) -> None:
"""Raise ``ValueError`` if either dict contains keys not accepted by *provider*."""
accepted = accepted_kwargs_for(provider)
for side, params in (("indexing_params", indexing_params), ("query_params", query_params)):
if not params:
continue
unknown = sorted(set(params) - accepted)
if unknown:
raise ValueError(
f"{side}: unknown key(s) {unknown!r} for provider {provider!r}. "
f"Accepted keys: {sorted(accepted)!r}."
)
class EmbedderParams(NamedTuple):
"""Params that will be spread into ``embedder.embed()`` calls at runtime."""
indexing: dict[str, Any] # never None; possibly empty
query: dict[str, Any] # never None; possibly empty
used_backward_compat: bool # True iff the legacy bridge fired
def resolve_embedder_params(settings: EmbeddingSettings) -> EmbedderParams:
"""Resolve the effective embedder params from user settings.
Whatever the user put in the file, verbatim, with one exception for
backward compatibility: if neither ``indexing_params`` nor ``query_params``
is set and the model was previously handled by the hardcoded
``_QUERY_PROMPT_MODELS`` path, fill in ``query = {'prompt_name': 'query'}``
and raise the ``used_backward_compat`` flag so the daemon emits a
handshake warning.
"""
indexing: dict[str, Any] = dict(settings.indexing_params) if settings.indexing_params else {}
query: dict[str, Any] = dict(settings.query_params) if settings.query_params else {}
used_backward_compat = False
if (
settings.indexing_params is None
and settings.query_params is None
and settings.provider == "sentence-transformers"
and settings.model in LEGACY_QUERY_PROMPT_MODELS
):
query = {"prompt_name": "query"}
used_backward_compat = True
validate_params(settings.provider, indexing, query)
return EmbedderParams(indexing=indexing, query=query, used_backward_compat=used_backward_compat)