Files
ai-contract-platform/backend/app/api/clauses.py
T

176 lines
7.1 KiB
Python

from typing import Any, Optional
from fastapi import APIRouter, Depends, Header, Request
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from app.core.response import success_response
from app.db import get_db_session
from app.services.audit_log_service import AuditLogService
from app.services.auth_service import AuthService
from app.services.clause_recommendation_service import ClauseRecommendationService
router = APIRouter(prefix="/clauses", tags=["clauses"])
ROUTE_PERMISSION_SCOPE = "clause_recommendation"
ROUTE_PERMISSION_ACTIONS = {
"recommend": "clauses:recommend",
"rewrite": "clauses:rewrite",
"recommendation_action": "clauses:recommendations:action",
}
class ClauseRecommendRequest(BaseModel):
contract_text: str
document_type_code: Optional[str] = None
contract_type: Optional[str] = None
contract_id: Optional[str] = None
max_items: int = Field(default=8, ge=1, le=20)
class ClauseRewriteRequest(BaseModel):
clause_text: str
rewrite_type: str = Field(default="professional")
document_type_code: Optional[str] = None
contract_type: Optional[str] = None
contract_id: Optional[str] = None
clause_id: Optional[str] = None
context_text: Optional[str] = None
class ClauseRecommendationActionRequest(BaseModel):
action_status: str = Field(description="shown/applied/ignored/modified")
action_note: Optional[str] = None
applied_contract_version_id: Optional[str] = None
action_detail: Optional[dict[str, Any]] = None
def get_clause_recommendation_service(db: Session = Depends(get_db_session)) -> ClauseRecommendationService:
return ClauseRecommendationService(db)
def get_audit_log_service(db: Session = Depends(get_db_session)) -> AuditLogService:
return AuditLogService(db)
def require_current_user(
authorization: Optional[str] = Header(default=None),
db: Session = Depends(get_db_session),
):
return AuthService(db).require_current_user(authorization)
@router.post("/recommend")
def recommend_missing_clauses(
payload: ClauseRecommendRequest,
request: Request,
clause_recommendation_service: ClauseRecommendationService = Depends(get_clause_recommendation_service),
audit_log_service: AuditLogService = Depends(get_audit_log_service),
current_user: dict = Depends(require_current_user),
):
result = clause_recommendation_service.recommend_missing_clauses(
current_user,
contract_text=payload.contract_text,
document_type_code=payload.document_type_code,
contract_type=payload.contract_type,
contract_id=payload.contract_id,
max_items=payload.max_items,
)
audit_log_service.record_event(
tenant_id=current_user.get("tenant_id"),
user_id=current_user.get("id"),
action="recommend_missing_clauses",
resource_type="clause_recommendation",
resource_id=payload.contract_id,
detail_json={
"document_type_code": result.get("document_type_code"),
"contract_type": payload.contract_type,
"recommended_count": result.get("total"),
"recommendation_keys": (result.get("summary") or {}).get("recommendation_keys") or [],
"missing_clause_keys": (result.get("summary") or {}).get("missing_clause_keys") or [],
"industry_practice_keys": (result.get("summary") or {}).get("industry_practice_keys") or [],
"industry_practice_count": (result.get("summary") or {}).get("industry_practice_count") or 0,
"source_count": (result.get("summary") or {}).get("source_count") or 0,
"text_length": len(payload.contract_text or ""),
},
ip_address=request.client.host if request.client else None,
user_agent=request.headers.get("user-agent"),
)
return success_response(result)
@router.post("/rewrite")
def rewrite_clause_wording(
payload: ClauseRewriteRequest,
request: Request,
clause_recommendation_service: ClauseRecommendationService = Depends(get_clause_recommendation_service),
audit_log_service: AuditLogService = Depends(get_audit_log_service),
current_user: dict = Depends(require_current_user),
):
result = clause_recommendation_service.rewrite_clause_wording(
current_user,
clause_text=payload.clause_text,
rewrite_type=payload.rewrite_type,
document_type_code=payload.document_type_code,
contract_type=payload.contract_type,
contract_id=payload.contract_id,
clause_id=payload.clause_id,
context_text=payload.context_text,
)
audit_log_service.record_event(
tenant_id=current_user.get("tenant_id"),
user_id=current_user.get("id"),
action="rewrite_clause_wording",
resource_type="clause_rewrite",
resource_id=payload.clause_id or payload.contract_id,
detail_json={
"contract_id": payload.contract_id,
"clause_id": payload.clause_id,
"document_type_code": result.get("document_type_code"),
"rewrite_type": result.get("rewrite_type"),
"change_count": (result.get("summary") or {}).get("change_count") or 0,
"risk_note_count": (result.get("summary") or {}).get("risk_note_count") or 0,
"source_count": (result.get("summary") or {}).get("source_count") or 0,
"text_length": len(payload.clause_text or ""),
},
ip_address=request.client.host if request.client else None,
user_agent=request.headers.get("user-agent"),
)
return success_response(result)
@router.post("/recommendations/{recommendation_id}/action")
def record_clause_recommendation_action(
recommendation_id: str,
payload: ClauseRecommendationActionRequest,
request: Request,
clause_recommendation_service: ClauseRecommendationService = Depends(get_clause_recommendation_service),
audit_log_service: AuditLogService = Depends(get_audit_log_service),
current_user: dict = Depends(require_current_user),
):
result = clause_recommendation_service.update_recommendation_action(
current_user,
recommendation_id,
action_status=payload.action_status,
action_note=payload.action_note,
applied_contract_version_id=payload.applied_contract_version_id,
action_detail=payload.action_detail,
)
audit_log_service.record_event(
tenant_id=current_user.get("tenant_id"),
user_id=current_user.get("id"),
action="record_clause_recommendation_action",
resource_type="clause_recommendation",
resource_id=recommendation_id,
detail_json={
"action_status": result.get("action_status"),
"contract_id": result.get("contract_id"),
"document_type_code": result.get("document_type_code"),
"clause_key": result.get("clause_key"),
"recommendation_kind": result.get("recommendation_kind"),
"applied_contract_version_id": result.get("applied_contract_version_id"),
},
ip_address=request.client.host if request.client else None,
user_agent=request.headers.get("user-agent"),
)
return success_response(result)