refactor(convex): modularize ai generation helpers and cleanup flows

This commit is contained in:
2026-04-07 09:21:40 +02:00
parent ed08b976f9
commit c10839b27e
5 changed files with 470 additions and 344 deletions

61
convex/ai_retry.ts Normal file
View File

@@ -0,0 +1,61 @@
import { categorizeError, errorMessage, type ErrorCategory } from "./ai_errors";
function wait(ms: number) {
return new Promise<void>((resolve) => {
setTimeout(resolve, ms);
});
}
export async function generateImageWithAutoRetry<T>(
operation: () => Promise<T>,
onRetry: (
retryCount: number,
maxRetries: number,
failure: { message: string; category: ErrorCategory }
) => Promise<void>,
maxRetries: number
): Promise<T> {
let lastError: unknown = null;
const startedAt = Date.now();
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const attemptStartedAt = Date.now();
try {
const result = await operation();
console.info("[generateImageWithAutoRetry] success", {
attempts: attempt + 1,
totalDurationMs: Date.now() - startedAt,
lastAttemptDurationMs: Date.now() - attemptStartedAt,
});
return result;
} catch (error) {
lastError = error;
const { retryable, category } = categorizeError(error);
const retryCount = attempt + 1;
const hasRemainingRetry = retryCount <= maxRetries;
console.warn("[generateImageWithAutoRetry] attempt failed", {
attempt: retryCount,
maxAttempts: maxRetries + 1,
retryable,
hasRemainingRetry,
category,
attemptDurationMs: Date.now() - attemptStartedAt,
totalDurationMs: Date.now() - startedAt,
message: errorMessage(error),
});
if (!retryable || !hasRemainingRetry) {
throw error;
}
await onRetry(retryCount, maxRetries, {
message: errorMessage(error),
category,
});
await wait(Math.min(1500, 400 * retryCount));
}
}
throw lastError ?? new Error("Generation failed");
}