chore: 添加虚拟环境到仓库

- 添加 backend_service/venv 虚拟环境
- 包含所有Python依赖包
- 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
2025-12-03 10:19:25 +08:00
parent a6c2027caa
commit c4f851d387
12655 changed files with 3009376 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
import math
import pytest
from chromadb.utils.embedding_functions.chroma_bm25_embedding_function import (
DEFAULT_CHROMA_BM25_STOPWORDS,
ChromaBm25EmbeddingFunction,
)
def _is_sorted(values: list[int]) -> bool:
return all(values[i] >= values[i - 1] for i in range(1, len(values)))
def test_comprehensive_tokenization_matches_reference() -> None:
embedder = ChromaBm25EmbeddingFunction()
embedding = embedder(
[
"Usain Bolt's top speed reached ~27.8 mph (44.72 km/h)",
]
)[0]
expected_indices = [
230246813,
395514983,
458027949,
488165615,
729632045,
734978415,
997512866,
1114505193,
1381820790,
1501587190,
1649421877,
1837285388,
]
expected_value = 1.6391153
assert embedding.indices == expected_indices
for value in embedding.values:
assert value == pytest.approx(expected_value, abs=1e-5)
def test_matches_rust_reference_values() -> None:
embedder = ChromaBm25EmbeddingFunction()
embedding = embedder(
[
"The space-time continuum WARPS near massive objects...",
]
)[0]
expected_indices = [
90097469,
519064992,
737893654,
1110755108,
1950894484,
2031641008,
2058513491,
]
expected_value = 1.660867
assert embedding.indices == expected_indices
for value in embedding.values:
assert value == pytest.approx(expected_value, abs=1e-5)
def test_generates_embeddings_for_multiple_documents() -> None:
embedder = ChromaBm25EmbeddingFunction()
texts = [
"Usain Bolt's top speed reached ~27.8 mph (44.72 km/h)",
"The space-time continuum WARPS near massive objects...",
"BM25 is great for sparse retrieval tasks",
]
embeddings = embedder(texts)
assert len(embeddings) == len(texts)
for embedding in embeddings:
assert embedding.indices
assert len(embedding.indices) == len(embedding.values)
assert _is_sorted(embedding.indices)
for value in embedding.values:
assert value > 0
assert math.isfinite(value)
def test_embed_query_matches_call() -> None:
embedder = ChromaBm25EmbeddingFunction()
query = "retrieve BM25 docs"
query_embedding = embedder.embed_query([query])[0]
doc_embedding = embedder([query])[0]
assert query_embedding.indices == doc_embedding.indices
assert query_embedding.values == doc_embedding.values
def test_config_round_trip() -> None:
embedder = ChromaBm25EmbeddingFunction()
config = embedder.get_config()
assert config["k"] == pytest.approx(1.2, abs=1e-9)
assert config["b"] == pytest.approx(0.75, abs=1e-9)
assert config["avg_doc_length"] == pytest.approx(256.0, abs=1e-9)
assert config["token_max_length"] == 40
assert "stopwords" not in config
custom_stopwords = DEFAULT_CHROMA_BM25_STOPWORDS[:10]
rebuilt = ChromaBm25EmbeddingFunction.build_from_config(
{
**config,
"stopwords": custom_stopwords,
}
)
rebuilt_config = rebuilt.get_config()
assert rebuilt_config["stopwords"] == custom_stopwords
assert rebuilt_config["token_max_length"] == config["token_max_length"]
assert rebuilt_config["k"] == pytest.approx(config["k"], abs=1e-9)
assert rebuilt_config["b"] == pytest.approx(config["b"], abs=1e-9)
assert rebuilt_config["avg_doc_length"] == pytest.approx(
config["avg_doc_length"], abs=1e-9
)
def test_validate_config_update_rejects_unknown_keys() -> None:
embedder = ChromaBm25EmbeddingFunction()
with pytest.raises(ValueError):
embedder.validate_config_update(embedder.get_config(), {"unknown": 123})
def test_validate_config_update_allows_known_keys() -> None:
embedder = ChromaBm25EmbeddingFunction()
embedder.validate_config_update(
embedder.get_config(), {"k": 1.1, "stopwords": ["custom"]}
)

View File

