root 656f596d7e feat: implement AI legal assistant system MVP
Core modules:
- Laws: CRUD, search, AI-powered QA
- Analysis: legal research and case management
- Contracts: lifecycle management with templates
- Signatures: electronic signature workflow

Infrastructure:
- FastAPI + SQLite + async SQLAlchemy
- Docker deployment support
- 54 unit tests passing

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 03:34:44 +08:00

185 lines
4.7 KiB
Python

"""Law API endpoints."""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.models.law import LawType, LawStatus
from app.schemas.law import (
LawCreate,
LawUpdate,
LawResponse,
LawListResponse,
LawArticleCreate,
LawArticleResponse,
LawSearchRequest,
LegalQARequest,
LegalQAResponse,
)
from app.services.law_service import LawService
from app.services.llm_service import llm_service
router = APIRouter(prefix="/laws", tags=["laws"])
@router.post("", response_model=LawResponse)
async def create_law(
law_data: LawCreate,
db: AsyncSession = Depends(get_db),
):
"""Create a new law."""
service = LawService(db)
law = await service.create_law(
title=law_data.title,
law_type=LawType(law_data.law_type.value),
promulgation_date=law_data.promulgation_date,
effective_date=law_data.effective_date,
issuing_authority=law_data.issuing_authority,
content=law_data.content,
status=LawStatus(law_data.status.value),
document_number=law_data.document_number,
)
return law
@router.get("", response_model=LawListResponse)
async def list_laws(
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
law_type: Optional[str] = Query(None),
status: Optional[str] = Query(None),
db: AsyncSession = Depends(get_db),
):
"""List laws with optional filters."""
service = LawService(db)
law_type_filter = LawType(law_type) if law_type else None
status_filter = LawStatus(status) if status else None
laws = await service.get_laws_list(
skip=skip,
limit=limit,
law_type=law_type_filter,
status=status_filter,
)
return LawListResponse(
items=laws,
total=len(laws),
skip=skip,
limit=limit,
)
@router.get("/{law_id}", response_model=LawResponse)
async def get_law(
law_id: int,
db: AsyncSession = Depends(get_db),
):
"""Get a law by ID."""
service = LawService(db)
law = await service.get_law_by_id(law_id)
if not law:
raise HTTPException(status_code=404, detail="Law not found")
return law
@router.put("/{law_id}", response_model=LawResponse)
async def update_law(
law_id: int,
law_data: LawUpdate,
db: AsyncSession = Depends(get_db),
):
"""Update a law."""
service = LawService(db)
update_dict = law_data.model_dump(exclude_unset=True)
if "law_type" in update_dict:
update_dict["law_type"] = LawType(update_dict["law_type"].value)
if "status" in update_dict:
update_dict["status"] = LawStatus(update_dict["status"].value)
law = await service.update_law(law_id, **update_dict)
if not law:
raise HTTPException(status_code=404, detail="Law not found")
return law
@router.delete("/{law_id}")
async def delete_law(
law_id: int,
db: AsyncSession = Depends(get_db),
):
"""Delete a law."""
service = LawService(db)
success = await service.delete_law(law_id)
if not success:
raise HTTPException(status_code=404, detail="Law not found")
return {"message": "Law deleted successfully"}
@router.post("/search", response_model=LawListResponse)
async def search_laws(
search_data: LawSearchRequest,
db: AsyncSession = Depends(get_db),
):
"""Search laws by keyword."""
service = LawService(db)
laws = await service.search_laws_by_keyword(
keyword=search_data.keyword,
limit=search_data.limit,
)
return LawListResponse(
items=laws,
total=len(laws),
skip=0,
limit=search_data.limit,
)
@router.post("/qa", response_model=LegalQAResponse)
async def legal_qa(
qa_data: LegalQARequest,
):
"""Ask a legal question and get AI-powered answer."""
answer = await llm_service.legal_qa(
question=qa_data.question,
context=qa_data.context,
)
return LegalQAResponse(
question=qa_data.question,
answer=answer,
references=[],
)
@router.post("/{law_id}/articles", response_model=LawArticleResponse)
async def create_article(
law_id: int,
article_data: LawArticleCreate,
db: AsyncSession = Depends(get_db),
):
"""Create a law article."""
service = LawService(db)
# Verify law exists
law = await service.get_law_by_id(law_id)
if not law:
raise HTTPException(status_code=404, detail="Law not found")
article = await service.create_article(
law_id=law_id,
article_number=article_data.article_number,
content=article_data.content,
)
return article