""" Label Studio Editor(前端嵌入式)接口 说明: - 不依赖 Label Studio Server;仅复用其“编辑器”前端库 - DataMate 负责提供 tasks/annotations 数据与保存能力 - 当前支持 dataset_type=TEXT/IMAGE 的项目 """ from __future__ import annotations from typing import Optional from fastapi import APIRouter, Depends, Query, Path from sqlalchemy.ext.asyncio import AsyncSession from app.core.logging import get_logger from app.db.session import get_db from app.module.annotation.schema.editor import ( EditorProjectInfo, EditorTaskListResponse, EditorTaskResponse, UpsertAnnotationRequest, UpsertAnnotationResponse, ) from app.module.annotation.service.editor import AnnotationEditorService from app.module.shared.schema import StandardResponse logger = get_logger(__name__) router = APIRouter( prefix="/editor", tags=["annotation/editor"], ) @router.get( "/projects/{project_id}", response_model=StandardResponse[EditorProjectInfo], ) async def get_editor_project_info( project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"), db: AsyncSession = Depends(get_db), ): service = AnnotationEditorService(db) info = await service.get_project_info(project_id) return StandardResponse(code=200, message="success", data=info) @router.get( "/projects/{project_id}/tasks", response_model=StandardResponse[EditorTaskListResponse], ) async def list_editor_tasks( project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"), page: int = Query(0, ge=0, description="页码(从0开始)"), size: int = Query(50, ge=1, le=200, description="每页大小"), db: AsyncSession = Depends(get_db), ): service = AnnotationEditorService(db) result = await service.list_tasks(project_id, page=page, size=size) return StandardResponse(code=200, message="success", data=result) @router.get( "/projects/{project_id}/tasks/{file_id}", response_model=StandardResponse[EditorTaskResponse], ) async def get_editor_task( project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"), file_id: str = Path(..., description="文件ID(t_dm_dataset_files.id)"), segment_index: Optional[int] = Query(None, alias="segmentIndex", description="段落索引(分段模式下使用)"), db: AsyncSession = Depends(get_db), ): service = AnnotationEditorService(db) task = await service.get_task(project_id, file_id, segment_index=segment_index) return StandardResponse(code=200, message="success", data=task) @router.put( "/projects/{project_id}/tasks/{file_id}/annotation", response_model=StandardResponse[UpsertAnnotationResponse], ) async def upsert_editor_annotation( request: UpsertAnnotationRequest, project_id: str = Path(..., description="标注项目ID(t_dm_labeling_projects.id)"), file_id: str = Path(..., description="文件ID(t_dm_dataset_files.id)"), db: AsyncSession = Depends(get_db), ): service = AnnotationEditorService(db) result = await service.upsert_annotation(project_id, file_id, request) return StandardResponse(code=200, message="success", data=result)