File size: 5,641 Bytes
ebae6ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import json
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Dict, List, Optional

from viral_script_engine.agents.llm_backend import LLMBackend
from viral_script_engine.escalation.difficulty_tracker import DifficultyTracker

_SYSTEM_PROMPT_TEMPLATE = """You are designing training challenges for an RL agent learning to improve video scripts.
The agent has mastered detecting and fixing '{mastered_class}' flaws.

Generate a harder challenge:
1. Create a script with a '{mastered_class}' flaw that is MORE SUBTLE than the example
2. Add a CONFLICTING CONSTRAINT: fixing the '{mastered_class}' flaw should create or
   worsen a different flaw from: {other_classes}
3. Difficulty: HARD — agent must learn action ordering, not just action selection

A challenge is good when: fixing the obvious flaw first leads to WORSE total reward
than fixing a less obvious flaw first.

Return JSON only:
{{
  "script_text": "...",
  "dominant_flaw": "...",
  "conflicting_flaw": "...",
  "why_its_harder": "one sentence",
  "optimal_action_order": ["action1", "action2"],
  "trap_action": "action that looks correct but degrades total reward"
}}"""

_USER_PROMPT_TEMPLATE = """MASTERED CLASS: {mastered_class}
REGION: {region}
PLATFORM: {platform}

ORIGINAL SCRIPT EXAMPLE (already mastered at this difficulty):
{original_script_example}

Generate a HARDER escalated challenge where fixing the dominant flaw immediately is a trap.
Respond with JSON only."""


@dataclass
class EscalatedChallenge:
    source_class: str
    script_text: str
    region: str
    platform: str
    dominant_flaw: str
    conflicting_flaw: str
    why_its_harder: str
    optimal_action_order: List[str]
    trap_action: str
    difficulty_level: str = "self_generated"
    generated_at: str = ""

    def __post_init__(self):
        if not self.generated_at:
            self.generated_at = datetime.now(timezone.utc).isoformat()

    def to_script_dict(self) -> dict:
        return {
            "script_id": f"escalated_{self.source_class}_{self.generated_at[:10]}",
            "script_text": self.script_text,
            "region": self.region,
            "platform": self.platform,
            "niche": "escalated",
            "difficulty": "self_generated",
        }


class CriticEscalationEngine:
    def __init__(self, backend: str = "anthropic", model_name: str = "claude-haiku-4-5-20251001"):
        self.llm = LLMBackend(backend=backend, model_name=model_name)
        self.escalated_classes: Dict[str, List[EscalatedChallenge]] = {}

    @staticmethod
    def _extract_json(text: str) -> dict:
        import re
        text = text.strip()
        text = re.sub(r"^```(?:json)?", "", text).strip()
        text = re.sub(r"```$", "", text).strip()
        try:
            return json.loads(text)
        except json.JSONDecodeError:
            pass
        start = text.find("{")
        if start != -1:
            depth, in_str, esc = 0, False, False
            for i, c in enumerate(text[start:], start):
                if esc:
                    esc = False
                    continue
                if c == "\\" and in_str:
                    esc = True
                    continue
                if c == '"':
                    in_str = not in_str
                elif not in_str:
                    if c == "{":
                        depth += 1
                    elif c == "}":
                        depth -= 1
                        if depth == 0:
                            try:
                                return json.loads(text[start: i + 1])
                            except json.JSONDecodeError:
                                break
        raise ValueError(f"No valid JSON in escalation response: {text[:300]}")

    def escalate(
        self,
        mastered_class: str,
        original_script_example: str,
        region: str,
        platform: str,
    ) -> EscalatedChallenge:
        other_classes = [c for c in DifficultyTracker.CRITIQUE_CLASSES if c != mastered_class]
        system_prompt = _SYSTEM_PROMPT_TEMPLATE.format(
            mastered_class=mastered_class,
            other_classes=", ".join(other_classes),
        )
        user_prompt = _USER_PROMPT_TEMPLATE.format(
            mastered_class=mastered_class,
            region=region,
            platform=platform,
            original_script_example=original_script_example,
        )

        raw = self.llm.generate(system_prompt, user_prompt, max_tokens=1024)
        data = self._extract_json(raw)

        challenge = EscalatedChallenge(
            source_class=mastered_class,
            script_text=data["script_text"],
            region=region,
            platform=platform,
            dominant_flaw=data["dominant_flaw"],
            conflicting_flaw=data["conflicting_flaw"],
            why_its_harder=data["why_its_harder"],
            optimal_action_order=data.get("optimal_action_order", []),
            trap_action=data.get("trap_action", ""),
        )

        self.escalated_classes.setdefault(mastered_class, []).append(challenge)
        return challenge

    def get_next_challenge(self, difficulty_tracker: DifficultyTracker) -> Optional[EscalatedChallenge]:
        mastered = difficulty_tracker.get_mastered_classes()
        if not mastered:
            return None

        for cls in mastered:
            challenges = self.escalated_classes.get(cls, [])
            if challenges:
                return challenges[-1]

        return None

    def total_generated(self) -> int:
        return sum(len(v) for v in self.escalated_classes.values())