Source code for mllm_shap.value.embedding

"""Embedding-based value functions.

@domain value
@see [[knowledge/architecture/value]]
@tested-by [[tests/value/test_embedding.py]]
"""

from abc import ABC, abstractmethod
from typing import TYPE_CHECKING, ClassVar

from ..domain.enums import EmbeddingMode
from ..domain.generation import GenerationRecord
from ..protocols.backend import GenerationResult
from ..protocols.embedding import EmbeddingProvider
from ..protocols.plugin import PluginOptions
from ._metrics import cosine_similarity, euclidean_similarity

if TYPE_CHECKING:
    from ..plugins.context import PluginContext


[docs] class EmbeddingValue(ABC): """Similarity between static or contextual embedding vectors.""" options_model: ClassVar[type[PluginOptions]] = PluginOptions """Accepts no ``explanation.plugin_options``.""" registered_name: ClassVar[str] """Registered value-function identifier.""" def __init__( self, context: "PluginContext | None" = None, embedding_mode: EmbeddingMode = EmbeddingMode.STATIC, provider: EmbeddingProvider | None = None, ) -> None: """Pick the mode; a ``context`` supplies ``explanation.embedding_mode``.""" if context is not None: embedding_mode = context.config.explanation.embedding_mode self._embedding_mode = embedding_mode self._provider = provider @property def name(self) -> str: """Return the registered value-function identifier.""" return self.registered_name @property def needs_embeddings(self) -> bool: """Always true; scoring reads the records' embedding vectors.""" return True
[docs] def score(self, base: GenerationResult, candidate: GenerationResult) -> float: """Return the similarity of the two embedding vectors.""" return self._similarity( _resolve_embedding(base, self._embedding_mode, self._provider), _resolve_embedding(candidate, self._embedding_mode, self._provider), )
@abstractmethod def _similarity(self, first: tuple[float, ...], second: tuple[float, ...]) -> float: """Return the similarity of two vectors."""
[docs] class CosineEmbeddingValue(EmbeddingValue): """Cosine similarity between static or contextual embeddings (U1/U2).""" registered_name = "embedding_cosine" def _similarity(self, first: tuple[float, ...], second: tuple[float, ...]) -> float: return cosine_similarity(first, second)
[docs] class EuclideanEmbeddingValue(EmbeddingValue): """Similarity ``1 / (1 + distance)`` between embeddings (U4).""" registered_name = "embedding_euclidean" def _similarity(self, first: tuple[float, ...], second: tuple[float, ...]) -> float: return euclidean_similarity(first, second)
def _resolve_embedding( generation: GenerationResult, embedding_mode: EmbeddingMode, provider: EmbeddingProvider | None, ) -> tuple[float, ...]: if isinstance(generation, GenerationRecord): if embedding_mode == EmbeddingMode.CONTEXTUAL: if generation.contextual_embedding: return generation.contextual_embedding if generation.embedding: return generation.embedding elif generation.embedding: return generation.embedding if provider is not None: return provider.embed(generation) msg = "embedding vector or EmbeddingProvider is required" raise TypeError(msg)