@@ -0,0 +1,95 @@
from chromadb.api.types import EmbeddingFunction, Embeddable, Embeddings
import numpy as np
from typing import cast, Any
from chromadb.utils.embedding_functions import (
register_embedding_function,
known_embedding_functions,
)
class LegacyCustomEmbeddingFunction(EmbeddingFunction[Embeddable]):
def __call__(self, input: Embeddable) -> Embeddings:
return cast(Embeddings, np.array([1, 2, 3]).tolist())
class CustomEmbeddingFunction(EmbeddingFunction[Embeddable]):
def __call__(self, input: Embeddable) -> Embeddings:
return cast(Embeddings, np.array([1, 2, 3]).tolist())
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass
@staticmethod
def name() -> str:
return "custom_embedding_function"
@staticmethod
def build_from_config(config: dict[str, Any]) -> "CustomEmbeddingFunction":
return CustomEmbeddingFunction()
def get_config(self) -> dict[str, Any]:
return {}
@register_embedding_function
class CustomEmbeddingFunctionWithRegistration(EmbeddingFunction[Embeddable]):
def __call__(self, input: Embeddable) -> Embeddings:
return cast(Embeddings, np.array([1, 2, 3]).tolist())
def __init__(self, *args: Any, **kwargs: Any) -> None:
pass
@staticmethod
def name() -> str:
return "custom_embedding_function_with_registration"
@staticmethod
def build_from_config(
config: dict[str, Any]
) -> "CustomEmbeddingFunctionWithRegistration":
return CustomEmbeddingFunctionWithRegistration()
def get_config(self) -> dict[str, Any]:
return {}
def test_legacy_custom_ef() -> None:
ef = LegacyCustomEmbeddingFunction()
result = ef(["test"])
# Check the structure: we expect a list with one NumPy array
assert isinstance(result, list), "Result should be a list"
assert len(result) == 1, "Result should contain exactly one element"
assert isinstance(result[0], np.ndarray), "Result element should be a NumPy array"
# Compare the contents of the array
expected = np.array([1, 2, 3], dtype=np.float32)
assert np.array_equal(
result[0], expected
), f"Arrays not equal: {result[0]} vs {expected}"
def test_custom_ef() -> None:
ef = CustomEmbeddingFunction()
result = ef(["test"])
# Same checks as above
assert isinstance(result, list), "Result should be a list"
assert len(result) == 1, "Result should contain exactly one element"
assert isinstance(result[0], np.ndarray), "Result element should be a NumPy array"
expected = np.array([1, 2, 3], dtype=np.float32)
assert np.array_equal(
result[0], expected
), f"Arrays not equal: {result[0]} vs {expected}"
def test_custom_ef_registration() -> None:
# check all 4 embedding functions for registration.
# LegacyCustomEmbeddingFunction should not be in known_embedding_functions
# CustomEmbeddingFunction should not be in known_embedding_functions
# CustomEmbeddingFunctionWithRegistration should be in known_embedding_functions
assert "legacy_custom_embedding_function" not in known_embedding_functions
assert "custom_embedding_function" not in known_embedding_functions
assert "custom_embedding_function_with_registration" in known_embedding_functions

View File

