e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
177 lines
6.1 KiB
Python
177 lines
6.1 KiB
Python
"""Tests for OpenAICompatibleEmbeddingAdapter._extract_embeddings_from_response."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from deeptutor.services.embedding.adapters.openai_compatible import (
|
|
OpenAICompatibleEmbeddingAdapter,
|
|
)
|
|
|
|
_extract = OpenAICompatibleEmbeddingAdapter._extract_embeddings_from_response
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Standard OpenAI schema: {"data": [{"embedding": [...]}, ...]}
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestOpenAIStandardSchema:
|
|
def test_single_embedding(self) -> None:
|
|
data = {"data": [{"embedding": [0.1, 0.2, 0.3]}]}
|
|
result = _extract(data)
|
|
assert result == [[0.1, 0.2, 0.3]]
|
|
|
|
def test_multiple_embeddings(self) -> None:
|
|
data = {
|
|
"data": [
|
|
{"embedding": [0.1, 0.2]},
|
|
{"embedding": [0.3, 0.4]},
|
|
]
|
|
}
|
|
result = _extract(data)
|
|
assert result == [[0.1, 0.2], [0.3, 0.4]]
|
|
|
|
def test_with_extra_fields(self) -> None:
|
|
data = {
|
|
"object": "list",
|
|
"model": "text-embedding-3-small",
|
|
"data": [
|
|
{"object": "embedding", "index": 0, "embedding": [1.0, 2.0]},
|
|
],
|
|
"usage": {"prompt_tokens": 5, "total_tokens": 5},
|
|
}
|
|
result = _extract(data)
|
|
assert result == [[1.0, 2.0]]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Proxy schema: {"embeddings": [[...], ...]}
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestProxySchema:
|
|
def test_nested_lists(self) -> None:
|
|
data = {"embeddings": [[0.1, 0.2], [0.3, 0.4]]}
|
|
result = _extract(data)
|
|
assert result == [[0.1, 0.2], [0.3, 0.4]]
|
|
|
|
def test_single_vector(self) -> None:
|
|
data = {"embeddings": [[0.5, 0.6, 0.7]]}
|
|
result = _extract(data)
|
|
assert result == [[0.5, 0.6, 0.7]]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Ollama /api/embeddings: {"embedding": [...]} (singular, flat vector)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestOllamaSingularEmbedding:
|
|
def test_flat_float_vector(self) -> None:
|
|
data = {"embedding": [0.1, 0.2, 0.3, 0.4]}
|
|
result = _extract(data)
|
|
assert result == [[0.1, 0.2, 0.3, 0.4]]
|
|
|
|
def test_flat_int_vector(self) -> None:
|
|
data = {"embedding": [1, 2, 3]}
|
|
result = _extract(data)
|
|
assert result == [[1, 2, 3]]
|
|
|
|
def test_with_ollama_metadata(self) -> None:
|
|
data = {
|
|
"embedding": [0.01, -0.02, 0.03],
|
|
"model": "nomic-embed-text",
|
|
"total_duration": 123456789,
|
|
}
|
|
result = _extract(data)
|
|
assert result == [[0.01, -0.02, 0.03]]
|
|
|
|
def test_high_dimensional(self) -> None:
|
|
vec = [float(i) / 1000 for i in range(768)]
|
|
data = {"embedding": vec}
|
|
result = _extract(data)
|
|
assert len(result) == 1
|
|
assert len(result[0]) == 768
|
|
assert result[0] == vec
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Nested result/output variants
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestNestedSchemas:
|
|
def test_result_data_with_embedding_objects(self) -> None:
|
|
data = {"result": {"data": [{"embedding": [0.1, 0.2]}]}}
|
|
result = _extract(data)
|
|
assert result == [[0.1, 0.2]]
|
|
|
|
def test_result_embeddings_nested_lists(self) -> None:
|
|
data = {"result": {"embeddings": [[0.1, 0.2], [0.3, 0.4]]}}
|
|
result = _extract(data)
|
|
assert result == [[0.1, 0.2], [0.3, 0.4]]
|
|
|
|
def test_output_data_with_embedding_objects(self) -> None:
|
|
data = {"output": {"data": [{"embedding": [0.5, 0.6]}]}}
|
|
result = _extract(data)
|
|
assert result == [[0.5, 0.6]]
|
|
|
|
def test_output_embeddings_nested_lists(self) -> None:
|
|
data = {"output": {"embeddings": [[0.7, 0.8]]}}
|
|
result = _extract(data)
|
|
assert result == [[0.7, 0.8]]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Error conditions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestErrorHandling:
|
|
def test_non_dict_raises(self) -> None:
|
|
with pytest.raises(ValueError, match="not a JSON object"):
|
|
_extract([1, 2, 3])
|
|
|
|
def test_error_payload_string(self) -> None:
|
|
with pytest.raises(ValueError, match="error payload"):
|
|
_extract({"error": "something went wrong"})
|
|
|
|
def test_error_payload_dict_with_message(self) -> None:
|
|
with pytest.raises(ValueError, match="model not found"):
|
|
_extract({"error": {"message": "model not found", "code": 404}})
|
|
|
|
def test_error_payload_dict_with_detail(self) -> None:
|
|
with pytest.raises(ValueError, match="invalid key"):
|
|
_extract({"error": {"detail": "invalid key"}})
|
|
|
|
def test_unknown_schema_raises_with_keys(self) -> None:
|
|
with pytest.raises(ValueError, match="Cannot parse embeddings"):
|
|
_extract({"foo": "bar", "baz": 42})
|
|
|
|
def test_empty_data_list_falls_through(self) -> None:
|
|
with pytest.raises(ValueError, match="Cannot parse embeddings"):
|
|
_extract({"data": []})
|
|
|
|
def test_empty_embeddings_list_falls_through(self) -> None:
|
|
with pytest.raises(ValueError, match="Cannot parse embeddings"):
|
|
_extract({"embeddings": []})
|
|
|
|
def test_empty_embedding_singular_falls_through(self) -> None:
|
|
with pytest.raises(ValueError, match="Cannot parse embeddings"):
|
|
_extract({"embedding": []})
|
|
|
|
def test_none_embedding_value_replaced_with_empty_list(self) -> None:
|
|
"""When a provider returns {"embedding": null}, use [] instead of None.
|
|
|
|
Prevents TypeError in LlamaIndex similarity computation (issue #346).
|
|
"""
|
|
data = {
|
|
"data": [
|
|
{"embedding": [0.1, 0.2]},
|
|
{"embedding": None},
|
|
{"embedding": [0.3, 0.4]},
|
|
]
|
|
}
|
|
result = _extract(data)
|
|
assert result == [[0.1, 0.2], [], [0.3, 0.4]]
|