feat(fase2): promote blocking validator + IR to src
This commit is contained in:
parent
99f0dd79fe
commit
bbba6e7337
3 changed files with 294 additions and 0 deletions
44
src/portfolio_optimiser/ir.py
Normal file
44
src/portfolio_optimiser/ir.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"""Typed Pydantic IR for a candidate cost-saving measure (B1).
|
||||
|
||||
Pure module — **no** ``agent_framework`` and no solver. The two structural invariants
|
||||
(non-negative quantities; a claimed saving may not exceed the affected items' own total)
|
||||
are enforced at construction by Pydantic, so a malformed proposal can never be built. This
|
||||
IR is the D7-portable contract both the deterministic validator and the LLM->IR generator
|
||||
speak.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
|
||||
class AffectedItem(BaseModel):
|
||||
"""One project cost line a proposal claims to save against."""
|
||||
|
||||
code: str
|
||||
quantity: float = Field(ge=0) # quantities must be >= 0
|
||||
unit_cost: float = Field(gt=0)
|
||||
|
||||
@property
|
||||
def total(self) -> float:
|
||||
return self.quantity * self.unit_cost
|
||||
|
||||
|
||||
class SavingsProposal(BaseModel):
|
||||
"""Typed IR for a candidate cost-saving measure (B1)."""
|
||||
|
||||
project_id: str
|
||||
measure: str
|
||||
affected_items: list[AffectedItem] = Field(min_length=1)
|
||||
claimed_saving_nok: float = Field(gt=0)
|
||||
# code -> (low_unit_cost, high_unit_cost) for the Monte Carlo step; empty = degenerate.
|
||||
assumptions: dict[str, tuple[float, float]] = Field(default_factory=dict)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _claim_within_affected_total(self) -> SavingsProposal:
|
||||
total = sum(item.total for item in self.affected_items)
|
||||
if self.claimed_saving_nok > total:
|
||||
raise ValueError(
|
||||
f"claimed saving {self.claimed_saving_nok} exceeds affected items' total {total}"
|
||||
)
|
||||
return self
|
||||
Loading…
Add table
Add a link
Reference in a new issue