@@ -0,0 +1,90 @@
import shutil
import os
from typing import List, Hashable
import hypothesis.strategies as st
import onnxruntime
import pytest
from hypothesis import given, settings
from chromadb.utils.embedding_functions.onnx_mini_lm_l6_v2 import (
ONNXMiniLM_L6_V2,
)
from chromadb.utils.embedding_functions.onnx_mini_lm_l6_v2 import _verify_sha256
def unique_by(x: Hashable) -> Hashable:
return x
@settings(deadline=None)
@given(
providers=st.lists(
st.sampled_from(onnxruntime.get_all_providers()).filter(
lambda x: x not in onnxruntime.get_available_providers()
),
unique_by=unique_by,
min_size=1,
)
)
def test_unavailable_provider_multiple(providers: List[str]) -> None:
with pytest.raises(ValueError) as e:
ef = ONNXMiniLM_L6_V2(preferred_providers=providers)
ef(["test"])
assert "Preferred providers must be subset of available providers" in str(e.value)
@given(
providers=st.lists(
st.sampled_from(onnxruntime.get_available_providers()),
min_size=1,
unique_by=unique_by,
)
)
def test_available_provider(providers: List[str]) -> None:
ef = ONNXMiniLM_L6_V2(preferred_providers=providers)
ef(["test"])
def test_warning_no_providers_supplied() -> None:
ef = ONNXMiniLM_L6_V2()
ef(["test"])
@given(
providers=st.lists(
st.sampled_from(onnxruntime.get_available_providers()),
min_size=1,
).filter(lambda x: len(x) > len(set(x)))
)
def test_provider_repeating(providers: List[str]) -> None:
with pytest.raises(ValueError) as e:
ef = ONNXMiniLM_L6_V2(preferred_providers=providers)
ef(["test"])
assert "Preferred providers must be unique" in str(e.value)
def test_invalid_sha256() -> None:
ef = ONNXMiniLM_L6_V2()
shutil.rmtree(ef.DOWNLOAD_PATH) # clean up any existing models
with pytest.raises(ValueError) as e:
ef._MODEL_SHA256 = "invalid"
ef(["test"])
assert "does not match expected SHA256 hash" in str(e.value)
def test_partial_download() -> None:
ef = ONNXMiniLM_L6_V2()
shutil.rmtree(ef.DOWNLOAD_PATH, ignore_errors=True) # clean up any existing models
os.makedirs(ef.DOWNLOAD_PATH, exist_ok=True)
path = os.path.join(ef.DOWNLOAD_PATH, ef.ARCHIVE_FILENAME)
with open(path, "wb") as f: # create invalid file to simulate partial download
f.write(b"invalid")
ef._download_model_if_not_exists() # re-download model
assert os.path.exists(path)
assert _verify_sha256(
str(os.path.join(ef.DOWNLOAD_PATH, ef.ARCHIVE_FILENAME)),
ef._MODEL_SHA256,
)
assert len(ef(["test"])) == 1

View File

@@ -0,0 +1,118 @@
from chromadb.utils import embedding_functions
from chromadb.utils.embedding_functions import (
EmbeddingFunction,
register_embedding_function,
)
from typing import Dict, Any
import pytest
from chromadb.api.types import (
Embeddings,
Space,
Embeddable,
SparseEmbeddingFunction,
)
from chromadb.api.models.CollectionCommon import validation_context
def test_get_builtins_holds() -> None:
"""
Ensure that `get_builtins` is consistent after the ef migration.
This test is intended to be temporary until the ef migration is complete as
these expected builtins are likely to grow as long as users add new
embedding functions.
REMOVE ME ON THE NEXT EF ADDITION
"""
expected_builtins = {
"AmazonBedrockEmbeddingFunction",
"BasetenEmbeddingFunction",
"CloudflareWorkersAIEmbeddingFunction",
"CohereEmbeddingFunction",
"VoyageAIEmbeddingFunction",
"GoogleGenerativeAiEmbeddingFunction",
"GooglePalmEmbeddingFunction",
"GoogleVertexEmbeddingFunction",
"HuggingFaceEmbeddingFunction",
"HuggingFaceEmbeddingServer",
"InstructorEmbeddingFunction",
"JinaEmbeddingFunction",
"MistralEmbeddingFunction",
"MorphEmbeddingFunction",
"ONNXMiniLM_L6_V2",
"OllamaEmbeddingFunction",
"OpenAIEmbeddingFunction",
"OpenCLIPEmbeddingFunction",
"RoboflowEmbeddingFunction",
"SentenceTransformerEmbeddingFunction",
"Text2VecEmbeddingFunction",
"ChromaLangchainEmbeddingFunction",
"TogetherAIEmbeddingFunction",
"DefaultEmbeddingFunction",
"HuggingFaceSparseEmbeddingFunction",
"FastembedSparseEmbeddingFunction",
"Bm25EmbeddingFunction",
"ChromaCloudQwenEmbeddingFunction",
"ChromaCloudSpladeEmbeddingFunction",
"ChromaBm25EmbeddingFunction",
}
assert expected_builtins == embedding_functions.get_builtins()
def test_default_ef_exists() -> None:
assert hasattr(embedding_functions, "DefaultEmbeddingFunction")
default_ef = embedding_functions.DefaultEmbeddingFunction()
assert default_ef is not None
assert isinstance(default_ef, EmbeddingFunction) or isinstance(
default_ef, SparseEmbeddingFunction
)
def test_ef_imports() -> None:
for ef in embedding_functions.get_builtins():
# Langchain embedding function is a special snowflake
if ef == "ChromaLangchainEmbeddingFunction":
continue
assert hasattr(embedding_functions, ef)
assert isinstance(getattr(embedding_functions, ef), type)
assert issubclass(
getattr(embedding_functions, ef), EmbeddingFunction
) or issubclass(getattr(embedding_functions, ef), SparseEmbeddingFunction)
@register_embedding_function
class CustomEmbeddingFunction(EmbeddingFunction[Embeddable]):
def __init__(self, dim: int = 3):
self._dim = dim
@validation_context("custom_ef_call")
def __call__(self, input: Embeddable) -> Embeddings:
raise Exception("This is a test exception")
@staticmethod
def name() -> str:
return "custom_ef"
def get_config(self) -> Dict[str, Any]:
return {"dim": self._dim}
@staticmethod
def build_from_config(config: Dict[str, Any]) -> "CustomEmbeddingFunction":
return CustomEmbeddingFunction(dim=config["dim"])
def default_space(self) -> Space:
return "cosine"
def test_validation_context_with_custom_ef() -> None:
custom_ef = CustomEmbeddingFunction()
with pytest.raises(Exception) as excinfo:
custom_ef(["test data"])
original_msg = "This is a test exception"
expected_msg = f"{original_msg} in custom_ef_call."
assert str(excinfo.value) == expected_msg
assert excinfo.value.args == (expected_msg,)

