Files
agent/app/schemas/changeset.py
2026-02-27 21:28:09 +03:00

63 lines
2.2 KiB
Python

from enum import Enum
from typing import Literal
from typing import Optional
from pydantic import BaseModel, Field, model_validator
class ChangeOp(str, Enum):
CREATE = "create"
UPDATE = "update"
DELETE = "delete"
class PatchHunk(BaseModel):
type: Literal["append_end", "replace_between", "replace_line_equals"]
new_text: str = Field(default="")
start_anchor: Optional[str] = None
end_anchor: Optional[str] = None
old_line: Optional[str] = None
@model_validator(mode="after")
def validate_hunk(self) -> "PatchHunk":
if self.type == "append_end":
if not self.new_text.strip():
raise ValueError("append_end requires non-empty new_text")
return self
if self.type == "replace_between":
if not (self.start_anchor and self.end_anchor):
raise ValueError("replace_between requires start_anchor and end_anchor")
return self
if self.type == "replace_line_equals":
if not self.old_line:
raise ValueError("replace_line_equals requires old_line")
if not self.new_text:
raise ValueError("replace_line_equals requires new_text")
return self
return self
class ChangeItem(BaseModel):
op: ChangeOp
path: str = Field(min_length=1)
base_hash: Optional[str] = None
proposed_content: Optional[str] = None
reason: str = Field(min_length=1, max_length=500)
hunks: list[PatchHunk] = Field(default_factory=list)
@model_validator(mode="after")
def validate_op_fields(self) -> "ChangeItem":
if self.op in (ChangeOp.UPDATE, ChangeOp.DELETE) and not self.base_hash:
raise ValueError("base_hash is required for update/delete")
if self.op in (ChangeOp.CREATE, ChangeOp.UPDATE) and self.proposed_content is None:
raise ValueError("proposed_content is required for create/update")
if self.op == ChangeOp.DELETE and self.proposed_content is not None:
raise ValueError("proposed_content is forbidden for delete")
return self
class ChangeSetPayload(BaseModel):
schema_version: str
task_id: str
changeset: list[ChangeItem]