Add scoped Agent-mode pipeline as experimental Classic fork.
Wire specialist waves, Brain consolidation, and Classic-compatible reports so Agent mode can run end-to-end via OpenRouter without changing the default Classic path. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
"""Per-cluster conflict critics with hard evidence and image caps."""
|
||||
|
||||
from typing import Dict, List
|
||||
|
||||
from backend import config
|
||||
from backend.agents.base import AgentResult, AgentScope, AgentUsage, failure
|
||||
from backend.llm import call_json
|
||||
from backend.pipeline.conflict_checker import _evidence_block, _valid_conflict
|
||||
from backend.prompts import CONFLICT_SYSTEM_PROMPT, CONFLICT_USER_INSTRUCTION
|
||||
|
||||
|
||||
def _as_finding(conflict: Dict, scope_id: str) -> Dict:
|
||||
return {
|
||||
"issue_id": conflict.get("conflict_id") or "",
|
||||
"source_stage": "conflict",
|
||||
"category": conflict.get("category") or "uncategorized",
|
||||
"severity": conflict.get("severity") or "medium",
|
||||
"confidence": conflict.get("confidence") or "medium",
|
||||
"location": conflict.get("location") or "",
|
||||
"disciplines": conflict.get("disciplines") or [],
|
||||
"sheets": conflict.get("sheets") or [],
|
||||
"description": conflict.get("description") or "",
|
||||
"evidence": conflict.get("evidence") or [],
|
||||
"recommended_resolution": conflict.get("recommended_resolution") or "",
|
||||
"code_reference": None,
|
||||
"agent": "conflict_critic",
|
||||
"scope_id": scope_id,
|
||||
}
|
||||
|
||||
|
||||
class ConflictCriticAgent:
|
||||
name = "conflict_critic"
|
||||
|
||||
def __init__(self, usage: AgentUsage) -> None:
|
||||
self.usage = usage
|
||||
|
||||
def run(self, scope: AgentScope) -> AgentResult:
|
||||
try:
|
||||
cluster = dict(scope.payload["cluster"])
|
||||
cluster["assertions"] = (
|
||||
cluster.get("assertions") or []
|
||||
)[:config.AGENT_CLUSTER_MAX_ASSERTIONS]
|
||||
page_to_b64: Dict[int, str] = scope.payload.get("page_to_b64") or {}
|
||||
images: List[str] = []
|
||||
for page_number in (
|
||||
cluster.get("page_numbers") or []
|
||||
)[:config.AGENT_CONFLICT_MAX_IMAGES]:
|
||||
if page_to_b64.get(page_number):
|
||||
images.append(page_to_b64[page_number])
|
||||
instruction = (
|
||||
CONFLICT_USER_INSTRUCTION
|
||||
.replace("{location}", cluster.get("location") or "")
|
||||
.replace("{evidence}", _evidence_block(cluster))
|
||||
)
|
||||
parsed = call_json(
|
||||
system_prompt=CONFLICT_SYSTEM_PROMPT,
|
||||
user_text=instruction,
|
||||
images_b64=images,
|
||||
max_tokens=config.REASON_MAX_TOKENS,
|
||||
model=config.AGENT_CONFLICT_MODEL,
|
||||
usage_tracker=self.usage,
|
||||
usage_stage="agent.conflict",
|
||||
)
|
||||
candidates = parsed if isinstance(parsed, list) else (
|
||||
parsed.get("conflicts") if isinstance(parsed, dict) else []
|
||||
)
|
||||
findings = []
|
||||
for candidate in candidates or []:
|
||||
conflict = _valid_conflict(candidate, cluster)
|
||||
if conflict:
|
||||
findings.append(_as_finding(conflict, scope.scope_id))
|
||||
return AgentResult(scope_id=scope.scope_id, artifacts=findings)
|
||||
except Exception as exc:
|
||||
return failure(scope, exc)
|
||||
Reference in New Issue
Block a user