View File

@@ -0,0 +1,135 @@
import os
import pytest
import numpy as np
from chromadb.utils.embedding_functions.morph_embedding_function import (
MorphEmbeddingFunction,
)
def test_morph_embedding_function_with_api_key() -> None:
"""Test Morph embedding function when API key is available."""
if os.environ.get("MORPH_API_KEY") is None:
pytest.skip("MORPH_API_KEY not set")
ef = MorphEmbeddingFunction(
model_name="morph-embedding-v2"
)
# Test with code snippets (Morph's specialty)
code_snippets = [
"def hello_world():\n print('Hello, World!')",
"class Calculator:\n def add(self, a, b):\n return a + b"
]
embeddings = ef(code_snippets)
assert embeddings is not None
assert len(embeddings) == 2
assert all(isinstance(emb, np.ndarray) for emb in embeddings)
assert all(len(emb) > 0 for emb in embeddings)
def test_morph_embedding_function_with_custom_parameters() -> None:
"""Test Morph embedding function with custom parameters."""
if os.environ.get("MORPH_API_KEY") is None:
pytest.skip("MORPH_API_KEY not set")
ef = MorphEmbeddingFunction(
model_name="morph-embedding-v2",
api_base="https://api.morphllm.com/v1",
encoding_format="float",
api_key_env_var="MORPH_API_KEY"
)
# Test with a simple function
code_snippet = ["function add(a, b) { return a + b; }"]
embeddings = ef(code_snippet)
assert embeddings is not None
assert len(embeddings) == 1
assert isinstance(embeddings[0], np.ndarray)
assert len(embeddings[0]) > 0
def test_morph_embedding_function_config_roundtrip() -> None:
"""Test that Morph embedding function configuration can be saved and restored."""
try:
import openai
except ImportError:
pytest.skip("openai package not installed")
ef = MorphEmbeddingFunction(
model_name="morph-embedding-v2",
api_base="https://api.morphllm.com/v1",
encoding_format="float",
api_key_env_var="MORPH_API_KEY"
)
# Get configuration
config = ef.get_config()
# Verify configuration contains expected keys
assert "model_name" in config
assert "api_base" in config
assert "encoding_format" in config
assert "api_key_env_var" in config
# Verify values
assert config["model_name"] == "morph-embedding-v2"
assert config["api_base"] == "https://api.morphllm.com/v1"
assert config["encoding_format"] == "float"
assert config["api_key_env_var"] == "MORPH_API_KEY"
# Test building from config
new_ef = MorphEmbeddingFunction.build_from_config(config)
new_config = new_ef.get_config()
# Configurations should match
assert config == new_config
def test_morph_embedding_function_name() -> None:
"""Test that Morph embedding function returns correct name."""
assert MorphEmbeddingFunction.name() == "morph"
def test_morph_embedding_function_spaces() -> None:
"""Test that Morph embedding function supports expected spaces."""
try:
import openai
except ImportError:
pytest.skip("openai package not installed")
ef = MorphEmbeddingFunction(
model_name="morph-embedding-v2",
api_key_env_var="MORPH_API_KEY"
)
# Test default space
assert ef.default_space() == "cosine"
# Test supported spaces
supported_spaces = ef.supported_spaces()
assert "cosine" in supported_spaces
assert "l2" in supported_spaces
assert "ip" in supported_spaces
def test_morph_embedding_function_validate_config() -> None:
"""Test that Morph embedding function validates configuration correctly."""
# Valid configuration
valid_config = {
"model_name": "morph-embedding-v2",
"api_key_env_var": "MORPH_API_KEY"
}
# This should not raise an exception
MorphEmbeddingFunction.validate_config(valid_config)
# Invalid configuration (missing required fields)
invalid_config = {
"model_name": "morph-embedding-v2"
# Missing api_key_env_var
}
with pytest.raises(Exception):
MorphEmbeddingFunction.validate_config(invalid_config)

