176 lines
7.1 KiB
Python
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)
|