Source code for sim_panel.outcomes.base
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Dict, Optional, Protocol
from sim_panel.outcomes.specs import QuestionnaireSpec
[docs]
@dataclass(frozen=True)
class EvaluationContext:
"""
Minimal context needed to render an evaluation prompt.
Keep it JSON-serializable.
"""
panelist_id: str
product_id: str
t: int
product_display: str
panelist_features: Optional[Dict[str, Any]] = None
product_features: Optional[Dict[str, Any]] = None
extra: Optional[Dict[str, Any]] = None
[docs]
@dataclass(frozen=True)
class OutcomeResult:
outcomes: Optional[Dict[str, Any]]
traces: Optional[Dict[str, Any]]
raw_text: Optional[str] = None
errors: Optional[list[str]] = None
[docs]
@dataclass(frozen=True)
class OutcomeConfig:
"""
YAML-governed config for outcomes module.
"""
name: str # "deterministic" | "llm"
questionnaire: QuestionnaireSpec
# LLM-specific defaults (Panelist also has defaults; this is an extra layer)
temperature: float = 0.2
max_tokens: Optional[int] = None
# Whether to include the raw model text in OutcomeResult (useful for debugging)
include_raw_text: bool = True
# Optional few-shot exemplar override used only when prompting_strategy == "few_shot".
# If absent, built-in few-shot prompting should remain general.
#
# Expected shape:
# {
# "intro": "For a product like 'Classic Lager - A traditional pale lager with crisp finish', a respondent might answer:",
# "response": {
# "outcomes": {"rating": 7, "purchase_intent": "maybe"},
# "traces": {"rationale": "Solid traditional beer, nothing exceptional but reliable."}
# }
# }
custom_few_shot_example: Optional[Dict[str, Any]] = None
[docs]
class OutcomeModel(Protocol):
cfg: OutcomeConfig
[docs]
def evaluate(self, *, panelist, ctx: EvaluationContext) -> OutcomeResult:
...