View File

@@ -0,0 +1,169 @@
import os
from typing import Generator, cast
import numpy as np
import pytest
import chromadb
from chromadb.api.types import (
Embeddable,
EmbeddingFunction,
Embeddings,
Image,
Document,
)
from chromadb.test.property.strategies import hashing_embedding_function
from chromadb.test.property.invariants import _exact_distances
from chromadb.config import Settings
# A 'standard' multimodal embedding function, which converts inputs to strings
# then hashes them to a fixed dimension.
class hashing_multimodal_ef(EmbeddingFunction[Embeddable]):
def __init__(self) -> None:
self._hef = hashing_embedding_function(dim=10, dtype=np.float64)
def __call__(self, input: Embeddable) -> Embeddings:
to_texts = [str(i) for i in input]
embeddings = np.array(self._hef(to_texts))
# Normalize the embeddings
# This is so we can generate random unit vectors and have them be close to the embeddings
embeddings /= np.linalg.norm(embeddings, axis=1, keepdims=True) # type: ignore[misc]
return cast(Embeddings, embeddings.tolist())
def random_image() -> Image:
return np.random.randint(0, 255, size=(10, 10, 3), dtype=np.int64)
def random_document() -> Document:
return str(random_image())
@pytest.fixture
def multimodal_collection(
default_ef: EmbeddingFunction[Embeddable] = hashing_multimodal_ef(),
) -> Generator[chromadb.Collection, None, None]:
settings = Settings()
if os.environ.get("CHROMA_INTEGRATION_TEST_ONLY"):
host = os.environ.get("CHROMA_SERVER_HOST", "localhost")
port = int(os.environ.get("CHROMA_SERVER_HTTP_PORT", 0))
settings.chroma_api_impl = "chromadb.api.fastapi.FastAPI"
settings.chroma_server_http_port = port
settings.chroma_server_host = host
client = chromadb.Client(settings=settings)
collection = client.create_collection(
name="multimodal_collection", embedding_function=default_ef
)
yield collection
client.clear_system_cache()
# Test adding and querying of a multimodal collection consisting of images and documents
def test_multimodal(
multimodal_collection: chromadb.Collection,
default_ef: EmbeddingFunction[Embeddable] = hashing_multimodal_ef(),
n_examples: int = 10,
n_query_results: int = 3,
) -> None:
# Fix numpy's random seed for reproducibility
random_state = np.random.get_state()
np.random.seed(0)
image_ids = [str(i) for i in range(n_examples)]
images = [random_image() for _ in range(n_examples)]
image_embeddings = default_ef(images)
document_ids = [str(i) for i in range(n_examples, 2 * n_examples)]
documents = [random_document() for _ in range(n_examples)]
document_embeddings = default_ef(documents)
# Trying to add a document and an image at the same time should fail
with pytest.raises(
ValueError,
# This error string may be in any order
match=r"Exactly one of (images|documents|uris)(?:, (images|documents|uris))?(?:, (images|documents|uris))? must be provided in add\.",
):
multimodal_collection.add(
ids=image_ids[0], documents=documents[0], images=images[0]
)
# Add some documents
multimodal_collection.add(ids=document_ids, documents=documents)
# Add some images
multimodal_collection.add(ids=image_ids, images=images)
# get() should return all the documents and images
# ids corresponding to images should not have documents
get_result = multimodal_collection.get(include=["documents"])
assert len(get_result["ids"]) == len(document_ids) + len(image_ids)
for i, id in enumerate(get_result["ids"]):
assert id in document_ids or id in image_ids
assert get_result["documents"] is not None
if id in document_ids:
assert get_result["documents"][i] == documents[document_ids.index(id)]
if id in image_ids:
assert get_result["documents"][i] is None
# Generate a random query image
query_image = random_image()
query_image_embedding = default_ef([query_image])
image_neighbor_indices, _ = _exact_distances(
query_image_embedding, image_embeddings + document_embeddings
)
# Get the ids of the nearest neighbors
nearest_image_neighbor_ids = [
image_ids[i] if i < n_examples else document_ids[i % n_examples]
for i in image_neighbor_indices[0][:n_query_results]
]
# Generate a random query document
query_document = random_document()
query_document_embedding = default_ef([query_document])
document_neighbor_indices, _ = _exact_distances(
query_document_embedding, image_embeddings + document_embeddings
)
nearest_document_neighbor_ids = [
image_ids[i] if i < n_examples else document_ids[i % n_examples]
for i in document_neighbor_indices[0][:n_query_results]
]
# Querying with both images and documents should fail
with pytest.raises(ValueError):
multimodal_collection.query(
query_images=[query_image], query_texts=[query_document]
)
# Query with images
query_result = multimodal_collection.query(
query_images=[query_image], n_results=n_query_results, include=["documents"]
)
assert query_result["ids"][0] == nearest_image_neighbor_ids
# Query with documents
query_result = multimodal_collection.query(
query_texts=[query_document], n_results=n_query_results, include=["documents"]
)
assert query_result["ids"][0] == nearest_document_neighbor_ids
np.random.set_state(random_state)
@pytest.mark.xfail
def test_multimodal_update_with_image(
multimodal_collection: chromadb.Collection,
) -> None:
# Updating an entry with an existing document should remove the documentß
document = random_document()
image = random_image()
id = "0"
multimodal_collection.add(ids=id, documents=document)
multimodal_collection.update(ids=id, images=image)
get_result = multimodal_collection.get(ids=id, include=["documents"])
assert get_result["documents"] is not None
assert get_result["documents"][0] is None

