File size: 4,757 Bytes
cd7807c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python
"""
Quick validation of stdout format compliance
Run: python validate_format.py <output_file>
"""

import sys
import re
import json

def validate_format(lines):
    """Validate [START], [STEP], [END] format"""
    
    errors = []
    
    # Must have exactly one [START]
    start_lines = [l for l in lines if l.startswith("[START]")]
    if len(start_lines) != 1:
        errors.append(f"Expected 1 [START] line, found {len(start_lines)}")
    else:
        start = start_lines[0]
        # Validate format: [START] task=X env=Y model=Z
        if not re.match(r'^\[START\]\s+task=\S+\s+env=\S+\s+model=\S+', start):
            errors.append(f"[START] format invalid: {start}")
    
    # Must have at least one [STEP]
    step_lines = [l for l in lines if l.startswith("[STEP]")]
    if len(step_lines) == 0:
        errors.append("No [STEP] lines found")
    else:
        # Validate each [STEP]
        for step_line in step_lines:
            # Format: [STEP] step=N action=... reward=X.XX done=true|false error=...|null
            if not re.match(r'^\[STEP\]\s+step=\d+\s+', step_line):
                errors.append(f"Invalid [STEP] format: {step_line[:80]}")
            
            # Check required fields
            if 'step=' not in step_line:
                errors.append(f"[STEP] missing step field: {step_line[:80]}")
            if 'action=' not in step_line:
                errors.append(f"[STEP] missing action field: {step_line[:80]}")
            if 'reward=' not in step_line:
                errors.append(f"[STEP] missing reward field: {step_line[:80]}")
            if 'done=' not in step_line:
                errors.append(f"[STEP] missing done field: {step_line[:80]}")
            if 'error=' not in step_line:
                errors.append(f"[STEP] missing error field: {step_line[:80]}")
            
            # Check done is boolean
            if 'done=true' not in step_line and 'done=false' not in step_line:
                errors.append(f"[STEP] done must be true|false: {step_line[:80]}")
    
    # Must have exactly one [END]
    end_lines = [l for l in lines if l.startswith("[END]")]
    if len(end_lines) != 1:
        errors.append(f"Expected 1 [END] line, found {len(end_lines)}")
    else:
        end = end_lines[0]
        # Format: [END] success=true|false steps=N score=X.XXX rewards=...
        if not re.match(r'^\[END\]\s+success=', end):
            errors.append(f"[END] format invalid: {end}")
        
        # Check required fields
        required_fields = ['success=', 'steps=', 'score=', 'rewards=']
        for field in required_fields:
            if field not in end:
                errors.append(f"[END] missing {field} field")
        
        # Check success is boolean
        if 'success=true' not in end and 'success=false' not in end:
            errors.append(f"[END] success must be true|false: {end}")
        
        # Check score is float
        score_match = re.search(r'score=([\d.]+)', end)
        if not score_match:
            errors.append(f"[END] invalid score format: {end}")
        else:
            try:
                score = float(score_match.group(1))
                if not (0.1 <= score <= 0.9):
                    errors.append(f"[END] score must be between 0.1 and 0.9, got {score}")
            except ValueError:
                errors.append(f"[END] score not a valid float: {score_match.group(1)}")
        
        # Check rewards format (comma-separated floats)
        rewards_match = re.search(r'rewards=([\d.,]+)', end)
        if not rewards_match:
            errors.append(f"[END] invalid rewards format: {end}")
    
    # Check single-line format (no embedded newlines in fields)
    for line in lines:
        if line.startswith("["):
            # Action field might have spaces but shouldn't have newlines
            if '\n' in line.split('error=')[-1]:
                errors.append(f"Line contains embedded newlines: {line[:80]}")
    
    return errors


if __name__ == "__main__":
    if len(sys.argv) > 1:
        with open(sys.argv[1], 'r') as f:
            lines = f.read().strip().split('\n')
    else:
        lines = sys.stdin.read().strip().split('\n')
    
    errors = validate_format(lines)
    
    if errors:
        print("❌ Format validation FAILED:")
        for i, error in enumerate(errors, 1):
            print(f"  {i}. {error}")
        sys.exit(1)
    else:
        print("✓ Format validation PASSED")
        print(f"  {len([l for l in lines if l.startswith('[START]')])} [START] lines")
        print(f"  {len([l for l in lines if l.startswith('[STEP]')])} [STEP] lines")
        print(f"  {len([l for l in lines if l.startswith('[END]')])} [END] lines")
        sys.exit(0)