Files

76 lines
2.4 KiB
Python
Raw Permalink Normal View History

2026-09-23 22:42:21 +03:00
from dataclasses import dataclass, field
2026-09-22 23:21:59 +03:00
from enum import Enum
from pathlib import Path
2026-09-23 22:42:21 +03:00
from typing import Optional, Literal, Any, Generic
2026-09-22 23:21:59 +03:00
2026-09-23 22:42:21 +03:00
import pandas as pd
from pydantic import field_validator, BaseModel, Field, ConfigDict, field_serializer
from contracts.CodeLanguages import Language
from contracts.GraphAnalyzer import AnalyzeResult, DependencyGraph
2026-09-22 23:21:59 +03:00
2026-09-23 22:42:21 +03:00
@dataclass
2026-09-22 23:21:59 +03:00
class RepoContext:
2026-09-23 22:42:21 +03:00
"""Result object carrying all info a coding agent needs."""
remote_url: str
branch: str
base_branch: str
head_commit: str
is_new_clone: bool
is_new_branch: bool
repo_dir: Path = Field(..., description="Absolute path to the cloned repository")
original_branch: Optional[str] = None
uncommitted_changes: Optional[str] = None # git diff (tracked)
untracked_files: list[str] = field(default_factory=list)
stash_ref: Optional[str] = None
env: dict = field(default_factory=dict)
2026-09-22 23:21:59 +03:00
2026-09-23 22:42:21 +03:00
@field_validator("repo_path")
@classmethod
def repo_must_exist(cls, v: Path) -> Path:
if not v.is_dir():
raise ValueError(f"Repository path does not exist or is not a directory: {v}")
return v.resolve()
2026-09-22 23:21:59 +03:00
class ToolRequirement(BaseModel):
"""Describes a required external tool and how to install it."""
tool_name: str
command: str = Field(..., description="CLI command that must be on PATH")
install_guide: str = Field(..., description="Markdown installation instructions")
docs_url: Optional[str] = None
class AnalysisError(BaseModel):
"""Structured error returned when analysis cannot proceed."""
error_code: str
message: str
missing_tools: list[ToolRequirement] = Field(default_factory=list)
language: Optional[Language] = None
entrypoint: Optional[str] = None
class AnalysisInput(BaseModel):
"""Input schema for the analyzer."""
2026-09-23 22:42:21 +03:00
repo: RepoContext
2026-09-22 23:21:59 +03:00
class AnalysisResult(BaseModel):
"""Top-level result — either a graph or an error."""
2026-09-23 22:42:21 +03:00
model_config = ConfigDict(arbitrary_types_allowed=True)
2026-09-22 23:21:59 +03:00
success: bool
graph: Optional[DependencyGraph] = None
error: Optional[AnalysisError] = None
2026-09-23 22:42:21 +03:00
leaf_clusters: Any = None
res_all: AnalyzeResult | None = None
@field_serializer("leaf_clusters")
def _ser_leaf_clusters(self, v: Any, _info):
if isinstance(v, pd.DataFrame):
return v.to_dict(orient="records")
return v # already JSON-safe