View File

@@ -0,0 +1,50 @@
import pytest
from chromadb.utils.embedding_functions.ollama_embedding_function import (
OllamaEmbeddingFunction,
)
def test_ollama_default_model() -> None:
pytest.importorskip("ollama", reason="ollama not installed")
ef = OllamaEmbeddingFunction()
embeddings = ef(["Here is an article about llamas...", "this is another article"])
assert embeddings is not None
assert len(embeddings) == 2
assert all(len(e) == 384 for e in embeddings)
def test_ollama_unknown_model() -> None:
pytest.importorskip("ollama", reason="ollama not installed")
model_name = "unknown-model"
ef = OllamaEmbeddingFunction(model_name=model_name)
with pytest.raises(Exception) as e:
ef(["Here is an article about llamas...", "this is another article"])
assert f'model "{model_name}" not found' in str(e.value)
def test_ollama_backward_compat() -> None:
pytest.importorskip("ollama", reason="ollama not installed")
ef = OllamaEmbeddingFunction(url="http://localhost:11434/api/embeddings")
embeddings = ef(["Here is an article about llamas...", "this is another article"])
assert embeddings is not None
def test_wrong_url() -> None:
pytest.importorskip("ollama", reason="ollama not installed")
ef = OllamaEmbeddingFunction(url="http://localhost:11434/this_is_wrong")
with pytest.raises(Exception) as e:
ef(["Here is an article about llamas...", "this is another article"])
assert "404" in str(e.value)
def test_ollama_ask_user_to_install() -> None:
try:
from ollama import Client # noqa: F401
except ImportError:
pass
else:
pytest.skip("ollama python package is installed")
with pytest.raises(ValueError) as e:
OllamaEmbeddingFunction()
assert "The ollama python package is not installed" in str(e.value)

View File

