Source code for nlp_shap.backends.mock.backend

"""Deterministic mock backend for CI-safe E2E tests."""

import hashlib
import struct

from ...domain.conversation import ConversationSnapshot
from ...domain.generation import GenerationRecord
from ...pipeline.config import BackendConfig
from .generation import generation_record_from_snapshot


[docs] class MockBackend: """Hash-based generative backend with no external model dependencies.""" def __init__( self, config: BackendConfig | None = None, *, model_id: str = "mock", ) -> None: self._model_id = config.model_id if config is not None else model_id @property def model_id(self) -> str: """Return the backend model identifier.""" return self._model_id
[docs] async def generate( self, snapshot: ConversationSnapshot, max_new_tokens: int, temperature: float, top_k: int, ) -> GenerationRecord: """Return deterministic generation output for ``snapshot``.""" text = _deterministic_text( snapshot, self._model_id, max_new_tokens, temperature, top_k, ) return generation_record_from_snapshot(text, snapshot)
def _deterministic_text( snapshot: ConversationSnapshot, model_id: str, max_new_tokens: int, temperature: float, top_k: int, ) -> str: hasher = hashlib.sha256() hasher.update(snapshot.snapshot_id.encode("utf-8")) for turn in snapshot.turns: for message in turn.messages: hasher.update(message.text.encode("utf-8")) hasher.update(model_id.encode("utf-8")) hasher.update( struct.pack("<ifi", max_new_tokens, temperature, top_k), ) digest = hasher.hexdigest() token_count = max(1, min(max_new_tokens, 16)) return " ".join(digest[index : index + 4] for index in range(0, token_count * 4, 4))