vajeeda's picture
feat(phase12): RetentionCurveSimulator, R10, 150-sample dataset, model trained, 14 tests PASS, gate PASS
79cb04a
Raw
History Blame Contribute Delete
3.1 kB
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel
from viral_script_engine.agents.critic import CritiqueClaim
from viral_script_engine.environment.actions import ArbitratorAction
_WEIGHTS: Dict[str, float] = {
"r1": 0.12, "r2": 0.10, "r3": 0.10,
"r4": 0.10, "r5": 0.08, "r6": 0.07,
"r7": 0.07, "r8": 0.08, "r9": 0.08,
"r10": 0.10,
}
class RewardComponents(BaseModel):
r1_hook_strength: Optional[float] = None
r2_coherence: Optional[float] = None
r3_cultural_alignment: Optional[float] = None
r4_debate_resolution: Optional[float] = None
r5_defender_preservation: Optional[float] = None
r6_safety: Optional[float] = None
r7_originality: Optional[float] = None
r8_persona_fit: Optional[float] = None # Phase 8: creator persona fit
r9_platform_pacing: Optional[float] = None # Phase 9: platform pacing fit
r10_retention_curve: Optional[float] = None # Phase 12: retention curve reward
process_reward: Optional[float] = None # fired before rewrite (Phase 7)
anti_gaming_penalty: float = 0.0
total: float = 0.0
def compute_total(self) -> float:
vals = {
"r1": self.r1_hook_strength,
"r2": self.r2_coherence,
"r3": self.r3_cultural_alignment,
"r4": self.r4_debate_resolution,
"r5": self.r5_defender_preservation,
"r6": self.r6_safety,
"r7": self.r7_originality,
"r8": self.r8_persona_fit,
"r9": self.r9_platform_pacing,
"r10": self.r10_retention_curve,
}
active = {k: v for k, v in vals.items() if v is not None}
if not active:
self.total = 0.0
return 0.0
norm = sum(_WEIGHTS[k] for k in active)
weighted = sum(_WEIGHTS[k] * v for k, v in active.items()) / norm
self.total = max(0.0, min(1.0, weighted - self.anti_gaming_penalty))
return self.total
class DebateRound(BaseModel):
step_num: int
critic_claims: List[CritiqueClaim]
defender_response: Optional[Any] = None
arbitrator_action: Optional[ArbitratorAction] = None
rewrite_diff: Optional[str] = None
reward_components: Optional[RewardComponents] = None
moderation_output: Optional[Any] = None
originality_output: Optional[Any] = None
reasoning_chain: Optional[Any] = None # Phase 7: parsed reasoning chain dict
class Observation(BaseModel):
current_script: str
original_script: str
region: str
platform: str
niche: str
step_num: int
max_steps: int
debate_history: List[DebateRound]
reward_components: RewardComponents
difficulty_level: str
episode_id: str
current_moderation_flags: List[Any] = []
current_originality_flags: List[Any] = []
creator_profile: Optional[Any] = None # Phase 8: CreatorProfile dict
creator_history: Optional[Any] = None # Phase 11: CreatorHistoryBuffer (None for first-timers)
history_context: Optional[str] = None # Phase 11: formatted prompt string