feat: async task pipeline for media and llm workflows

这个提交包含在:
cryptocommuniums-afk
2026-03-15 00:12:26 +08:00
父节点 1cc863e60e
当前提交 20e183d2da
修改 36 个文件,包含 1961 行新增339 行删除

查看文件

@@ -59,6 +59,7 @@ type MockAppState = {
user: MockUser;
videos: any[];
analyses: any[];
tasks: any[];
activePlan: {
id: number;
title: string;
@@ -79,6 +80,7 @@ type MockAppState = {
} | null;
mediaSession: MockMediaSession | null;
nextVideoId: number;
nextTaskId: number;
authMeNullResponsesAfterLogin: number;
};
@@ -159,6 +161,32 @@ function buildMediaSession(user: MockUser, title: string): MockMediaSession {
};
}
function createTask(state: MockAppState, input: {
type: string;
title: string;
status?: string;
progress?: number;
message?: string;
result?: any;
error?: string | null;
}) {
const task = {
id: `task-${state.nextTaskId++}`,
userId: state.user.id,
type: input.type,
status: input.status ?? "succeeded",
title: input.title,
message: input.message ?? "任务执行完成",
progress: input.progress ?? 100,
result: input.result ?? null,
error: input.error ?? null,
createdAt: nowIso(),
updatedAt: nowIso(),
};
state.tasks = [task, ...state.tasks];
return task;
}
async function fulfillJson(route: Route, body: unknown) {
await route.fulfill({
status: 200,
@@ -218,11 +246,112 @@ async function handleTrpc(route: Route, state: MockAppState) {
},
],
};
return trpcResult({ planId: state.activePlan.id, plan: state.activePlan });
return trpcResult({
taskId: createTask(state, {
type: "training_plan_generate",
title: "7天训练计划生成",
result: {
kind: "training_plan_generate",
planId: state.activePlan.id,
plan: state.activePlan,
},
}).id,
});
case "plan.adjust":
return trpcResult({
taskId: createTask(state, {
type: "training_plan_adjust",
title: "训练计划调整",
result: {
kind: "training_plan_adjust",
adjustmentNotes: "已根据最近分析结果调整训练重点。",
},
}).id,
});
case "video.list":
return trpcResult(state.videos);
case "analysis.list":
return trpcResult(state.analyses);
case "task.list":
return trpcResult(state.tasks);
case "task.get": {
const rawInput = url.searchParams.get("input");
const parsedInput = rawInput ? JSON.parse(rawInput) : {};
const taskId = parsedInput.json?.taskId || parsedInput[0]?.json?.taskId;
return trpcResult(state.tasks.find((task) => task.id === taskId) || null);
}
case "task.retry": {
const rawInput = url.searchParams.get("input");
const parsedInput = rawInput ? JSON.parse(rawInput) : {};
const taskId = parsedInput.json?.taskId || parsedInput[0]?.json?.taskId;
const task = state.tasks.find((item) => item.id === taskId);
if (task) {
task.status = "succeeded";
task.progress = 100;
task.error = null;
task.message = "任务执行完成";
}
return trpcResult({ task });
}
case "task.createMediaFinalize": {
if (state.mediaSession) {
state.mediaSession.status = "archived";
state.mediaSession.archiveStatus = "completed";
state.mediaSession.playback = {
ready: true,
webmUrl: "/media/assets/sessions/session-e2e/recording.webm",
mp4Url: "/media/assets/sessions/session-e2e/recording.mp4",
webmSize: 2_400_000,
mp4Size: 1_800_000,
previewUrl: "/media/assets/sessions/session-e2e/recording.webm",
};
state.videos = [
{
id: state.nextVideoId++,
title: state.mediaSession.title,
url: state.mediaSession.playback.webmUrl,
format: "webm",
fileSize: state.mediaSession.playback.webmSize,
exerciseType: "recording",
analysisStatus: "completed",
createdAt: nowIso(),
},
...state.videos,
];
}
return trpcResult({
taskId: createTask(state, {
type: "media_finalize",
title: "录制归档",
result: {
kind: "media_finalize",
sessionId: state.mediaSession?.id,
videoId: state.videos[0]?.id,
url: state.videos[0]?.url,
},
}).id,
});
}
case "analysis.getCorrections":
return trpcResult({
taskId: createTask(state, {
type: "pose_correction_multimodal",
title: "动作纠正",
result: {
corrections: "## 动作概览\n整体节奏稳定,建议继续优化击球点前置。",
report: {
priorityFixes: [
{
title: "击球点前置",
why: "击球点略靠后会影响挥拍连贯性。",
howToPractice: "每组 8 次影子挥拍,刻意在身体前侧完成触球动作。",
successMetric: "连续 3 组都能稳定在身体前侧完成挥拍。",
},
],
},
},
}).id,
});
case "video.registerExternal":
if (state.mediaSession?.playback.webmUrl || state.mediaSession?.playback.mp4Url) {
state.videos = [
@@ -366,9 +495,11 @@ export async function installAppMocks(
createdAt: nowIso(),
},
],
tasks: [],
activePlan: null,
mediaSession: null,
nextVideoId: 100,
nextTaskId: 1,
authMeNullResponsesAfterLogin: options?.authMeNullResponsesAfterLogin ?? 0,
};