Source code for sim_panel.policies.base

from __future__ import annotations

from dataclasses import dataclass
from typing import Dict, Optional, Sequence

import numpy as np

from sim_panel.policies.types import ExposureDecision, ManualAssignmentFn, PolicyName, RandomMode


[docs] @dataclass(frozen=True) class PolicyConfig: """ YAML-governed policy config. Note: - policies are PURE exposure logic (no LLM calls, no IO, no schema row creation) - generator owns orchestration, validation, and execution budgets """ name: PolicyName # random/manual: how many evaluations per (panelist, t) evals_per_period: int = 1 # random specifics random_mode: RandomMode = "balanced_quota" # Used only when random_mode == "iid_probs" product_probs: Optional[Dict[str, float]] = None # product_id -> prob (need not sum to 1) # self_selection exposure # Default is "show all"; if want shortlist later, set choice_set_size. choice_set_size: Optional[int] = None # None => show all allow_empty_selection: bool = True # manual policy hook (loader/wiring happens elsewhere) manual_assignment_fn: Optional[ManualAssignmentFn] = None
[docs] class Policy: """ Pure exposure logic: decides what the panelist is exposed to. - random/manual -> list of product_ids to evaluate - self_selection -> choice_set of product_ids shown """ def __init__(self, cfg: PolicyConfig) -> None: self.cfg = cfg @property def name(self) -> PolicyName: return self.cfg.name
[docs] def decide( self, *, rng: np.random.Generator, panelist_id: str, t: int, product_ids: Sequence[str], ) -> ExposureDecision: raise NotImplementedError