Jules
Final deployment with all fixes and verified content
c09f67c
/**
* Timeout wrapper utility for external API calls
* Prevents jobs from hanging indefinitely on slow or unresponsive services
*/
export class TimeoutError extends Error {
constructor(
message: string,
public readonly timeoutMs: number,
) {
super(message);
this.name = "TimeoutError";
}
}
/**
* Wrap a promise with a timeout
* @param promise The promise to wrap
* @param timeoutMs Timeout in milliseconds
* @param errorMessage Optional custom error message
* @returns The promise result or throws TimeoutError
*/
export async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
errorMessage?: string,
): Promise<T> {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
const timeoutPromise = new Promise<never>((_, reject) => {
timeoutId = setTimeout(() => {
reject(
new TimeoutError(
errorMessage || `Operation timed out after ${timeoutMs}ms`,
timeoutMs,
),
);
}, timeoutMs);
});
try {
return await Promise.race([promise, timeoutPromise]);
} finally {
if (timeoutId) {
clearTimeout(timeoutId);
}
}
}
/**
* Default timeout configurations for different operation types
*/
export const TIMEOUTS = {
EMBEDDING: 30_000, // 30 seconds for embedding generation
DOCUMENT_PROCESSING: 600_000, // 10 minutes for document processing
// Multi-pass extraction can take time: Pass 1 (primary), Pass 2 (fallback),
// Pass 3 (field re-extraction), Pass 4 (consistency validation), plus retries
FILE_DOWNLOAD: 60_000, // 1 minute for file downloads
FILE_UPLOAD: 60_000, // 1 minute for file uploads
DATABASE_QUERY: 15_000, // 15 seconds for database queries
EXTERNAL_API: 30_000, // 30 seconds for external API calls
AI_CLASSIFICATION: 90_000, // 90 seconds for AI document/image classification
// AI classification can be slow for complex documents with OCR, multiple pages,
// or when the model is under load. 90s provides sufficient buffer.
CLASSIFICATION_JOB_WAIT: 180_000, // 3 minutes for waiting on classification jobs
// Must be >= AI_CLASSIFICATION (90s) + FILE_DOWNLOAD (60s for images) + job overhead
// Using 180s to ensure parent job doesn't timeout while child classification is valid
// This prevents race conditions where parent marks "failed" but child completes "completed"
} as const;
/**
* Image size configurations for processing
* Based on research: 2048px is optimal for vision models + OCR
* - Preserves text legibility (x-height >= 20px for receipts)
* - Within all major AI model limits (Gemini, GPT-4V, Claude)
* - Good balance between OCR quality and processing speed
*/
export const IMAGE_SIZES = {
MAX_DIMENSION: 2048, // Max width/height for image processing
} as const;