This commit is contained in:
@@ -44,6 +44,7 @@ export interface ImageProviderDebug {
|
||||
|
||||
export interface ImageTaskCreateResponse {
|
||||
taskId: string;
|
||||
resultUrl?: string | null;
|
||||
providerDebug?: ImageProviderDebug;
|
||||
}
|
||||
|
||||
@@ -97,6 +98,7 @@ export interface ImageEditInput {
|
||||
prompt?: string;
|
||||
maskUrl?: string;
|
||||
ratio?: string;
|
||||
referenceUrls?: string[];
|
||||
n?: number;
|
||||
}
|
||||
|
||||
@@ -126,7 +128,7 @@ export type ChatMessageContent =
|
||||
| Array<{ type: "text"; text: string } | { type: "image_url"; image_url: { url: string } }>;
|
||||
|
||||
export interface ChatInput {
|
||||
model: string;
|
||||
model?: string;
|
||||
messages: Array<{ role: string; content: ChatMessageContent }>;
|
||||
stream?: boolean;
|
||||
temperature?: number;
|
||||
|
||||
@@ -110,6 +110,15 @@ describe("parseImageTaskCreateResponse", () => {
|
||||
expect(result.providerDebug).toBeUndefined();
|
||||
});
|
||||
|
||||
it("extracts immediate image result URLs", () => {
|
||||
const result = parseImageTaskCreateResponse({
|
||||
taskId: "img-sync",
|
||||
result_url: "https://example.com/result.png",
|
||||
});
|
||||
expect(result.taskId).toBe("img-sync");
|
||||
expect(result.resultUrl).toBe("https://example.com/result.png");
|
||||
});
|
||||
|
||||
it("tolerates snake_case providerDebug fields", () => {
|
||||
const result = parseImageTaskCreateResponse({
|
||||
taskId: "img-3",
|
||||
|
||||
@@ -130,8 +130,13 @@ export function parseTaskCreateResponse(payload: unknown): { taskId: string } {
|
||||
export function parseImageTaskCreateResponse(payload: unknown): ImageTaskCreateResponse {
|
||||
const base = parseTaskCreateResponse(payload);
|
||||
const body = isRecord(payload) ? payload : {};
|
||||
const resultUrl = toNullableString(body.resultUrl ?? body.result_url);
|
||||
const providerDebug = normalizeProviderDebug(body.providerDebug ?? body.provider_debug);
|
||||
return providerDebug ? { ...base, providerDebug } : base;
|
||||
return {
|
||||
...base,
|
||||
resultUrl,
|
||||
...(providerDebug ? { providerDebug } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,6 +38,39 @@ export interface SaveGenerationRecordResult {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface GenerationRecord {
|
||||
id: string;
|
||||
clientRecordId: string;
|
||||
tool: string;
|
||||
mode?: string;
|
||||
title: string;
|
||||
status: GenerationRecordStatus;
|
||||
prompt?: string;
|
||||
taskIds: string[];
|
||||
assets: GenerationRecordAsset[];
|
||||
config: Record<string, unknown>;
|
||||
result: Record<string, unknown>;
|
||||
metadata: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface ListGenerationRecordsParams {
|
||||
tool?: string;
|
||||
mode?: string;
|
||||
status?: GenerationRecordStatus;
|
||||
q?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface ListGenerationRecordsResult {
|
||||
items: GenerationRecord[];
|
||||
total: number;
|
||||
limit: number;
|
||||
offset: number;
|
||||
}
|
||||
|
||||
// 同一 clientRecordId 的保存去重:套图主流程、backgroundTaskRunner、useGenerationTasks
|
||||
// 三处都可能对同一条终态任务调用 saveGenerationRecord,SSE 重复推送 completed 时
|
||||
// 单个 poller 内也会重复触发。这里做客户端幂等:in-flight 合流 + 成功后短期拦截,
|
||||
@@ -185,6 +218,23 @@ export async function flushPendingGenerationRecords(): Promise<{ synced: number;
|
||||
return { synced, remaining: remaining.length };
|
||||
}
|
||||
|
||||
export async function listGenerationRecords(params: ListGenerationRecordsParams = {}): Promise<ListGenerationRecordsResult> {
|
||||
const search = new URLSearchParams();
|
||||
if (params.tool) search.set("tool", params.tool);
|
||||
if (params.mode) search.set("mode", params.mode);
|
||||
if (params.status) search.set("status", params.status);
|
||||
if (params.q) search.set("q", params.q);
|
||||
if (params.limit !== undefined) search.set("limit", String(params.limit));
|
||||
if (params.offset !== undefined) search.set("offset", String(params.offset));
|
||||
|
||||
const suffix = search.toString();
|
||||
return serverRequest<ListGenerationRecordsResult>(`ai/generation-records${suffix ? `?${suffix}` : ""}`, {
|
||||
method: "GET",
|
||||
maxRetries: 1,
|
||||
fallbackMessage: "Failed to load generation records",
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteGenerationRecordByClientId(clientRecordId: string): Promise<void> {
|
||||
await serverRequest<{ success: boolean }>(`ai/generation-records/by-client-id/${encodeURIComponent(clientRecordId)}`, {
|
||||
method: "DELETE",
|
||||
|
||||
Reference in New Issue
Block a user