BuddyMath / domain /step_types.py
dotandru's picture
Fix: Clean production deployment with sse-starlette
9d29c62
Raw
History Blame Contribute Delete
2.55 kB
# domain/step_types.py - V7.4 (TYPED DOMAIN CONTRACTS)
"""
V7.4: Typed SignedStep โ€” Domain-Aware Validation Contract.
Replaces the raw dict `{"id", "hash", "expression"}` with a typed dataclass
that carries a StepType so the ConsistencyGate validates by semantic domain,
not by string shape.
CTO directive:
- expression โ†’ display string only (for renderer injection)
- payload โ†’ semantic truth (list[str] for geometry, str for algebraic)
- step_type โ†’ routing key for ConsistencyGate dispatch
Backwards compatibility: SignedStep emulates a dict fully so ALL existing
dict-access code (step["id"], step.get("hash"), "id" in step, step.items())
in pedagogical_renderer.py / semantic_bank.py continues to work unchanged.
"""
from enum import Enum
from dataclasses import dataclass, asdict
from typing import Any, Optional
class StepType(Enum):
ALGEBRAIC = "algebraic" # SymPy-parseable expression โ€” validated with sympify
GEOMETRY = "geometry" # Points, distances, labels โ€” validated structurally
NUMERIC = "numeric" # Pure numeric results โ€” future use
@dataclass
class SignedStep:
"""
V7.4: Typed, SHA256-signed computation step.
Fields:
id โ€” step identifier used as {{id}} placeholder in renderer
expression โ€” human-readable display string (injected into pedagogy text)
payload โ€” semantic truth: list[str] for geometry, str for algebraic
step_type โ€” StepType enum โ€” routes ConsistencyGate validation
hash โ€” SHA256 digest seeded with canonical form + problem_id + step_id
"""
id: str
expression: str
payload: Any
step_type: StepType
hash: str
# โ”€โ”€ Full dict emulation (CTO requirement) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Allows ALL existing code using step["key"], step.get(...), "key" in step,
# step.items() to work without any changes.
_FIELDS = ("id", "expression", "payload", "step_type", "hash")
def __getitem__(self, key: str):
if key not in self._FIELDS:
raise KeyError(key)
return getattr(self, key)
def get(self, key: str, default=None):
try:
return self[key]
except KeyError:
return default
def keys(self):
return list(self._FIELDS)
def items(self):
return [(k, self[k]) for k in self._FIELDS]
def __contains__(self, key: object) -> bool:
return key in self._FIELDS