File size: 4,830 Bytes
10ea63e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Parity check: backend/constants.py vs frontend/src/constants.ts.
Run from project root: python3 scripts/check_constants_sync.py
Exits 0 if in sync, 1 if drift detected.
"""
import re
import sys
from pathlib import Path

ROOT = Path(__file__).parent.parent
BACKEND = ROOT / "backend" / "app" / "constants.py"
FRONTEND = ROOT / "frontend" / "src" / "constants.ts"

EMOTION_KEYS = {"calm", "happy", "narrative", "angry", "excited", "sad", "fearful", "command"}
errors = []

# ── backend/constants.py ──────────────────────────────────────────────────────
py_text = BACKEND.read_text()

# reliability: "angry":     {"label": "Angry", ..., "reliability": "medium"},
py_reliability: dict[str, str] = {}
for m in re.finditer(r'"(calm|happy|narrative|angry|excited|sad|fearful|command)":\s*\{[^}]*"reliability":\s*"(high|medium)"', py_text):
    py_reliability[m.group(1)] = m.group(2)

# EMOTION_TEMPLATES speaker blocks: find lines like `"Yash": {` then scan for emotion keys
# until next top-level speaker or closing of the outer dict
def extract_template_keys_py(text: str, speaker: str) -> set[str]:
    # Find the speaker block inside EMOTION_TEMPLATES
    pattern = rf'"{speaker}":\s*\{{'
    m = re.search(pattern, text)
    if not m:
        return set()
    start = m.end()
    # Scan forward collecting emotion keys until balanced brace closes
    depth = 1
    pos = start
    keys: set[str] = set()
    while pos < len(text) and depth > 0:
        if text[pos] == '{':
            depth += 1
        elif text[pos] == '}':
            depth -= 1
            if depth == 0:
                break
        # Check for emotion key line: "calm": or "happy":
        m2 = re.match(r'\s*"(calm|happy|narrative|angry|excited|sad|fearful|command)":\s*', text[pos:pos+30])
        if m2:
            keys.add(m2.group(1))
        pos += 1
    return keys

py_yash_emo = extract_template_keys_py(py_text, "Yash")
py_neha_emo = extract_template_keys_py(py_text, "Neha")

# ── frontend/src/constants.ts ─────────────────────────────────────────────────
ts_text = FRONTEND.read_text()

# reliability: angry: { label: "Angry", emoji: "😠", reliability: "medium" },
ts_reliability: dict[str, str] = {}
for m in re.finditer(r'(calm|happy|narrative|angry|excited|sad|fearful|command):\s*\{[^}]*reliability:\s*"(high|medium)"', ts_text):
    ts_reliability[m.group(1)] = m.group(2)

# EMOTION_TEMPLATES speaker blocks (TypeScript): `Yash: {`
def extract_template_keys_ts(text: str, speaker: str) -> set[str]:
    pattern = rf'\b{speaker}:\s*\{{'
    m = re.search(pattern, text)
    if not m:
        return set()
    start = m.end()
    depth = 1
    pos = start
    keys: set[str] = set()
    while pos < len(text) and depth > 0:
        if text[pos] == '{':
            depth += 1
        elif text[pos] == '}':
            depth -= 1
            if depth == 0:
                break
        m2 = re.match(r'\s*(calm|happy|narrative|angry|excited|sad|fearful|command):\s*', text[pos:pos+30])
        if m2:
            keys.add(m2.group(1))
        pos += 1
    return keys

ts_yash_emo = extract_template_keys_ts(ts_text, "Yash")
ts_neha_emo = extract_template_keys_ts(ts_text, "Neha")

# ── Compare ───────────────────────────────────────────────────────────────────
for key in EMOTION_KEYS:
    py_rel = py_reliability.get(key)
    ts_rel = ts_reliability.get(key)
    if py_rel and ts_rel and py_rel != ts_rel:
        errors.append(f"RELIABILITY MISMATCH '{key}': constants.py={py_rel!r}, constants.ts={ts_rel!r}")
    if py_rel and not ts_rel:
        errors.append(f"MISSING reliability for '{key}' in constants.ts")
    if ts_rel and not py_rel:
        errors.append(f"MISSING reliability for '{key}' in constants.py")

for label, py_keys, ts_keys in [
    ("Yash", py_yash_emo, ts_yash_emo),
    ("Neha", py_neha_emo, ts_neha_emo),
]:
    missing_in_ts = EMOTION_KEYS - ts_keys
    missing_in_py = EMOTION_KEYS - py_keys
    if missing_in_py:
        errors.append(f"MISSING EMOTIONS in {label} (constants.py): {sorted(missing_in_py)}")
    if missing_in_ts:
        errors.append(f"MISSING EMOTIONS in {label} (constants.ts): {sorted(missing_in_ts)}")
    if py_keys != ts_keys:
        errors.append(f"{label} template keys differ: py={sorted(py_keys)}, ts={sorted(ts_keys)}")

if errors:
    print("CONSTANTS SYNC FAILED:")
    for e in errors:
        print(f"  {e}")
    sys.exit(1)
else:
    print("OK β€” constants.py and constants.ts are in sync.")
    sys.exit(0)