174 lines
6.2 KiB
Python
174 lines
6.2 KiB
Python
# SPDX-FileCopyrightText: 2026 Art Sea LLC
|
|
#
|
|
# SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
"""Language-neutral input for one bounded code-edit attempt."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from band_aider.judgement.test_runner_strategy import TestRunOutcome
|
|
|
|
from dataclasses import dataclass, field, replace
|
|
from enum import IntEnum
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING, Sequence, TypeVar
|
|
|
|
from band_aider.strategies.compiler import CompileError, StaticCompileError
|
|
from band_aider.context.validation_receipts import ValidationReceipts
|
|
from band_aider.judgement.code_editor import EditorRecord
|
|
|
|
if TYPE_CHECKING:
|
|
from band_aider.context.class_meta import ClassMeta
|
|
from band_aider.context.cot_task_generator import CotTaskRecord
|
|
from band_aider.context.provenance import FutureGuidance
|
|
from band_aider.strategies.compiler import FrontierProvider
|
|
|
|
|
|
@dataclass
|
|
class AiderEditRequest:
|
|
"""Carry the current task, edit scope, and runtime workflow context."""
|
|
|
|
authoritative_prompt: str
|
|
editable_files: list[Path]
|
|
readonly_files: list[Path]
|
|
pinned_context: list[str] = field(default_factory=list)
|
|
backlog_guidance: list[str] = field(default_factory=list)
|
|
future_guidance: tuple[FutureGuidance, ...] = ()
|
|
previous_cot_records: tuple[CotTaskRecord, ...] = ()
|
|
failure_error_message: str = ""
|
|
failure_stack_trace: list[str] = field(default_factory=list)
|
|
failure_raw_output: str = ""
|
|
historical_logs: list[str] = field(default_factory=list)
|
|
change_logs: list[str] = field(default_factory=list)
|
|
allowed_additional_roots: list[Path] = field(default_factory=list)
|
|
is_remediation: bool = False
|
|
remediation_originator_test: ClassMeta | None = None
|
|
context: type | None = None
|
|
frontier_provider: FrontierProvider | None = None
|
|
failure_test_location: str = ""
|
|
failure_test_name: str = ""
|
|
failure_outcome: TestRunOutcome | None = None
|
|
failure_run_info: str = ""
|
|
validation_receipts: ValidationReceipts = field(default_factory=ValidationReceipts)
|
|
editor_records: list[EditorRecord] = field(default_factory=list)
|
|
|
|
@property
|
|
def work_logs(self) -> str:
|
|
"""Render retained edits as history without mutating durable backlog guidance."""
|
|
|
|
return "Work Logs\n\n" + "\n\n".join(map(EditorRecord.render, self.editor_records)) if self.editor_records else ""
|
|
|
|
@property
|
|
def guidance(self) -> list[str]:
|
|
"""Append current Work Logs to the ordinary guidance presented to this request's consumers."""
|
|
|
|
return [*self.backlog_guidance, *filter(None, (self.work_logs,))]
|
|
|
|
def validation_view(self) -> AiderEditRequest:
|
|
"""Project current matching execution without mutating captured history."""
|
|
file_scope = self.failure_outcome is not None and self.failure_outcome.is_error
|
|
receipt = self.validation_receipts.current_for(self.failure_test_location,
|
|
"" if file_scope else self.failure_test_name, file_scope=file_scope)
|
|
# Unknown identities and older observations cannot erase an incoming obligation.
|
|
if receipt is None:
|
|
return self
|
|
failure = receipt.failure
|
|
return replace(self, failure_error_message=failure.error_message if failure else "",
|
|
failure_stack_trace=list(failure.stack_trace) if failure else [],
|
|
failure_raw_output=failure.raw_output if failure else "",
|
|
failure_outcome=failure.outcome if failure else self.failure_outcome,
|
|
failure_run_info=failure.run_info if failure else "")
|
|
|
|
class AiderFailureMode(IntEnum):
|
|
"""Set the highest failure-evidence level visible to an edit request.
|
|
|
|
Example::
|
|
|
|
mode = AiderFailureMode.STATIC
|
|
"""
|
|
|
|
DEFAULT = 0 # PATCH
|
|
STATIC = 1 # COMPILE
|
|
SEMANTIC = 2
|
|
|
|
@classmethod
|
|
def attempt_level(
|
|
cls,
|
|
errors: Sequence[CompileError],
|
|
*,
|
|
default: AiderFailureMode,
|
|
) -> AiderFailureMode:
|
|
"""Return the earliest validation level reached by one compiler attempt."""
|
|
|
|
return min(
|
|
map(
|
|
lambda error: (
|
|
cls.STATIC
|
|
if isinstance(error, StaticCompileError)
|
|
else cls.SEMANTIC
|
|
),
|
|
errors,
|
|
),
|
|
default=default,
|
|
)
|
|
|
|
|
|
_Visible = TypeVar("_Visible")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AiderEditRequestMask:
|
|
"""Project one request without changing its caller-owned evidence or collections.
|
|
|
|
Example::
|
|
|
|
view = AiderEditRequestMask(request, AiderFailureMode.STATIC).projected()
|
|
"""
|
|
|
|
original: AiderEditRequest
|
|
mode: AiderFailureMode
|
|
|
|
@property
|
|
def has_semantic_failure(self) -> bool:
|
|
"""Return whether the request carries runtime semantic failure evidence."""
|
|
|
|
return any(
|
|
value.strip()
|
|
for value in (
|
|
self.original.failure_error_message,
|
|
*self.original.failure_stack_trace,
|
|
self.original.failure_raw_output,
|
|
)
|
|
)
|
|
|
|
def visible(
|
|
self,
|
|
*,
|
|
patch_level: _Visible,
|
|
compile_level: _Visible,
|
|
semantic_level: _Visible,
|
|
) -> tuple[_Visible, ...]:
|
|
"""Return the PATCH < COMPILE < SEMANTIC prefix through this workflow level."""
|
|
|
|
levels = (patch_level, compile_level, semantic_level)
|
|
return levels[: self.mode + 1]
|
|
|
|
def projected(self) -> AiderEditRequest:
|
|
"""Materialize the selected shallow view while retaining the original request."""
|
|
|
|
hidden_semantic: dict[str, object] = {
|
|
"failure_error_message": "",
|
|
"failure_stack_trace": [],
|
|
"failure_raw_output": "",
|
|
"failure_run_info": "",
|
|
}
|
|
# Lower repair levels hide backlog guidance only with runtime failure evidence.
|
|
if self.has_semantic_failure:
|
|
hidden_semantic["backlog_guidance"] = []
|
|
lower_level = replace(self.original, **hidden_semantic)
|
|
semantic_level = replace(self.original, backlog_guidance=[])
|
|
return self.visible(
|
|
patch_level=lower_level,
|
|
compile_level=lower_level,
|
|
semantic_level=semantic_level,
|
|
)[-1]
|