mirror of
https://github.com/cocoindex-io/cocoindex-code.git
synced 2026-09-14 16:39:38 +08:00
ee3515fa57
Users can now set `indexing_params` and `query_params` under `embedding:` in
`global_settings.yml` to pass extra kwargs to the embedder separately for
indexing vs. query — supporting asymmetric retrieval models (Cohere v3,
Voyage, Nvidia NIM, Gemini, nomic-ai code/text models, Snowflake arctic,
etc.).
- `ccc init` auto-populates these from a curated table of known models and
prints the applied defaults; unknown models get a commented-out template
for the accepted keys (`prompt_name` for sentence-transformers;
`input_type`, `dimensions` for litellm).
- Daemon validates the effective params at startup; invalid keys fail fast
with a clear error.
- Backward compat: configs for `nomic-ai/CodeRankEmbed` /
`nomic-ai/nomic-embed-code` that predate this feature keep the previous
hardcoded `prompt_name=query` behavior, and a one-time handshake warning
asks users to make the setting explicit. The warning is suppressible by
any non-None `query_params` (including `{}`).
- `ccc doctor` now tests indexing and query separately so asymmetric
misconfigurations surface independently.
Drops the legacy `shared.query_prompt_name` module variable and
`_QUERY_PROMPT_MODELS` set; the new resolution path is centralized in
`embedder_params.resolve_embedder_params` and the curated defaults live in
`embedder_defaults._DEFAULT_PARAMS`.
Also enables `litellm.drop_params = True` so provider-specific kwargs that
a particular model doesn't accept are silently dropped instead of failing.
122 lines
4.4 KiB
Python
122 lines
4.4 KiB
Python
"""Tests for the curated default-params table."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
import pytest
|
|
|
|
from cocoindex_code.embedder_defaults import (
|
|
_DEFAULT_PARAMS,
|
|
LEGACY_QUERY_PROMPT_MODELS,
|
|
DefaultParamsEntry,
|
|
_assert_legacy_bridge_invariant,
|
|
lookup_defaults,
|
|
)
|
|
|
|
|
|
def test_lookup_defaults_exact_match() -> None:
|
|
indexing, query = lookup_defaults("sentence-transformers", "nomic-ai/CodeRankEmbed")
|
|
assert indexing == {}
|
|
assert query == {"prompt_name": "query"}
|
|
|
|
|
|
def test_lookup_defaults_regex_match_snowflake() -> None:
|
|
indexing, query = lookup_defaults(
|
|
"sentence-transformers", "Snowflake/snowflake-arctic-embed-xs"
|
|
)
|
|
assert indexing == {}
|
|
assert query == {"prompt_name": "query"}
|
|
|
|
|
|
def test_lookup_defaults_regex_match_voyage() -> None:
|
|
indexing, query = lookup_defaults("litellm", "voyage/voyage-3")
|
|
assert indexing == {"input_type": "document"}
|
|
assert query == {"input_type": "query"}
|
|
|
|
|
|
def test_lookup_defaults_regex_match_cohere() -> None:
|
|
indexing, query = lookup_defaults("litellm", "cohere/embed-english-v3.0")
|
|
assert indexing == {"input_type": "search_document"}
|
|
assert query == {"input_type": "search_query"}
|
|
|
|
|
|
def test_lookup_defaults_regex_match_gemini_embedding_001() -> None:
|
|
indexing, query = lookup_defaults("litellm", "gemini/gemini-embedding-001")
|
|
assert indexing == {"input_type": "RETRIEVAL_DOCUMENT"}
|
|
assert query == {"input_type": "RETRIEVAL_QUERY"}
|
|
|
|
|
|
def test_lookup_defaults_regex_match_gemini_text_embedding_legacy() -> None:
|
|
indexing, query = lookup_defaults("litellm", "gemini/text-embedding-004")
|
|
assert indexing == {"input_type": "RETRIEVAL_DOCUMENT"}
|
|
assert query == {"input_type": "RETRIEVAL_QUERY"}
|
|
|
|
|
|
def test_lookup_defaults_openai_no_match() -> None:
|
|
"""OpenAI embeddings are symmetric — no recommended params."""
|
|
assert lookup_defaults("litellm", "openai/text-embedding-3-small") == (None, None)
|
|
assert lookup_defaults("litellm", "text-embedding-3-large") == (None, None)
|
|
|
|
|
|
def test_lookup_defaults_no_match() -> None:
|
|
assert lookup_defaults("litellm", "openai/text-embedding-3-small") == (None, None)
|
|
|
|
|
|
def test_lookup_defaults_provider_mismatch() -> None:
|
|
# litellm/voyage regex should not match when provider is sentence-transformers
|
|
assert lookup_defaults("sentence-transformers", "voyage/voyage-3") == (None, None)
|
|
|
|
|
|
def test_lookup_defaults_exact_takes_priority_over_regex(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""Exact entries placed before regex entries are returned first."""
|
|
table = [
|
|
DefaultParamsEntry(
|
|
"litellm",
|
|
"voyage/voyage-3",
|
|
{"input_type": "OVERRIDDEN_DOC"},
|
|
{"input_type": "OVERRIDDEN_QUERY"},
|
|
),
|
|
DefaultParamsEntry(
|
|
"litellm",
|
|
re.compile(r"voyage/.+"),
|
|
{"input_type": "document"},
|
|
{"input_type": "query"},
|
|
),
|
|
]
|
|
monkeypatch.setattr("cocoindex_code.embedder_defaults._DEFAULT_PARAMS", table)
|
|
indexing, query = lookup_defaults("litellm", "voyage/voyage-3")
|
|
assert indexing == {"input_type": "OVERRIDDEN_DOC"}
|
|
assert query == {"input_type": "OVERRIDDEN_QUERY"}
|
|
|
|
|
|
def test_lookup_defaults_returns_fresh_dicts() -> None:
|
|
"""Callers can mutate the returned dicts without corrupting the table."""
|
|
_, query1 = lookup_defaults("sentence-transformers", "nomic-ai/CodeRankEmbed")
|
|
assert query1 is not None
|
|
query1["prompt_name"] = "mutated"
|
|
_, query2 = lookup_defaults("sentence-transformers", "nomic-ai/CodeRankEmbed")
|
|
assert query2 == {"prompt_name": "query"}
|
|
|
|
|
|
def test_legacy_models_have_matching_defaults() -> None:
|
|
"""Every legacy model must have an exact sentence-transformers entry with
|
|
query_params={'prompt_name': 'query'}.
|
|
"""
|
|
for model in LEGACY_QUERY_PROMPT_MODELS:
|
|
_, query = lookup_defaults("sentence-transformers", model)
|
|
assert query == {"prompt_name": "query"}
|
|
|
|
|
|
def test_legacy_bridge_invariant_assertion_detects_missing(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""The invariant check should raise when a legacy model has no matching entry."""
|
|
# Strip all sentence-transformers entries so no legacy model has a match.
|
|
stripped = [e for e in _DEFAULT_PARAMS if e.provider != "sentence-transformers"]
|
|
monkeypatch.setattr("cocoindex_code.embedder_defaults._DEFAULT_PARAMS", stripped)
|
|
with pytest.raises(AssertionError, match="has no matching"):
|
|
_assert_legacy_bridge_invariant()
|