feat(annotation): 优化文本标注分段功能实现

- 新增 getEditorTaskSegmentsUsingGet 接口用于获取任务分段信息
- 移除 SegmentInfo 中的 text、start、end 字段,精简数据结构
- 添加 EditorTaskSegmentsResponse 类型定义用于分段摘要响应
- 实现服务端 get_task_segments 方法,支持分段信息查询
- 重构前端组件缓存机制,使用 segmentSummaryFileRef 管理分段状态
- 优化分段构建逻辑,提取 _build_segment_contexts 公共方法
- 调整后端 _build_text_task 方法中的分段处理流程
- 更新 API 类型定义,统一 RequestParams 和 RequestPayload 类型
This commit is contained in:
2026-02-04 16:59:04 +08:00
parent 394e2bda18
commit cda22a720c
5 changed files with 250 additions and 108 deletions

View File

@@ -6,6 +6,7 @@ import { useNavigate, useParams } from "react-router";
import {
getEditorProjectInfoUsingGet,
getEditorTaskUsingGet,
getEditorTaskSegmentsUsingGet,
listEditorTasksUsingGet,
upsertEditorAnnotationUsingPut,
} from "../annotation.api";
@@ -38,9 +39,6 @@ type LsfMessage = {
type SegmentInfo = {
idx: number;
text: string;
start: number;
end: number;
hasAnnotation: boolean;
lineIndex: number;
chunkIndex: number;
@@ -66,10 +64,16 @@ type EditorTaskPayload = {
type EditorTaskResponse = {
task?: EditorTaskPayload;
segmented?: boolean;
segments?: SegmentInfo[];
totalSegments?: number;
currentSegmentIndex?: number;
};
type EditorTaskSegmentsResponse = {
segmented?: boolean;
segments?: SegmentInfo[];
totalSegments?: number;
};
type EditorTaskListResponse = {
content?: EditorTaskListItem[];
totalElements?: number;
@@ -288,6 +292,7 @@ export default function LabelStudioTextEditor() {
const segmentStatsCacheRef = useRef<Record<string, SegmentStats>>({});
const segmentStatsSeqRef = useRef(0);
const segmentStatsLoadingRef = useRef<Set<string>>(new Set());
const segmentSummaryFileRef = useRef<string>("");
const [loadingProject, setLoadingProject] = useState(true);
const [loadingTasks, setLoadingTasks] = useState(false);
@@ -358,9 +363,7 @@ export default function LabelStudioTextEditor() {
if (segmentStatsCacheRef.current[fileId] || segmentStatsLoadingRef.current.has(fileId)) return;
segmentStatsLoadingRef.current.add(fileId);
try {
const resp = (await getEditorTaskUsingGet(projectId, fileId, {
segmentIndex: 0,
})) as ApiResponse<EditorTaskResponse>;
const resp = (await getEditorTaskSegmentsUsingGet(projectId, fileId)) as ApiResponse<EditorTaskSegmentsResponse>;
if (segmentStatsSeqRef.current !== seq) return;
const data = resp?.data;
if (!data?.segmented) return;
@@ -591,20 +594,38 @@ export default function LabelStudioTextEditor() {
if (seq !== initSeqRef.current) return;
// 更新分段状态
const segmentIndex = data?.segmented
const isSegmented = !!data?.segmented;
const segmentIndex = isSegmented
? resolveSegmentIndex(data.currentSegmentIndex) ?? 0
: undefined;
if (data?.segmented) {
const stats = buildSegmentStats(data.segments);
if (isSegmented) {
let nextSegments: SegmentInfo[] = [];
if (segmentSummaryFileRef.current === fileId && segments.length > 0) {
nextSegments = segments;
} else {
try {
const segmentResp = (await getEditorTaskSegmentsUsingGet(projectId, fileId)) as ApiResponse<EditorTaskSegmentsResponse>;
if (seq !== initSeqRef.current) return;
const segmentData = segmentResp?.data;
if (segmentData?.segmented) {
nextSegments = Array.isArray(segmentData.segments) ? segmentData.segments : [];
}
} catch (e) {
console.error(e);
}
}
const stats = buildSegmentStats(nextSegments);
setSegmented(true);
setSegments(data.segments || []);
setSegments(nextSegments);
setCurrentSegmentIndex(segmentIndex ?? 0);
updateSegmentStatsCache(fileId, stats);
segmentSummaryFileRef.current = fileId;
} else {
setSegmented(false);
setSegments([]);
setCurrentSegmentIndex(0);
updateSegmentStatsCache(fileId, null);
segmentSummaryFileRef.current = fileId;
}
const taskData = {
@@ -664,7 +685,7 @@ export default function LabelStudioTextEditor() {
} finally {
if (seq === initSeqRef.current) setLoadingTaskDetail(false);
}
}, [iframeReady, message, postToIframe, project, projectId, updateSegmentStatsCache]);
}, [iframeReady, message, postToIframe, project, projectId, segments, updateSegmentStatsCache]);
const advanceAfterSave = useCallback(async (fileId: string, segmentIndex?: number) => {
if (!fileId) return;
@@ -979,6 +1000,7 @@ export default function LabelStudioTextEditor() {
setSegmented(false);
setSegments([]);
setCurrentSegmentIndex(0);
segmentSummaryFileRef.current = "";
savedSnapshotsRef.current = {};
segmentStatsSeqRef.current += 1;
segmentStatsCacheRef.current = {};