refactor(api): 收口 server/client 数据解析层,消除 aiGenerationClient 的 as T 断言

This commit is contained in:
2026-06-15 15:06:41 +08:00
parent 8985deea0a
commit 6dd292207f
4 changed files with 396 additions and 23 deletions
+26 -20
View File
@@ -1,12 +1,18 @@
import {
buildApiUrl,
buildAuthHeaders,
isRecord,
readJsonResponse,
serverRequest,
throwResponseError,
} from "./serverConnection";
import { isOptionalApiRouteMissing } from "./apiErrorUtils";
import {
parseAiTaskStatus,
parseAiTaskStatusList,
parseImageTaskCreateResponse,
parseSseTaskFrame,
parseTaskCreateResponse,
} from "./dtoParsers";
import type { WebGenerationPreviewTask } from "../types";
export interface ImageGenInput {
@@ -190,13 +196,6 @@ function parseContentDispositionFilename(value: string | null): string | undefin
return plainMatch?.[1]?.trim() || undefined;
}
function extractTaskList(payload: unknown): AiTaskStatus[] {
if (Array.isArray(payload)) return payload as AiTaskStatus[];
if (!isRecord(payload)) return [];
const rows = payload.tasks ?? payload.items;
return Array.isArray(rows) ? (rows as AiTaskStatus[]) : [];
}
function getStoredSessionRole(): string {
try {
if (typeof window === "undefined") return "";
@@ -251,67 +250,73 @@ export const aiGenerationClient = {
projectId: input.projectId,
conversationId: input.conversationId,
});
const payload = await serverRequest<ImageTaskCreateResponse>("ai/image", {
const payload = await serverRequest<unknown>("ai/image", {
method: "POST",
body: input,
timeoutMs: TASK_SUBMIT_TIMEOUT_MS,
maxRetries: NON_RETRYING_REQUEST.maxRetries,
fallbackMessage: "Image generation request failed",
});
if (payload.providerDebug) {
emitImageRouteDebug("[ai/image-provider-debug]", payload.providerDebug as Record<string, unknown>);
const parsed = parseImageTaskCreateResponse(payload);
if (parsed.providerDebug) {
emitImageRouteDebug("[ai/image-provider-debug]", parsed.providerDebug as Record<string, unknown>);
}
return payload;
return parsed;
},
async createVideoTask(input: VideoGenInput): Promise<{ taskId: string }> {
return serverRequest<{ taskId: string }>("ai/video", {
const payload = await serverRequest<unknown>("ai/video", {
method: "POST",
body: input,
timeoutMs: TASK_SUBMIT_TIMEOUT_MS,
maxRetries: NON_RETRYING_REQUEST.maxRetries,
fallbackMessage: "Video generation request failed",
});
return parseTaskCreateResponse(payload);
},
async createVideoSuperResolveTask(input: VideoSuperResolveInput): Promise<{ taskId: string }> {
return serverRequest<{ taskId: string }>("ai/video/super-resolve", {
const payload = await serverRequest<unknown>("ai/video/super-resolve", {
method: "POST",
body: input,
timeoutMs: TASK_SUBMIT_TIMEOUT_MS,
maxRetries: NON_RETRYING_REQUEST.maxRetries,
fallbackMessage: "Video super-resolution request failed",
});
return parseTaskCreateResponse(payload);
},
async createEraseSubtitlesTask(input: EraseSubtitlesInput): Promise<{ taskId: string }> {
return serverRequest<{ taskId: string }>("ai/video/erase-subtitles", {
const payload = await serverRequest<unknown>("ai/video/erase-subtitles", {
method: "POST",
body: input,
timeoutMs: TASK_SUBMIT_TIMEOUT_MS,
maxRetries: NON_RETRYING_REQUEST.maxRetries,
fallbackMessage: "Subtitle removal request failed",
});
return parseTaskCreateResponse(payload);
},
async createImageSuperResolveTask(input: ImageSuperResolveInput): Promise<{ taskId: string }> {
return serverRequest<{ taskId: string }>("ai/image/super-resolve", {
const payload = await serverRequest<unknown>("ai/image/super-resolve", {
method: "POST",
body: input,
timeoutMs: TASK_SUBMIT_TIMEOUT_MS,
maxRetries: NON_RETRYING_REQUEST.maxRetries,
fallbackMessage: "Image super-resolution request failed",
});
return parseTaskCreateResponse(payload);
},
async createImageEditTask(input: ImageEditInput): Promise<{ taskId: string }> {
return serverRequest<{ taskId: string }>("ai/image/edit", {
const payload = await serverRequest<unknown>("ai/image/edit", {
method: "POST",
body: input,
timeoutMs: TASK_SUBMIT_TIMEOUT_MS,
maxRetries: NON_RETRYING_REQUEST.maxRetries,
fallbackMessage: "Image edit request failed",
});
return parseTaskCreateResponse(payload);
},
async cancelTask(taskId: string): Promise<void> {
@@ -328,10 +333,11 @@ export const aiGenerationClient = {
},
async getTaskStatus(taskId: string): Promise<AiTaskStatus> {
return serverRequest<AiTaskStatus>(`ai/tasks/${taskId}`, {
const payload = await serverRequest<unknown>(`ai/tasks/${taskId}`, {
timeoutMs: TASK_STATUS_TIMEOUT_MS,
fallbackMessage: "Task status request failed",
});
return parseAiTaskStatus(payload);
},
async downloadTaskResult(taskId: string): Promise<{ blob: Blob; filename?: string; contentType?: string }> {
@@ -361,7 +367,7 @@ export const aiGenerationClient = {
const payload = await serverRequest<unknown>(`ai/tasks${search.toString() ? `?${search}` : ""}`, {
fallbackMessage: "Task history request failed",
});
return extractTaskList(payload).map(toPreviewTask);
return parseAiTaskStatusList(payload).map(toPreviewTask);
} catch (error) {
if (isOptionalApiRouteMissing(error)) {
taskHistoryRouteMissing = true;
@@ -451,7 +457,7 @@ export const aiGenerationClient = {
if (!line.startsWith("data: ")) continue;
try {
const data = JSON.parse(line.slice(6));
onUpdate(data);
onUpdate(parseSseTaskFrame(data));
} catch { /* ignore */ }
}
}