File size: 3,015 Bytes
29c6586
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# models.py – Typed Models (Discriminated Unions, POMDP Separation)
from typing import Literal, Union, Annotated, Optional
from dataclasses import dataclass
from pydantic import BaseModel, Field, TypeAdapter, field_validator

# ----------------------------------------------------------------------
# Action classes (discriminated union)
# ----------------------------------------------------------------------
class Action(BaseModel):
    action_type: Literal["write_comment", "skip", "done", "ask_question",
                         "propose_fix", "execute", "inspect", "run_linter",
                         "run_tests", "query_docs"]

class WriteComment(Action):
    action_type: Literal["write_comment"] = "write_comment"
    comment_text: str = Field(..., min_length=1)

class Skip(Action):
    action_type: Literal["skip"] = "skip"

class Done(Action):
    action_type: Literal["done"] = "done"

class AskQuestion(Action):
    action_type: Literal["ask_question"] = "ask_question"
    question: str = Field(..., min_length=1)

class ProposeFix(Action):
    action_type: Literal["propose_fix"] = "propose_fix"
    fix_code: str = Field(..., min_length=1)
    @field_validator('fix_code')
    @classmethod
    def not_empty(cls, v: str) -> str:
        if not v.strip():
            raise ValueError('fix_code cannot be empty')
        return v

class Execute(Action):
    action_type: Literal["execute"] = "execute"

class Inspect(Action):
    action_type: Literal["inspect"] = "inspect"

class RunLinter(Action):
    action_type: Literal["run_linter"] = "run_linter"

class RunTests(Action):
    action_type: Literal["run_tests"] = "run_tests"

class QueryDocs(Action):
    action_type: Literal["query_docs"] = "query_docs"
    query_topic: str = Field(..., min_length=1)

# Discriminated union for one‑line polymorphic deserialization
AnyAction = Annotated[
    Union[WriteComment, Skip, Done, AskQuestion, ProposeFix,
          Execute, Inspect, RunLinter, RunTests, QueryDocs],
    Field(discriminator='action_type')
]
action_adapter = TypeAdapter(AnyAction)

# ----------------------------------------------------------------------
# Observation (POMDP – what the agent sees)
# ----------------------------------------------------------------------
@dataclass(slots=True)
class Observation:
    code_snippet: str
    last_tool_output: str = ""
    step: int = 0
    done: bool = False

# ----------------------------------------------------------------------
# Reward (lightweight)
# ----------------------------------------------------------------------
@dataclass(slots=True)
class Reward:
    value: float

# ----------------------------------------------------------------------
# State (full environment state – not exposed to agent)
# ----------------------------------------------------------------------
@dataclass(slots=True)
class State:
    pr_title: str
    pr_description: str
    code_snippet: str
    comments: list[str]
    test_results: Optional[str]
    step: int
    done: bool