Masking

Coalition masking codecs, views, and absence policies.

@domain masking @see [[knowledge/architecture/masking]]

Player partition plugins: whitespace tokens and whole messages.

@domain masking @see [[knowledge/architecture/masking]] @tested-by [[tests/masking/test_partitions.py]]

class mllm_shap.masking.partitions.RolesOptions(*, roles: list[Literal['user', 'assistant', 'system']] = ['user', 'assistant', 'system'])[source]

Bases: PluginOptions

Options of the tokens and messages partitions.

roles: list[Literal['user', 'assistant', 'system']]

Roles whose text becomes players; other messages are always present.

model_config = {'extra': 'forbid', 'frozen': True, 'strict': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

mllm_shap.masking.partitions.MessagesOptions

Options of the messages partition (same fields as tokens).

class mllm_shap.masking.partitions.TokenPartitioner(context: PluginContext | None = None)[source]

Bases: object

Partition a snapshot into whitespace-delimited text tokens.

options_model

Options accepted from explanation.plugin_options.

alias of RolesOptions

roles

Roles whose tokens become players.

property name: str

Return the registered partition identifier.

partition(snapshot: ConversationSnapshot) → PlayerSet[source]

Derive ordered token players and their spans from the snapshot text.

Tokens of messages whose role is not in roles are no players and stay present in every coalition; player ids do not depend on the filter.

mllm_shap.masking.partitions.LABEL_PREVIEW_CHARS: Final[int] = 30

Characters of the message text shown in a message player’s label.

class mllm_shap.masking.partitions.MessagePartitioner(context: PluginContext | None = None)[source]

Bases: object

Partition a snapshot into one player per non-empty message.

options_model

Options accepted from explanation.plugin_options.

alias of RolesOptions

roles

Roles whose messages become players.

property name: str

Return the registered partition identifier.

partition(snapshot: ConversationSnapshot) → PlayerSet[source]

Make each selected message a player whose span is its whole text.

Player ids are {snapshot_id}:{turn}:{message}; labels read "<role>[/<speaker>] <n>: <first 30 characters>" with n the 1-based message position.

Whitespace token spans and span-based snapshot rendering.

@domain masking @see [[knowledge/architecture/masking]] @tested-by [[tests/masking/test_tokens.py]]

mllm_shap.masking.tokens.token_spans(snapshot: ConversationSnapshot) → tuple[tuple[str, PlayerSpan], ...][source]

Return (player_id, span) for every whitespace token of snapshot.

Player ids have the form {snapshot_id}:{turn}:{message}:{token}.

mllm_shap.masking.tokens.render_spans(snapshot: ConversationSnapshot, spans: Sequence[PlayerSpan], replacements: Sequence[str | None]) → ConversationSnapshot[source]

Rebuild snapshot with each span replaced by its replacement text.

None deletes a span together with the whitespace that separated it from its neighbours, so deleting b from "a b c" gives "a c". Text no span covers is kept as is.

Raises:

ValueError – If spans and replacements differ in length, or a span does not match the snapshot text.

Absence policies for rendering masked conversation snapshots.

@domain masking @see [[knowledge/architecture/masking]] @tested-by [[tests/masking/test_policies.py]]

class mllm_shap.masking.policies.PadOptions(*, placeholder: str = '[MASK]')[source]

Bases: PluginOptions

Options of the pad absence policy.

placeholder: str

Placeholder text inserted for absent players.

model_config = {'extra': 'forbid', 'frozen': True, 'strict': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class mllm_shap.masking.policies.NeutralOptions(*, fill_char: str = '_')[source]

Bases: PluginOptions

Options of the neutral absence policy.

fill_char: str

Single character repeated to match each absent player’s length.

model_config = {'extra': 'forbid', 'frozen': True, 'strict': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class mllm_shap.masking.policies.DeletePolicy(context: PluginContext | None = None)[source]

Bases: object

Remove absent players from the rendered conversation.

options_model

Accepts no explanation.plugin_options.

alias of PluginOptions

property name: str

Return the registered absence-policy identifier.

apply(snapshot: ConversationSnapshot, players: PlayerSet, mask: CoalitionMask) → ConversationSnapshot[source]

Drop absent players (text, and audio samples) keeping the message structure.

The empty coalition keeps every message with empty player text (v(empty)).

class mllm_shap.masking.policies.PadPolicy(context: PluginContext | None = None, placeholder: str = '[MASK]')[source]

Bases: object

Replace absent players with a fixed mask placeholder.

options_model

Options accepted from explanation.plugin_options.

alias of PadOptions

placeholder

Placeholder text inserted for absent players.

property name: str

Return the registered absence-policy identifier.

apply(snapshot: ConversationSnapshot, players: PlayerSet, mask: CoalitionMask) → ConversationSnapshot[source]

Substitute absent players with placeholder; silence their audio.

class mllm_shap.masking.policies.NeutralPolicy(context: PluginContext | None = None, fill_char: str = '_')[source]

Bases: object

Replace absent players with neutral fillers that preserve their width.

options_model

Options accepted from explanation.plugin_options.

alias of NeutralOptions

fill_char

Single character repeated to match each absent player’s length.

property name: str

Return the registered absence-policy identifier.

apply(snapshot: ConversationSnapshot, players: PlayerSet, mask: CoalitionMask) → ConversationSnapshot[source]

Substitute width-matched fillers for absent players; silence their audio.

Render audio players: cut or silence absent samples, hide alignment transcripts.

@domain masking @see [[knowledge/architecture/masking]] @tested-by [[tests/alignment/test_sgpa_players.py]]

mllm_shap.masking.audio.render_audio(snapshot: ConversationSnapshot, spans: Sequence[PlayerSpan], present: Sequence[bool], cut: bool) → ConversationSnapshot[source]

Remove (cut) or silence the samples of absent players with samples.

Silencing keeps the audio’s length and timing; cutting joins what is left. Spans without samples and messages without absent audio are untouched.

mllm_shap.masking.audio.hide_text(snapshot: ConversationSnapshot, message_indices: Sequence[int]) → ConversationSnapshot[source]

Drop the text parts of the messages at message_indices (turns flattened).

View-based masked snapshots with structural sharing.

@domain masking @see [[knowledge/architecture/masking]] @tested-by [[tests/masking/test_builder.py]]

class mllm_shap.masking.builder.MaskedSnapshot(base: ConversationSnapshot, players: PlayerSet, mask: CoalitionMask, policy_name: str)[source]

Bases: object

Shared view over a conversation snapshot and coalition mask.

base: ConversationSnapshot

Underlying snapshot shared across coalition evaluations.

players: PlayerSet

Ordered players aligned with the coalition mask.

mask: CoalitionMask

Presence mask selecting which players remain in the coalition.

policy_name: str

Registered absence-policy identifier used to render the view.

property base_snapshot_id: str

Return the shared base snapshot identifier.

property mask_hash: int

Return a stable hash for the coalition mask.

class mllm_shap.masking.builder.MaskBuilder(policy: AbsencePolicy)[source]

Bases: object

Build masked snapshot views without copying the base conversation.

property policy_name: str

Return the configured absence-policy identifier.

view(snapshot: ConversationSnapshot, players: PlayerSet, mask: CoalitionMask) → MaskedSnapshot[source]

Create a masked view that shares snapshot across coalitions.

render(masked: MaskedSnapshot) → ConversationSnapshot[source]

Materialize a coalition-specific conversation snapshot.

Packed bitset encoding and stable hashing for coalition masks.

@domain masking @see [[knowledge/architecture/masking]] @tested-by [[tests/masking/test_codec.py]]

class mllm_shap.masking.codec.PackedMask(words: bytes, n_bits: int)[source]

Bases: object

Packed bit representation of one boolean mask.

words: bytes

Little-endian packed bytes encoding the boolean mask.

n_bits: int

Original number of bits (mask length) before packing.

class mllm_shap.masking.codec.MaskCodec[source]

Bases: object

Encode and decode boolean masks to packed bytes.

static normalize(mask: Sequence[bool] | NDArray[bool]) → NDArray[bool][source]

Normalize incoming mask to a 1D boolean array.

static pack(mask: Sequence[bool] | NDArray[bool]) → PackedMask[source]

Pack a mask into little-endian bytes.

static unpack(packed: PackedMask) → tuple[bool, ...][source]

Unpack bytes back to a boolean tuple.

static hash_mask(mask: Sequence[bool] | NDArray[bool]) → int[source]

Return a process-independent 64-bit hash over packed mask bytes.

Mask-space utilities for explainable feature indexing.

@domain masking @see [[knowledge/architecture/masking]] @tested-by [[tests/masking/test_space.py]]

class mllm_shap.masking.space.MaskSpace(explainable_mask: tuple[bool, ...], target_length: int)[source]

Bases: object

Describe explainable feature positions inside a full coalition mask.

explainable_mask: tuple[bool, ...]

Boolean flags selecting explainable feature positions from the full mask.

target_length: int

Total length of the full coalition mask including fixed positions.

property n_features: int

Return the number of explainable features.

materialize(split: CoalitionMask | Sequence[bool]) → tuple[bool, ...][source]

Project a split over explainable positions back to the full mask.