capscore-agent / api /app /orchestrator.py
mathurinacheisoft's picture
Deploy CAPScore (clean recreate)
2a7f679 verified
Raw
History Blame Contribute Delete
9.07 kB
from __future__ import annotations
import asyncio
import json
from datetime import datetime, timezone
from typing import Any, Dict, List
from .config import settings
from .models import (
AuditAgentRequest, AuditRepositoryRequest, VerifyClaimsRequest,
JobResult, JobStatus, WorkerResults, A2ACall
)
from .scoring import compute_scorecard
from .proof_pack import persist_proof_pack
from .a2a_client import call_cap_agent
class ExecutionLogger:
def __init__(self):
self._lines: List[str] = []
def log(self, event: str, data: Dict[str, Any] = None) -> None:
entry = {
"ts": datetime.now(timezone.utc).isoformat(),
"event": event,
**(data or {}),
}
self._lines.append(json.dumps(entry))
def lines(self) -> List[str]:
return self._lines.copy()
async def _import_workers():
from .workers.repo_reproducer import reproduce_repository
from .workers.claim_verifier import verify_claims
from .workers.cap_inspector import inspect_cap_integration
from .workers.security_scanner import scan_security
from .workers.readme_rewriter import suggest_readme_fixes
from .workers.demo_coach import generate_demo_script
return reproduce_repository, verify_claims, inspect_cap_integration, scan_security, suggest_readme_fixes, generate_demo_script
async def run_audit_repository(job: JobResult, req: AuditRepositoryRequest) -> JobResult:
logger = ExecutionLogger()
logger.log("job_start", {"job_id": job.job_id, "capability": "audit_repository"})
job.status = JobStatus.RUNNING
job.started_at = datetime.now(timezone.utc)
(reproduce_repository, verify_claims_fn, inspect_cap_integration,
scan_security, suggest_readme_fixes, generate_demo_script) = await _import_workers()
worker_results = WorkerResults()
# Step 1: Repository analysis
logger.log("worker_start", {"worker": "repo_reproducer"})
try:
worker_results.repo = await asyncio.wait_for(
reproduce_repository(req.github_url, req.branch),
timeout=settings.max_job_seconds / 2
)
logger.log("worker_done", {"worker": "repo_reproducer", "score": worker_results.repo.score})
except asyncio.TimeoutError:
logger.log("worker_timeout", {"worker": "repo_reproducer"})
except Exception as e:
logger.log("worker_error", {"worker": "repo_reproducer", "error": str(e)})
# Step 2: Security scan
if req.run_security_scan and worker_results.repo:
logger.log("worker_start", {"worker": "security_scanner"})
try:
worker_results.security = await asyncio.wait_for(
scan_security(req.github_url),
timeout=60
)
logger.log("worker_done", {"worker": "security_scanner", "risk": worker_results.security.risk_level})
except Exception as e:
logger.log("worker_error", {"worker": "security_scanner", "error": str(e)})
# Step 3: CAP inspection
logger.log("worker_start", {"worker": "cap_inspector"})
try:
worker_results.cap = await asyncio.wait_for(
inspect_cap_integration(req.github_url),
timeout=60
)
logger.log("worker_done", {"worker": "cap_inspector", "score": worker_results.cap.score})
except Exception as e:
logger.log("worker_error", {"worker": "cap_inspector", "error": str(e)})
# Step 4: README rewrite suggestions (LLM)
logger.log("worker_start", {"worker": "readme_rewriter"})
try:
worker_results.readme_suggestions = await asyncio.wait_for(
suggest_readme_fixes(req.github_url, worker_results.repo),
timeout=60
)
logger.log("worker_done", {"worker": "readme_rewriter"})
except Exception as e:
logger.log("worker_error", {"worker": "readme_rewriter", "error": str(e)})
# Step 5: Demo coach (LLM)
logger.log("worker_start", {"worker": "demo_coach"})
try:
worker_results.demo_script = await asyncio.wait_for(
generate_demo_script(req.github_url, worker_results),
timeout=60
)
logger.log("worker_done", {"worker": "demo_coach"})
except Exception as e:
logger.log("worker_error", {"worker": "demo_coach", "error": str(e)})
# Step 6: A2A calls (source verifier)
a2a_calls: List[A2ACall] = []
logger.log("a2a_start", {"agent": "source-verifier-mock"})
try:
a2a_call = await call_cap_agent(
"source-verifier-mock",
"verify_sources",
{"github_url": req.github_url},
timeout=30,
)
a2a_calls.append(a2a_call)
logger.log("a2a_done", {"agent": "source-verifier-mock", "status": a2a_call.status})
except Exception as e:
logger.log("a2a_error", {"agent": "source-verifier-mock", "error": str(e)})
job.a2a_calls = a2a_calls
job.worker_results = worker_results
# Compute scorecard
input_data = req.model_dump()
job.scorecard = compute_scorecard(worker_results, input_data, len(a2a_calls))
logger.log("scorecard_computed", {"overall_score": job.scorecard.overall_score})
# Build proof pack
job.completed_at = datetime.now(timezone.utc)
job.status = JobStatus.DONE
persist_proof_pack(job, logger.lines())
logger.log("proof_pack_built", {"result_hash": job.result_hash})
return job
async def run_audit_agent(job: JobResult, req: AuditAgentRequest) -> JobResult:
logger = ExecutionLogger()
logger.log("job_start", {"job_id": job.job_id, "capability": "audit_agent_listing"})
job.status = JobStatus.RUNNING
job.started_at = datetime.now(timezone.utc)
(_, verify_claims_fn, inspect_cap_integration,
_, suggest_readme_fixes, generate_demo_script) = await _import_workers()
worker_results = WorkerResults()
if req.github_url:
logger.log("worker_start", {"worker": "cap_inspector"})
try:
worker_results.cap = await asyncio.wait_for(
inspect_cap_integration(req.github_url),
timeout=60
)
logger.log("worker_done", {"worker": "cap_inspector", "score": worker_results.cap.score})
except Exception as e:
logger.log("worker_error", {"worker": "cap_inspector", "error": str(e)})
# Verify any claims from listing
if req.claimed_tracks:
logger.log("worker_start", {"worker": "claim_verifier"})
try:
evidence_urls = [req.agent_listing_url]
if req.github_url:
evidence_urls.append(req.github_url)
worker_results.claims = await asyncio.wait_for(
verify_claims_fn(req.claimed_tracks, evidence_urls, "standard"),
timeout=90
)
logger.log("worker_done", {"worker": "claim_verifier"})
except Exception as e:
logger.log("worker_error", {"worker": "claim_verifier", "error": str(e)})
# A2A call
a2a_calls: List[A2ACall] = []
try:
a2a_call = await call_cap_agent(
"source-verifier-mock", "verify_sources",
{"listing_url": req.agent_listing_url}, timeout=30
)
a2a_calls.append(a2a_call)
except Exception:
pass
job.a2a_calls = a2a_calls
job.worker_results = worker_results
job.scorecard = compute_scorecard(worker_results, req.model_dump(), len(a2a_calls))
job.completed_at = datetime.now(timezone.utc)
job.status = JobStatus.DONE
persist_proof_pack(job, logger.lines())
return job
async def run_verify_claims(job: JobResult, req: VerifyClaimsRequest) -> JobResult:
logger = ExecutionLogger()
logger.log("job_start", {"job_id": job.job_id, "capability": "verify_claims"})
job.status = JobStatus.RUNNING
job.started_at = datetime.now(timezone.utc)
(_, verify_claims_fn, _, _, _, _) = await _import_workers()
worker_results = WorkerResults()
logger.log("worker_start", {"worker": "claim_verifier"})
try:
worker_results.claims = await asyncio.wait_for(
verify_claims_fn(req.claims, req.evidence_urls or [], req.strictness),
timeout=90
)
logger.log("worker_done", {"worker": "claim_verifier", "claims_count": len(req.claims)})
except Exception as e:
logger.log("worker_error", {"worker": "claim_verifier", "error": str(e)})
a2a_calls: List[A2ACall] = []
if req.evidence_urls:
try:
a2a_call = await call_cap_agent(
"source-verifier-mock", "verify_sources",
{"evidence_urls": req.evidence_urls}, timeout=30
)
a2a_calls.append(a2a_call)
except Exception:
pass
job.a2a_calls = a2a_calls
job.worker_results = worker_results
job.scorecard = compute_scorecard(worker_results, req.model_dump(), len(a2a_calls))
job.completed_at = datetime.now(timezone.utc)
job.status = JobStatus.DONE
persist_proof_pack(job, logger.lines())
return job