File size: 3,097 Bytes
41ea373
 
 
 
 
 
 
 
79cb04a
 
 
 
41ea373
 
 
 
 
 
 
 
 
5c28dc0
 
dfa9070
79cb04a
 
 
41ea373
 
 
 
 
 
 
 
 
 
5c28dc0
 
dfa9070
998d987
79cb04a
41ea373
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5c28dc0
 
0e4f105
41ea373
 
 
 
 
 
 
 
 
 
 
 
 
 
5c28dc0
 
dfa9070
09f7d63
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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