"""Provider adapters, exercised with no AWS account and no network. Every Bedrock call goes through a recording stub, so what is under test is the part that can actually be wrong offline: the request body we send, and our reading of the response bodies AWS documents. The one thing these tests cannot establish is whether AWS accepts that body — that needs the live probe, and the coordination handoff says so explicitly. """ import io import json import math import pytest from ingestion.embed import ( BGE_M3, COHERE_V4, TITAN_V2, INPUT_DOCUMENT, INPUT_QUERY, Boto3BedrockInvoker, EmbeddingVector, build_provider, provider_names, text_digest, ) from ingestion.embed import probe from ingestion.embed.bedrock_cohere import CohereEmbedV4 from ingestion.embed.bedrock_titan import TitanTextEmbeddingsV2 from ingestion.embed.local_bge_m3 import BgeM3Local class RecordingInvoker: """Stands in for Bedrock; remembers every request it was handed.""" def __init__(self, responses): self._responses = list(responses) self.calls = [] def invoke_json(self, model_id, payload, accept="application/json"): self.calls.append( {"model_id": model_id, "payload": payload, "accept": accept} ) return self._responses.pop(0) def _titan_response(dimensions=1024, token_count=12): return { "embedding": [0.01] * dimensions, "inputTextTokenCount": token_count, "embeddingsByType": {"float": [0.01] * dimensions}, } def _cohere_by_type_response(rows, dimensions=1024): return { "id": "stub-id", "response_type": "embeddings_by_type", "embeddings": {"float": [[0.02] * dimensions for _ in range(rows)]}, "texts": ["stub"] * rows, } def _cohere_floats_response(rows, dimensions=1024): return { "id": "stub-id", "response_type": "embeddings_floats", "embeddings": [[0.02] * dimensions for _ in range(rows)], } def test_titan_request_body_matches_the_documented_v2_shape(): invoker = RecordingInvoker([_titan_response()]) provider = TitanTextEmbeddingsV2(invoker, dimensions=1024, normalize=True) provider.embed_documents(["paracetamol"]) payload = invoker.calls[0]["payload"] assert invoker.calls[0]["model_id"] == "amazon.titan-embed-text-v2:0" assert payload == { "inputText": "paracetamol", "dimensions": 1024, "normalize": True, } def test_titan_records_provenance_and_reported_token_count(): invoker = RecordingInvoker([_titan_response(token_count=7)]) provider = TitanTextEmbeddingsV2(invoker) vector = provider.embed_documents(["paracetamol"]).vectors[0] assert vector.model_id == "amazon.titan-embed-text-v2:0" assert vector.provider == TITAN_V2 assert vector.dimensions == 1024 assert vector.input_kind == INPUT_DOCUMENT assert vector.normalized is True assert vector.input_token_count == 7 assert vector.text_sha256 == text_digest("paracetamol") def test_titan_sends_one_request_per_text(): invoker = RecordingInvoker([_titan_response(), _titan_response()]) provider = TitanTextEmbeddingsV2(invoker) batch = provider.embed_documents(["a", "b"]) assert batch.request_count == 2 assert len(batch.vectors) == 2 def test_titan_rejects_a_dimension_the_model_does_not_offer(): with pytest.raises(ValueError, match="supports"): TitanTextEmbeddingsV2(RecordingInvoker([]), dimensions=768) def test_cohere_uses_search_document_for_corpus_and_search_query_for_queries(): invoker = RecordingInvoker( [_cohere_by_type_response(1), _cohere_by_type_response(1)] ) provider = CohereEmbedV4(invoker) provider.embed_documents(["metformin"]) provider.embed_queries(["liều metformin"]) assert invoker.calls[0]["payload"]["input_type"] == "search_document" assert invoker.calls[1]["payload"]["input_type"] == "search_query" def test_cohere_request_body_pins_dimension_float_type_and_no_truncation(): invoker = RecordingInvoker([_cohere_by_type_response(2)]) provider = CohereEmbedV4(invoker, dimensions=1024) provider.embed_documents(["a", "b"]) payload = invoker.calls[0]["payload"] assert invoker.calls[0]["model_id"] == "cohere.embed-v4:0" assert payload["texts"] == ["a", "b"] assert payload["embedding_types"] == ["float"] # Left unset the model would return 1536, which no 1024-wide collection # can accept. assert payload["output_dimension"] == 1024 # An over-length input must fail, not arrive silently shortened. assert payload["truncate"] == "NONE" assert invoker.calls[0]["accept"] == "*/*" def test_cohere_reads_the_embeddings_by_type_response(): invoker = RecordingInvoker([_cohere_by_type_response(2)]) provider = CohereEmbedV4(invoker) batch = provider.embed_documents(["a", "b"]) assert len(batch.vectors) == 2 assert all(len(v.values) == 1024 for v in batch.vectors) assert batch.request_count == 1 def test_cohere_also_reads_the_plain_embeddings_floats_response(): invoker = RecordingInvoker([_cohere_floats_response(2)]) provider = CohereEmbedV4(invoker) batch = provider.embed_documents(["a", "b"]) assert len(batch.vectors) == 2 assert all(len(v.values) == 1024 for v in batch.vectors) def test_cohere_leaves_normalization_unknown_because_the_docs_do_not_say(): invoker = RecordingInvoker([_cohere_by_type_response(1)]) vector = CohereEmbedV4(invoker).embed_documents(["a"]).vectors[0] assert vector.normalized is None def test_cohere_splits_at_the_documented_96_text_ceiling(): invoker = RecordingInvoker( [_cohere_by_type_response(96), _cohere_by_type_response(4)] ) provider = CohereEmbedV4(invoker) batch = provider.embed_documents([f"t{i}" for i in range(100)]) assert batch.request_count == 2 assert len(invoker.calls[0]["payload"]["texts"]) == 96 assert len(invoker.calls[1]["payload"]["texts"]) == 4 assert len(batch.vectors) == 100 def test_cohere_rejects_a_batch_size_above_the_documented_ceiling(): with pytest.raises(ValueError, match="batch_size"): CohereEmbedV4(RecordingInvoker([]), batch_size=97) def test_a_wrong_width_vector_fails_instead_of_entering_the_corpus(): invoker = RecordingInvoker([_titan_response(dimensions=512)]) provider = TitanTextEmbeddingsV2(invoker, dimensions=1024) with pytest.raises(ValueError, match="512 dimensions"): provider.embed_documents(["a"]) def test_a_response_missing_its_vectors_fails_loudly(): invoker = RecordingInvoker([{"id": "stub", "response_type": "x"}]) with pytest.raises(ValueError, match="no 'embeddings' field"): CohereEmbedV4(invoker).embed_documents(["a"]) def test_a_count_mismatch_between_texts_and_vectors_fails(): invoker = RecordingInvoker([_cohere_by_type_response(1)]) with pytest.raises(ValueError, match="1 vectors for 2 texts"): CohereEmbedV4(invoker).embed_documents(["a", "b"]) def test_an_unknown_input_kind_is_refused_before_any_request_is_made(): invoker = RecordingInvoker([]) with pytest.raises(ValueError, match="input_kind"): CohereEmbedV4(invoker).embed(["a"], "search_document") assert invoker.calls == [] def test_empty_text_is_refused_before_any_request_is_made(): invoker = RecordingInvoker([]) with pytest.raises(ValueError, match="empty"): CohereEmbedV4(invoker).embed_documents(["a", " "]) assert invoker.calls == [] def test_bge_m3_runs_through_an_injected_encoder_with_no_weights_loaded(): seen = [] def encoder(texts): seen.append(list(texts)) unit = 1.0 / math.sqrt(1024) return [[unit] * 1024 for _ in texts] provider = BgeM3Local(encoder=encoder, batch_size=2) batch = provider.embed_queries(["a", "b", "c"]) assert seen == [["a", "b"], ["c"]] assert batch.request_count == 2 assert len(batch.vectors) == 3 assert batch.vectors[0].input_kind == INPUT_QUERY assert batch.vectors[0].model_id == "BAAI/bge-m3" # Injected encoder: we did not set normalize_embeddings, so we do not claim it. assert batch.vectors[0].normalized is None def test_registry_builds_every_provider_without_touching_an_sdk(): assert set(provider_names()) == {TITAN_V2, COHERE_V4, BGE_M3} titan = build_provider(TITAN_V2, invoker=RecordingInvoker([])) cohere = build_provider(COHERE_V4, invoker=RecordingInvoker([])) local = build_provider(BGE_M3) assert (titan.dimensions, cohere.dimensions, local.dimensions) == ( 1024, 1024, 1024, ) assert titan.max_batch_size == 1 assert cohere.max_batch_size == 96 def test_registry_rejects_an_unknown_provider_name(): with pytest.raises(ValueError, match="unknown embedding provider"): build_provider("text-embedding-3-small") class FakeBotoClient: """The shape boto3's bedrock-runtime client returns: a streaming body.""" def __init__(self, response_body): self._response_body = response_body self.kwargs = None def invoke_model(self, **kwargs): self.kwargs = kwargs return {"body": io.BytesIO(json.dumps(self._response_body).encode())} def test_boto3_invoker_serialises_the_request_and_reads_the_streamed_body(): client = FakeBotoClient({"embedding": [0.5]}) invoker = Boto3BedrockInvoker(region="us-east-1", client=client) body = invoker.invoke_json("some.model", {"inputText": "à"}, accept="*/*") assert body == {"embedding": [0.5]} assert client.kwargs["modelId"] == "some.model" assert client.kwargs["contentType"] == "application/json" assert client.kwargs["accept"] == "*/*" # Vietnamese must survive the round trip as characters, not \\u escapes # the model would then embed literally. assert json.loads(client.kwargs["body"]) == {"inputText": "à"} def test_probe_measures_the_l2_norm_rather_than_trusting_the_docs(): unit = 1.0 / math.sqrt(4) assert probe._l2_norm([unit] * 4) == pytest.approx(1.0) assert probe._l2_norm([3.0, 4.0]) == pytest.approx(5.0) def test_probe_reports_a_vector_without_raising(capsys): vector = EmbeddingVector( values=[0.5, 0.5, 0.5, 0.5], text_sha256=text_digest("x"), provider=TITAN_V2, model_id="amazon.titan-embed-text-v2:0", dimensions=4, input_kind=INPUT_DOCUMENT, normalized=True, input_token_count=3, ) probe._report(vector, latency_ms=12.5, requests=1) out = capsys.readouterr().out assert "amazon.titan-embed-text-v2:0" in out assert "measured L2 norm: 1.000000" in out