@@ -0,0 +1,204 @@
import os
import tempfile
from typing import Dict, Any
import numpy as np
from numpy.typing import NDArray
import pytest
import onnxruntime
from unittest.mock import patch, MagicMock
from chromadb.utils.embedding_functions import ONNXMiniLM_L6_V2, EmbeddingFunction
class TestONNXMiniLM_L6_V2:
"""Test suite for ONNXMiniLM_L6_V2 embedding function."""
def test_initialization(self) -> None:
"""Test that the embedding function initializes correctly."""
ef = ONNXMiniLM_L6_V2()
assert ef is not None
assert isinstance(ef, EmbeddingFunction)
# Test with valid providers
available_providers = onnxruntime.get_available_providers()
if available_providers:
ef = ONNXMiniLM_L6_V2(preferred_providers=[available_providers[0]])
assert ef is not None
# Test with None providers
ef = ONNXMiniLM_L6_V2(preferred_providers=None)
assert ef is not None
def test_embedding_shape_and_normalization(self) -> None:
"""Test that embeddings have the correct shape and are normalized."""
ef = ONNXMiniLM_L6_V2()
# Test with a single document
docs = ["This is a test document"]
embeddings = ef(docs)
# Check shape and type
assert isinstance(embeddings, list)
assert len(embeddings) == 1
assert (
len(embeddings[0]) == 384
) # MiniLM-L6-v2 produces 384-dimensional embeddings
# Check normalization (for cosine similarity)
embedding_np = np.array(embeddings[0])
norm = np.linalg.norm(embedding_np)
assert np.isclose(norm, 1.0, atol=1e-5)
# Test with multiple documents
docs = ["First document", "Second document", "Third document"]
embeddings = ef(docs)
# Check shape
assert len(embeddings) == 3
assert all(len(emb) == 384 for emb in embeddings)
def test_batch_processing(self) -> None:
"""Test that the embedding function correctly processes batches."""
ef = ONNXMiniLM_L6_V2()
# Create a list of documents larger than the default batch size (32)
docs = [f"Document {i}" for i in range(40)]
# Get embeddings
embeddings = ef(docs)
# Check that all documents were processed
assert len(embeddings) == 40
assert all(len(emb) == 384 for emb in embeddings)
def test_config_serialization(self) -> None:
"""Test that the embedding function can be serialized and deserialized."""
# Create an embedding function with specific providers
available_providers = onnxruntime.get_available_providers()
providers = available_providers[:1] if available_providers else None
ef = ONNXMiniLM_L6_V2(preferred_providers=providers)
# Get config
config = ef.get_config()
# Check config
assert isinstance(config, dict)
assert "preferred_providers" in config
# Build from config
ef2 = ONNXMiniLM_L6_V2.build_from_config(config)
# Check that the new instance works
docs = ["Test document"]
embeddings = ef2(docs)
assert len(embeddings) == 1
assert len(embeddings[0]) == 384
def test_max_tokens(self) -> None:
"""Test the max_tokens method."""
ef = ONNXMiniLM_L6_V2()
assert ef.max_tokens() == 256 # Default for this model
@patch("httpx.stream")
def test_download_functionality(self, mock_stream: MagicMock) -> None:
"""Test the model download functionality with mocking."""
# Setup mock response
mock_response = MagicMock()
mock_response.raise_for_status.return_value = None
mock_response.headers.get.return_value = "1000"
mock_response.iter_bytes.return_value = [b"test data"]
mock_stream.return_value.__enter__.return_value = mock_response
# Create a temporary directory for testing
with tempfile.TemporaryDirectory() as temp_dir:
# Patch the download path
with patch.object(ONNXMiniLM_L6_V2, "DOWNLOAD_PATH", temp_dir):
with patch(
"chromadb.utils.embedding_functions.onnx_mini_lm_l6_v2._verify_sha256",
return_value=True,
):
ef = ONNXMiniLM_L6_V2()
# Call download method directly
ef._download(
url="https://test.url",
fname=os.path.join(temp_dir, "test_file"),
)
# Check that the file was created
assert os.path.exists(os.path.join(temp_dir, "test_file"))
def test_validate_config(self) -> None:
"""Test config validation."""
ef = ONNXMiniLM_L6_V2()
# Test validate_config
config: Dict[str, Any] = {"preferred_providers": ["CPUExecutionProvider"]}
ef.validate_config(config) # Should not raise
# Test validate_config_update
old_config: Dict[str, Any] = {"preferred_providers": ["CPUExecutionProvider"]}
new_config: Dict[str, Any] = {"preferred_providers": ["CUDAExecutionProvider"]}
ef.validate_config_update(old_config, new_config) # Should not raise
@pytest.mark.parametrize(
"input_text",
[
"Short text",
"A longer text that contains multiple words and should be embedded properly",
"", # Empty string
"Special characters: !@#$%^&*()",
"Numbers: 1234567890",
"Unicode: 你好, こんにちは, 안녕하세요",
],
)
def test_various_inputs(self, input_text: str) -> None:
"""Test the embedding function with various types of input text."""
ef = ONNXMiniLM_L6_V2()
# Get embeddings
embeddings = ef([input_text])
# Check that embeddings were generated
assert len(embeddings) == 1
assert len(embeddings[0]) == 384
def test_consistency(self) -> None:
"""Test that the embedding function produces consistent results."""
ef = ONNXMiniLM_L6_V2()
# Get embeddings for the same text twice
text = "This is a test document"
embeddings1 = ef([text])
embeddings2 = ef([text])
# Check that the embeddings are the same
np.testing.assert_allclose(embeddings1[0], embeddings2[0])
def test_similar_texts_have_similar_embeddings(self) -> None:
"""Test that similar texts have similar embeddings."""
ef = ONNXMiniLM_L6_V2()
# Get embeddings for similar texts
text1 = "The cat sat on the mat"
text2 = "A cat was sitting on a mat"
text3 = "Quantum physics is fascinating"
embeddings = ef([text1, text2, text3])
# Calculate cosine similarities
def cosine_similarity(a: NDArray[np.float32], b: NDArray[np.float32]) -> float:
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
# Similar texts should have higher similarity
sim_1_2 = cosine_similarity(
np.array(embeddings[0], dtype=np.float32),
np.array(embeddings[1], dtype=np.float32),
)
sim_1_3 = cosine_similarity(
np.array(embeddings[0], dtype=np.float32),
np.array(embeddings[2], dtype=np.float32),
)
# The similarity between text1 and text2 should be higher than between text1 and text3
assert sim_1_2 > sim_1_3

