diff --git a/src/openai/lib/_parsing/_embeddings.py b/src/openai/lib/_parsing/_embeddings.py index acb890fa52..5fd8aea2c3 100644 --- a/src/openai/lib/_parsing/_embeddings.py +++ b/src/openai/lib/_parsing/_embeddings.py @@ -20,11 +20,15 @@ def parse_embedding_response( if not obj.data: raise ValueError("No embedding data received") + use_numpy: bool | None = None + for embedding in obj.data: data = cast(object, embedding.embedding) if not isinstance(data, str): continue - if not has_numpy(): + if use_numpy is None: + use_numpy = has_numpy() + if not use_numpy: # use array for base64 optimisation embedding.embedding = array.array("f", base64.b64decode(data)).tolist() else: diff --git a/tests/lib/test_embeddings.py b/tests/lib/test_embeddings.py index 787d580716..fab3a96e65 100644 --- a/tests/lib/test_embeddings.py +++ b/tests/lib/test_embeddings.py @@ -63,6 +63,37 @@ def test_decode_preserves_response_and_non_string_vectors(encoding_format: Omit assert parsed.model == "text-embedding-3-small" +@pytest.mark.parametrize("encoding_format", [omit, not_given], ids=["omit", "not-given"]) +def test_decoder_is_inspected_once(encoding_format: Omit | NotGiven, monkeypatch: pytest.MonkeyPatch) -> None: + calls = 0 + + def counting_decoder() -> bool: + nonlocal calls + calls += 1 + return False + + monkeypatch.setattr(embeddings_parser, "has_numpy", counting_decoder) + response = make_response(ENCODED, ENCODED, ENCODED) + + parsed = embeddings_parser.parse_embedding_response(response, encoding_format=encoding_format) + + assert calls == 1 + assert [cast(object, item.embedding) for item in parsed.data] == [VALUES, VALUES, VALUES] + + +@pytest.mark.parametrize("encoding_format", [omit, not_given], ids=["omit", "not-given"]) +def test_float_vectors_skip_the_decoder(encoding_format: Omit | NotGiven, monkeypatch: pytest.MonkeyPatch) -> None: + def unexpected_decoder() -> bool: + raise AssertionError("a response without string vectors must not inspect the decoder") + + monkeypatch.setattr(embeddings_parser, "has_numpy", unexpected_decoder) + response = make_response(VALUES, [4.0, 5.0]) + + parsed = embeddings_parser.parse_embedding_response(response, encoding_format=encoding_format) + + assert [cast(object, item.embedding) for item in parsed.data] == [VALUES, [4.0, 5.0]] + + @pytest.mark.parametrize("encoding_format", ["float", "base64", None]) @pytest.mark.parametrize("vectors", [(ENCODED,), ("abc",), ()], ids=["encoded", "invalid", "empty"]) def test_explicit_format_is_untouched(