feat(backend): 更新 API 端点实现

- 更新 Chunks API 端点
- 更新 Datasets API 端点
- 更新 Evaluation API 端点
- 更新 Files API 端点
- 更新 Projects API 端点
- 更新 Questions API 端点

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Developer
2026-03-17 17:29:58 +08:00
parent eac10a9d95
commit db11429290
6 changed files with 519 additions and 317 deletions

View File

@@ -3,37 +3,38 @@ Questions API Router
"""
from typing import List, Optional
from uuid import UUID
from pydantic import BaseModel
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.api.response import ApiResponse, PaginatedResponse
from app.core.database import get_db
from app.core.exceptions import NotFoundException, ValidationException
from app.core.crud import CRUDBase
from app.models.models import Question, Chunk
from app.schemas.base import QuestionCreate, QuestionResponse
from app.schemas.question import QuestionResponse
from app.schemas.question import QuestionCreateSchema
router = APIRouter()
# Initialize CRUD
question_crud = CRUDBase(Question)
class GenerateRequest(BaseModel):
"""Request model for generating questions"""
chunk_ids: List[UUID] = []
count: int = 5
chunk_ids: List[UUID] = Field(..., min_length=1)
count: int = Field(5, ge=1, le=50)
question_types: List[str] = ["fact", "summary"]
@router.post("/generate", response_model=dict)
@router.post("/generate", response_model=ApiResponse)
async def generate_questions(
project_id: UUID,
request: GenerateRequest,
db: AsyncSession = Depends(get_db)
):
"""Generate questions from chunks using LLM"""
# TODO: Implement LLM-based question generation
# This is a placeholder that creates sample questions
if not request.chunk_ids:
raise HTTPException(status_code=400, detail="chunk_ids is required")
# Get chunks
result = await db.execute(
select(Chunk).where(Chunk.id.in_(request.chunk_ids), Chunk.project_id == project_id)
@@ -41,9 +42,9 @@ async def generate_questions(
chunks = result.scalars().all()
if not chunks:
raise HTTPException(status_code=404, detail="No chunks found")
raise ValidationException("No valid chunks found", field="chunk_ids")
# Create sample questions (placeholder)
# Create sample questions (placeholder for LLM-based generation)
created_questions = []
for chunk in chunks:
for i in range(request.count):
@@ -60,63 +61,73 @@ async def generate_questions(
await db.commit()
return {
"questions": len(created_questions),
"message": f"Successfully generated {len(created_questions)} questions"
}
return ApiResponse.ok(
data={"questions": len(created_questions)},
message=f"Successfully generated {len(created_questions)} questions"
)
@router.get("/", response_model=dict)
@router.get("", response_model=ApiResponse)
async def list_questions(
project_id: UUID,
chunk_id: Optional[UUID] = Query(None),
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
db: AsyncSession = Depends(get_db)
):
"""List questions for a project"""
query = select(Question).where(Question.project_id == project_id)
filters = {"project_id": project_id}
if chunk_id:
query = query.where(Question.chunk_id == chunk_id)
filters["chunk_id"] = chunk_id
result = await db.execute(query)
questions = result.scalars().all()
skip = (page - 1) * page_size
questions, total = await question_crud.get_multi(
db,
skip=skip,
limit=page_size,
filters=filters,
order_by="created_at",
descending=True
)
return {"questions": [QuestionResponse.model_validate(q) for q in questions]}
question_responses = [QuestionResponse.model_validate(q) for q in questions]
return PaginatedResponse.ok(
items=question_responses,
page=page,
page_size=page_size,
total=total
)
@router.put("/{question_id}", response_model=dict)
@router.put("/{question_id}", response_model=ApiResponse)
async def update_question(
project_id: UUID,
question_id: UUID,
question: QuestionCreate,
question: QuestionCreateSchema,
db: AsyncSession = Depends(get_db)
):
"""Update question"""
result = await db.execute(
select(Question).where(Question.id == question_id, Question.project_id == project_id)
db_question = await question_crud.get(db, question_id)
if not db_question or db_question.project_id != project_id:
raise NotFoundException("Question", question_id)
updated_question = await question_crud.update(db, db_question, question)
return ApiResponse.ok(
data=QuestionResponse.model_validate(updated_question),
message="Question updated successfully"
)
db_question = result.scalar_one_or_none()
if not db_question:
raise HTTPException(status_code=404, detail="Question not found")
for key, value in question.model_dump(exclude_unset=True).items():
setattr(db_question, key, value)
await db.commit()
await db.refresh(db_question)
return QuestionResponse.model_validate(db_question)
@router.delete("/{question_id}", response_model=dict)
async def delete_question(project_id: UUID, question_id: UUID, db: AsyncSession = Depends(get_db)):
@router.delete("/{question_id}", response_model=ApiResponse)
async def delete_question(
project_id: UUID,
question_id: UUID,
db: AsyncSession = Depends(get_db)
):
"""Delete question"""
result = await db.execute(
select(Question).where(Question.id == question_id, Question.project_id == project_id)
)
question = result.scalar_one_or_none()
if not question:
raise HTTPException(status_code=404, detail="Question not found")
question = await question_crud.get(db, question_id)
if not question or question.project_id != project_id:
raise NotFoundException("Question", question_id)
await db.delete(question)
await db.commit()
return {"message": "Question deleted successfully"}
await question_crud.delete(db, question_id)
return ApiResponse.ok(message="Question deleted successfully")