View File

@@ -0,0 +1,38 @@
import os
import pytest
from chromadb.utils.embedding_functions.openai_embedding_function import (
OpenAIEmbeddingFunction,
)
def test_with_embedding_dimensions() -> None:
if os.environ.get("OPENAI_API_KEY") is None:
pytest.skip("OPENAI_API_KEY not set")
ef = OpenAIEmbeddingFunction(
api_key=os.environ["OPENAI_API_KEY"],
model_name="text-embedding-3-small",
dimensions=64,
)
embeddings = ef(["hello world"])
assert embeddings is not None
assert len(embeddings) == 1
assert len(embeddings[0]) == 64
def test_with_embedding_dimensions_not_working_with_old_model() -> None:
if os.environ.get("OPENAI_API_KEY") is None:
pytest.skip("OPENAI_API_KEY not set")
ef = OpenAIEmbeddingFunction(api_key=os.environ["OPENAI_API_KEY"], dimensions=64)
with pytest.raises(
Exception, match="This model does not support specifying dimensions"
):
ef(["hello world"])
def test_with_incorrect_api_key() -> None:
pytest.importorskip("openai", reason="openai not installed")
ef = OpenAIEmbeddingFunction(api_key="incorrect_api_key", dimensions=64)
with pytest.raises(Exception, match="Incorrect API key provided"):
ef(["hello world"])

View File

@@ -0,0 +1,19 @@
import os
import pytest
from chromadb.utils.embedding_functions.voyageai_embedding_function import (
VoyageAIEmbeddingFunction,
)
voyageai = pytest.importorskip("voyageai", reason="voyageai not installed")
def test_with_embedding_dimensions() -> None:
if os.environ.get("CHROMA_VOYAGE_API_KEY") is None:
pytest.skip("CHROMA_VOYAGE_API_KEY not set")
ef = VoyageAIEmbeddingFunction(
api_key=os.environ["CHROMA_VOYAGE_API_KEY"]
)
embeddings = ef(["hello world"])
assert embeddings is not None
assert len(embeddings) == 1
assert len(embeddings[0]) == 1536