generated from coulomb/repo-seed
Route auto review requests to agentic review
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from repo_registry.acceptance.agentic import AgenticReviewer, AgenticReviewRequest
|
||||
from repo_registry.acceptance.criteria import (
|
||||
active_quality_criteria_version,
|
||||
criteria_registry_dict,
|
||||
@@ -14,6 +15,8 @@ from repo_registry.acceptance.gates import (
|
||||
|
||||
__all__ = [
|
||||
"active_quality_criteria_version",
|
||||
"AgenticReviewer",
|
||||
"AgenticReviewRequest",
|
||||
"blocking_quality_gate_outcomes",
|
||||
"criteria_registry_dict",
|
||||
"criteria_registry_json",
|
||||
|
||||
24
src/repo_registry/acceptance/agentic.py
Normal file
24
src/repo_registry/acceptance/agentic.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Protocol
|
||||
|
||||
from repo_registry.acceptance.gates import QualityGateOutcome
|
||||
from repo_registry.core.models import CandidateGraph, Repository
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AgenticReviewRequest:
|
||||
repository: Repository
|
||||
candidate_graph: CandidateGraph
|
||||
criteria_version: str
|
||||
quality_gate_outcomes: list[QualityGateOutcome]
|
||||
context: str
|
||||
|
||||
|
||||
class AgenticReviewer(Protocol):
|
||||
reviewer_id: str
|
||||
policy_version: str
|
||||
|
||||
def review(self, request: AgenticReviewRequest) -> None:
|
||||
"""Review a candidate graph and record decisions through the caller."""
|
||||
@@ -40,9 +40,9 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
rebuild.add_argument("--dry-run", action="store_true", help="Preview without clearing approved characteristics.")
|
||||
rebuild.add_argument("--no-llm", action="store_true", help="Disable configured LLM assistance.")
|
||||
rebuild.add_argument(
|
||||
"--trusted-auto-approve",
|
||||
"--agentic-review",
|
||||
action="store_true",
|
||||
help="Run trusted auto-approval after a confirmed rebuild.",
|
||||
help="Request configured agentic review after a confirmed rebuild.",
|
||||
)
|
||||
rebuild.add_argument(
|
||||
"--confirm",
|
||||
@@ -146,7 +146,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
self_assess.add_argument(
|
||||
"--agentic-review",
|
||||
action="store_true",
|
||||
help="Reserved for a configured agentic reviewer; currently errors when requested.",
|
||||
help="Request configured agentic review; leaves candidates pending when none is configured.",
|
||||
)
|
||||
self_assess.add_argument(
|
||||
"--fail-on-regression",
|
||||
@@ -213,11 +213,11 @@ def rebuild_characteristics_command(
|
||||
confirm=not dry_run,
|
||||
use_llm_assistance=not args.no_llm,
|
||||
)
|
||||
if args.trusted_auto_approve and not dry_run and result.analysis_run.status == "completed":
|
||||
service.trusted_auto_approve_candidate_graph(
|
||||
if args.agentic_review and not dry_run and result.analysis_run.status == "completed":
|
||||
service.request_agentic_review(
|
||||
repository.id,
|
||||
result.analysis_run.id,
|
||||
notes="CLI trusted auto-approve after rebuild.",
|
||||
notes="CLI agentic review request after rebuild.",
|
||||
)
|
||||
print(rebuild_summary_line(service, result, args))
|
||||
return 0
|
||||
@@ -258,8 +258,6 @@ def self_assess_command(
|
||||
args: argparse.Namespace,
|
||||
parser: argparse.ArgumentParser,
|
||||
) -> int:
|
||||
if args.agentic_review:
|
||||
parser.error("agentic review is not configured yet")
|
||||
service = service_from_args(args)
|
||||
source_path = Path(args.source_path).expanduser().resolve()
|
||||
if not source_path.is_dir():
|
||||
@@ -269,6 +267,7 @@ def self_assess_command(
|
||||
repository.id,
|
||||
source_path=str(source_path),
|
||||
use_llm_assistance=not args.no_llm,
|
||||
agentic_review=args.agentic_review,
|
||||
trusted_auto_approve=False,
|
||||
)
|
||||
if summary.analysis_run.status != "completed":
|
||||
|
||||
@@ -5,8 +5,12 @@ from dataclasses import asdict, replace
|
||||
from typing import Any
|
||||
|
||||
from repo_registry.acceptance import (
|
||||
AgenticReviewer,
|
||||
AgenticReviewRequest,
|
||||
active_quality_criteria_version,
|
||||
blocking_quality_gate_outcomes,
|
||||
evaluate_candidate_capability_quality,
|
||||
evaluate_candidate_graph_quality,
|
||||
)
|
||||
from repo_registry.core.models import (
|
||||
AbilitySummary,
|
||||
@@ -57,6 +61,7 @@ class RegistryService:
|
||||
ingestion: GitIngestionService | None = None,
|
||||
llm_extractor: LLMCandidateExtractor | None = None,
|
||||
embedding_provider: EmbeddingProvider | None = None,
|
||||
agentic_reviewer: AgenticReviewer | None = None,
|
||||
) -> None:
|
||||
self.store = store
|
||||
self.scanner = DeterministicScanner()
|
||||
@@ -67,6 +72,7 @@ class RegistryService:
|
||||
self.llm_extractor = llm_extractor
|
||||
self.llm_mapper = LLMExtractionMapper()
|
||||
self.embedding_provider = embedding_provider
|
||||
self.agentic_reviewer = agentic_reviewer
|
||||
|
||||
def register_repository(
|
||||
self,
|
||||
@@ -135,6 +141,7 @@ class RegistryService:
|
||||
source_path: str | None = None,
|
||||
use_cached_checkout: bool = False,
|
||||
use_llm_assistance: bool = True,
|
||||
agentic_review: bool = False,
|
||||
trusted_auto_approve: bool = False,
|
||||
access_username: str | None = None,
|
||||
access_password: str | None = None,
|
||||
@@ -241,13 +248,20 @@ class RegistryService:
|
||||
f"from {candidate_source} candidate generation."
|
||||
),
|
||||
)
|
||||
if trusted_auto_approve:
|
||||
self.trusted_auto_approve_candidate_graph(
|
||||
if agentic_review or trusted_auto_approve:
|
||||
legacy_note = (
|
||||
" Deprecated trusted_auto_approve request was routed to "
|
||||
"agentic review."
|
||||
if trusted_auto_approve
|
||||
else ""
|
||||
)
|
||||
self.request_agentic_review(
|
||||
repository_id,
|
||||
completed_run.id,
|
||||
notes=(
|
||||
"Trusted auto-populate mode reviewed candidate graph "
|
||||
f"after {candidate_source} candidate generation."
|
||||
"Agentic review requested after "
|
||||
f"{candidate_source} candidate generation."
|
||||
f"{legacy_note}"
|
||||
),
|
||||
)
|
||||
log_operation(
|
||||
@@ -602,6 +616,50 @@ class RegistryService:
|
||||
)
|
||||
return self.store.get_ability_map(repository_id)
|
||||
|
||||
def request_agentic_review(
|
||||
self,
|
||||
repository_id: int,
|
||||
analysis_run_id: int,
|
||||
*,
|
||||
notes: str = "",
|
||||
) -> CandidateGraph:
|
||||
graph = self.store.get_candidate_graph(repository_id, analysis_run_id)
|
||||
gate_outcomes = evaluate_candidate_graph_quality(graph)
|
||||
criteria_version = active_quality_criteria_version()
|
||||
if self.agentic_reviewer is None:
|
||||
self.store.create_review_decision(
|
||||
repository_id,
|
||||
analysis_run_id,
|
||||
action="agentic_review_unconfigured",
|
||||
notes=(
|
||||
f"{notes} No agentic reviewer is configured; candidates "
|
||||
"remain pending human review. "
|
||||
f"criteria_version={criteria_version}; "
|
||||
f"quality_gate_outcomes={len(gate_outcomes)}."
|
||||
).strip(),
|
||||
)
|
||||
return graph
|
||||
request = AgenticReviewRequest(
|
||||
repository=graph.repository,
|
||||
candidate_graph=graph,
|
||||
criteria_version=criteria_version,
|
||||
quality_gate_outcomes=gate_outcomes,
|
||||
context="candidate-characteristic-acceptance",
|
||||
)
|
||||
self.agentic_reviewer.review(request)
|
||||
self.store.create_review_decision(
|
||||
repository_id,
|
||||
analysis_run_id,
|
||||
action="agentic_review_completed",
|
||||
notes=(
|
||||
f"{notes} reviewer={self.agentic_reviewer.reviewer_id}; "
|
||||
f"policy_version={self.agentic_reviewer.policy_version}; "
|
||||
f"criteria_version={criteria_version}; "
|
||||
f"quality_gate_outcomes={len(gate_outcomes)}."
|
||||
).strip(),
|
||||
)
|
||||
return self.store.get_candidate_graph(repository_id, analysis_run_id)
|
||||
|
||||
def _trusted_auto_approve_capability_safe(
|
||||
self,
|
||||
capability: CandidateCapability,
|
||||
|
||||
@@ -423,6 +423,8 @@ def _known_regression_patterns(
|
||||
|
||||
|
||||
def _execution_mode(decisions: list[ReviewDecision]) -> str:
|
||||
if any(decision.action.startswith("agentic_review") for decision in decisions):
|
||||
return "agentic-review"
|
||||
if any(decision.action == "trusted_auto_approve_candidate_graph" for decision in decisions):
|
||||
return "trusted-auto-review"
|
||||
if any(decision.action == "llm_extraction_used" for decision in decisions):
|
||||
@@ -439,6 +441,12 @@ def _candidate_source(decisions: list[ReviewDecision]) -> str:
|
||||
|
||||
|
||||
def _acceptance_mode(decisions: list[ReviewDecision]) -> str:
|
||||
agentic_decision = next(
|
||||
(decision for decision in decisions if decision.action.startswith("agentic_review")),
|
||||
None,
|
||||
)
|
||||
if agentic_decision is not None:
|
||||
return agentic_decision.action
|
||||
if any(decision.action == "trusted_auto_approve_candidate_graph" for decision in decisions):
|
||||
return "trusted_auto_approve_candidate_graph"
|
||||
if any(decision.action == "approve_candidate_graph" for decision in decisions):
|
||||
|
||||
@@ -295,6 +295,7 @@ def create_analysis_run(
|
||||
source_path=payload.source_path,
|
||||
use_cached_checkout=payload.use_cached_checkout,
|
||||
use_llm_assistance=payload.use_llm_assistance,
|
||||
agentic_review=payload.agentic_review,
|
||||
trusted_auto_approve=payload.trusted_auto_approve,
|
||||
access_username=payload.access_username,
|
||||
access_password=payload.access_password,
|
||||
|
||||
@@ -214,7 +214,20 @@ class AnalysisRunCreate(BaseModel):
|
||||
source_path: str | None = None
|
||||
use_cached_checkout: bool = False
|
||||
use_llm_assistance: bool = True
|
||||
trusted_auto_approve: bool = False
|
||||
agentic_review: bool = Field(
|
||||
default=False,
|
||||
description=(
|
||||
"Request configured agentic review after analysis; candidates remain "
|
||||
"pending when no reviewer is configured."
|
||||
),
|
||||
)
|
||||
trusted_auto_approve: bool = Field(
|
||||
default=False,
|
||||
description=(
|
||||
"Deprecated compatibility input. Requests are routed to agentic "
|
||||
"review and do not deterministically approve candidates."
|
||||
),
|
||||
)
|
||||
access_username: str | None = None
|
||||
access_password: str | None = Field(default=None, repr=False)
|
||||
|
||||
@@ -225,7 +238,7 @@ class AnalysisRunCreate(BaseModel):
|
||||
{"source_path": "/path/to/local/repository"},
|
||||
{"use_cached_checkout": True},
|
||||
{"use_llm_assistance": False},
|
||||
{"trusted_auto_approve": True},
|
||||
{"agentic_review": True},
|
||||
{
|
||||
"access_username": "git-user",
|
||||
"access_password": "access-token",
|
||||
|
||||
@@ -416,7 +416,7 @@ def render_repository_index(
|
||||
<label>Password or access token <input name="access_password" type="password" autocomplete="current-password" placeholder="Used for this Git operation only"></label>
|
||||
<label class="checkbox"><input type="checkbox" name="explore_after_registration" value="1" checked> Explore after registration</label>
|
||||
<label class="checkbox"><input type="checkbox" name="use_llm_assistance" value="1" checked> Use LLM assistance if configured</label>
|
||||
<label class="checkbox"><input type="checkbox" name="trusted_auto_approve" value="1"> Trusted auto-populate after analysis</label>
|
||||
<label class="checkbox"><input type="checkbox" name="agentic_review" value="1"> Request agentic review after analysis</label>
|
||||
<div class="actions">
|
||||
<button type="submit">Register</button>
|
||||
<span data-pending>Registering repository...</span>
|
||||
@@ -1440,7 +1440,7 @@ def create_repository_from_form(
|
||||
access_password: str = Form(""),
|
||||
explore_after_registration: str | None = Form(None),
|
||||
use_llm_assistance: str | None = Form(None),
|
||||
trusted_auto_approve: str | None = Form(None),
|
||||
agentic_review: str | None = Form(None),
|
||||
service: RegistryService = Depends(get_service),
|
||||
):
|
||||
try:
|
||||
@@ -1460,7 +1460,7 @@ def create_repository_from_form(
|
||||
summary = service.analyze_repository(
|
||||
repository.id,
|
||||
use_llm_assistance=bool(use_llm_assistance),
|
||||
trusted_auto_approve=bool(trusted_auto_approve),
|
||||
agentic_review=bool(agentic_review),
|
||||
access_username=access_username or None,
|
||||
access_password=access_password or None,
|
||||
)
|
||||
@@ -1529,7 +1529,7 @@ def repository_detail(
|
||||
<label>Override source path <input name="source_path" placeholder="Optional local path"></label>
|
||||
<label class="checkbox"><input type="checkbox" name="use_cached_checkout" value="1"> Analyze cached checkout without fetching upstream</label>
|
||||
<label class="checkbox"><input type="checkbox" name="use_llm_assistance" value="1" checked> Use LLM assistance if configured</label>
|
||||
<label class="checkbox"><input type="checkbox" name="trusted_auto_approve" value="1"> Trusted auto-populate after analysis</label>
|
||||
<label class="checkbox"><input type="checkbox" name="agentic_review" value="1"> Request agentic review after analysis</label>
|
||||
<label>Username <input name="access_username" autocomplete="username" placeholder="Optional for private HTTP(S) repos"></label>
|
||||
<label>Password or access token <input name="access_password" type="password" autocomplete="current-password" placeholder="Used for this Git operation only"></label>
|
||||
<div class="actions">
|
||||
@@ -1964,7 +1964,7 @@ def create_analysis_run_from_form(
|
||||
source_path: str = Form(""),
|
||||
use_cached_checkout: str | None = Form(None),
|
||||
use_llm_assistance: str | None = Form(None),
|
||||
trusted_auto_approve: str | None = Form(None),
|
||||
agentic_review: str | None = Form(None),
|
||||
access_username: str = Form(""),
|
||||
access_password: str = Form(""),
|
||||
service: RegistryService = Depends(get_service),
|
||||
@@ -1974,7 +1974,7 @@ def create_analysis_run_from_form(
|
||||
source_path=source_path or None,
|
||||
use_cached_checkout=bool(use_cached_checkout),
|
||||
use_llm_assistance=bool(use_llm_assistance),
|
||||
trusted_auto_approve=bool(trusted_auto_approve),
|
||||
agentic_review=bool(agentic_review),
|
||||
access_username=access_username or None,
|
||||
access_password=access_password or None,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user