feat: V1 - DAG编排/告警系统/Agent循环/知识库RAG/Webhook + 审核管理富上下文修复

This commit is contained in:
2026-08-13 00:46:54 +08:00
parent dbd2d1ba81
commit 4cc8e1c464
+103 -3
View File
@@ -1,13 +1,20 @@
"""Review service: HITL approval/rejection flow."""
"""Review service: HITL approval/rejection flow with full context."""
from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import List, Optional
from datetime import datetime
from app.models.review import Review
from app.models.task import Task
from app.schemas.review import ReviewCreate, ReviewDecision
from app.models.project import Project
from app.models.worker import AIWorker
from app.models.cost import CostLog
from app.schemas.review import ReviewCreate, ReviewDecision, ReviewDetailResponse
class ReviewService:
# ---- Basic CRUD ----
@staticmethod
def list_reviews(
db: Session,
@@ -51,6 +58,100 @@ class ReviewService:
db.refresh(review)
return review
# ---- Detail with full context ----
@staticmethod
def list_reviews_detail(
db: Session,
tenant_id: int,
status: Optional[str] = None,
project_id: Optional[int] = None,
skip: int = 0,
limit: int = 100,
) -> List[ReviewDetailResponse]:
"""List reviews with joined task/project/worker/cost context."""
reviews = ReviewService.list_reviews(
db, tenant_id, status, project_id, skip, limit
)
return [ReviewService._enrich(db, r) for r in reviews]
@staticmethod
def get_review_detail(
db: Session, tenant_id: int, review_id: int
) -> Optional[ReviewDetailResponse]:
"""Get a single review with full context."""
review = ReviewService.get_review(db, tenant_id, review_id)
if not review:
return None
return ReviewService._enrich(db, review)
@staticmethod
def _enrich(db: Session, review: Review) -> ReviewDetailResponse:
"""Join review with task, project, worker, and aggregate cost."""
task = (
db.query(Task)
.filter(Task.id == review.task_id, Task.tenant_id == review.tenant_id)
.first()
)
project = (
db.query(Project)
.filter(Project.id == review.project_id, Project.tenant_id == review.tenant_id)
.first()
)
worker = None
if task and task.worker_id:
worker = (
db.query(AIWorker)
.filter(AIWorker.id == task.worker_id, AIWorker.tenant_id == review.tenant_id)
.first()
)
# Aggregate cost data for this task
cost_row = (
db.query(
func.sum(CostLog.cost_cents).label("total_cost"),
func.sum(CostLog.total_tokens).label("total_tokens"),
func.sum(CostLog.duration_ms).label("total_duration"),
)
.filter(CostLog.task_id == review.task_id)
.first()
)
return ReviewDetailResponse(
id=review.id,
task_id=review.task_id,
project_id=review.project_id,
review_type=review.review_type,
status=review.status,
reviewer_id=review.reviewer_id,
reviewer_role=review.reviewer_role,
review_content=review.review_content,
reviewer_comment=review.reviewer_comment,
auto_check_result=review.auto_check_result,
submitted_at=review.submitted_at,
reviewed_at=review.reviewed_at,
created_at=review.created_at,
task_title=task.title if task else "",
task_description=task.description if task else "",
task_input_data=task.input_data if task else "",
task_output_data=task.output_data if task else "",
task_priority=task.priority if task else "medium",
task_type=task.task_type if task else "single_call",
task_status=task.status if task else "",
project_name=project.name if project else "",
project_description=project.description if project else "",
worker_id=worker.id if worker else None,
worker_name=worker.name if worker else None,
worker_model_name=worker.model_name if worker else None,
worker_provider=worker.provider if worker else None,
worker_system_prompt=worker.system_prompt if worker else None,
cost_cents=cost_row.total_cost or 0 if cost_row else 0,
token_usage=cost_row.total_tokens or 0 if cost_row else 0,
duration_ms=cost_row.total_duration or 0 if cost_row else 0,
)
# ---- Decision ----
@staticmethod
def make_decision(
db: Session,
@@ -72,7 +173,6 @@ class ReviewService:
review.reviewer_comment = req.comment
review.reviewed_at = datetime.utcnow()
# Update task status
task = (
db.query(Task)
.filter(Task.id == review.task_id, Task.tenant_id == tenant_id)