File size: 2,497 Bytes
8b4c1a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee2d45e
8b4c1a6
4c557cd
8b4c1a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from pydantic import BaseModel, Field
from typing import Literal, Optional, List, Dict, Any
from enum import Enum


# === Enums ===

class ActionType(str, Enum):
    REPORT_FINDING = "report_finding"
    REQUEST_CONTEXT = "request_context"
    REQUEST_FILE_LIST = "request_file_list"
    MARK_COMPLETE = "mark_complete"


class Severity(str, Enum):
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"
    INFO = "info"


class Difficulty(str, Enum):
    EASY = "easy"
    MEDIUM = "medium"
    HARD = "hard"


# === Observation Space ===

class FileContent(BaseModel):
    filename: str
    content: str
    language: str


class ReviewContext(BaseModel):
    task_id: str
    task_description: str
    difficulty: Difficulty
    files: List[FileContent]
    available_files: List[str]
    review_checklist: List[str]
    max_steps: int
    current_step: int


class Observation(BaseModel):
    context: ReviewContext
    findings_so_far: List[Dict[str, Any]]
    feedback: Optional[str] = None


# === Action Space ===

class Finding(BaseModel):
    file: str
    line: Optional[int] = None
    rule_id: str
    severity: Severity
    description: str


class Action(BaseModel):
    action_type: ActionType
    finding: Optional[Finding] = None
    filename: Optional[str] = None


# === Reward ===

class Reward(BaseModel):
    score: float = Field(ge=0.0, le=1.0)
    breakdown: Dict[str, Any]


# === API Request/Response Models ===

class ResetRequest(BaseModel):
    task_id: Optional[str] = None
    scenario_id: Optional[str] = None
    adaptive: bool = False


class StepRequest(BaseModel):
    action: Action


class StepResponse(BaseModel):
    observation: Observation
    reward: float
    done: bool
    info: Dict[str, Any]


class ResetResponse(BaseModel):
    observation: Observation
    info: Dict[str, Any]


class TaskInfo(BaseModel):
    id: str
    name: str
    description: str
    difficulty: Difficulty
    max_steps: int


# === Internal: Ground Truth (not exposed via API) ===

class GroundTruthFinding(BaseModel):
    file: str
    line: Optional[int] = None
    rule_id: str
    severity: Severity
    description: str
    match_key: str
    category: Optional[str] = None


class ScenarioConfig(BaseModel):
    scenario_id: str
    task_id: str
    description: str
    files: Dict[str, FileContent]
    initial_files: List[str]
    available_files: List[str]
    ground_truth: List[GroundTruthFinding]
    review_checklist: List[str]