From 01cb95d8e7cc765964269ae8596bbb20c62d4cb3 Mon Sep 17 00:00:00 2001 From: Richard R Date: Tue, 19 May 2026 19:38:36 -0600 Subject: [PATCH] fix(compute): migrate compute backend initialization to async and update consumers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor compute backend initialization to use asynchronous loading and promise caching, replacing previous synchronous singleton pattern. Update all consumers—including TTS segment alignment and PDF layout parsing jobs—to await compute backend resolution before invoking methods. This change improves compatibility with dynamic imports and future-proofs backend selection logic. --- src/app/api/tts/segments/ensure/route.ts | 11 +++++++++-- src/lib/server/compute/index.ts | 20 +++++++++++--------- src/lib/server/jobs/parsePdfJob.ts | 3 ++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/app/api/tts/segments/ensure/route.ts b/src/app/api/tts/segments/ensure/route.ts index aa80c1e..e16c084 100644 --- a/src/app/api/tts/segments/ensure/route.ts +++ b/src/app/api/tts/segments/ensure/route.ts @@ -150,6 +150,11 @@ async function deleteEntryIfUnused(userId: string, segmentEntryId: string): Prom export async function POST(request: NextRequest) { let didCreateDeviceIdCookie = false; let deviceIdToSet: string | null = null; + let computeBackendPromise: ReturnType | null = null; + const getComputeBackend = async () => { + if (!computeBackendPromise) computeBackendPromise = getCompute(); + return computeBackendPromise; + }; try { if (!isS3Configured()) return s3NotConfiguredResponse(); @@ -372,7 +377,8 @@ export async function POST(request: NextRequest) { // previously unavailable, retry alignment using the current segment text. if (!alignment) { try { - const aligned = (await getCompute().alignWords({ + const computeBackend = await getComputeBackend(); + const aligned = (await computeBackend.alignWords({ audioObjectKey: existing.audioKey, text: segment.text, })).alignments; @@ -523,7 +529,8 @@ export async function POST(request: NextRequest) { let alignment: TTSSegmentManifestItem['alignment'] = null; try { const whisperBytes = Uint8Array.from(persistedBuffer); - const aligned = (await getCompute().alignWords({ + const computeBackend = await getComputeBackend(); + const aligned = (await computeBackend.alignWords({ audioBuffer: whisperBytes.buffer, audioObjectKey: audioKey, text: segment.text, diff --git a/src/lib/server/compute/index.ts b/src/lib/server/compute/index.ts index 4424f1f..5442429 100644 --- a/src/lib/server/compute/index.ts +++ b/src/lib/server/compute/index.ts @@ -2,21 +2,23 @@ import type { ComputeBackend, ComputeMode } from '@/lib/server/compute/types'; import { isComputeModeAvailable, readComputeMode } from '@/lib/server/compute/mode'; import { WorkerComputeBackend } from '@/lib/server/compute/worker'; -let backend: ComputeBackend | null = null; +let backendPromise: Promise | null = null; -function createBackend(): ComputeBackend { +async function createBackend(): Promise { const mode: ComputeMode = readComputeMode(); if (mode === 'worker') return new WorkerComputeBackend(); - // Intentionally lazy-load local compute to avoid tracing heavy ONNX - // dependencies unless the backend is actually local. - // eslint-disable-next-line @typescript-eslint/no-require-imports - const { LocalComputeBackend } = require('@/lib/server/compute/local') as typeof import('@/lib/server/compute/local'); + const { LocalComputeBackend } = await import('@/lib/server/compute/local'); return new LocalComputeBackend(); } -export function getCompute(): ComputeBackend { - if (!backend) backend = createBackend(); - return backend; +export async function getCompute(): Promise { + if (!backendPromise) { + backendPromise = createBackend().catch((error) => { + backendPromise = null; + throw error; + }); + } + return backendPromise; } export function isComputeAvailable(): boolean { diff --git a/src/lib/server/jobs/parsePdfJob.ts b/src/lib/server/jobs/parsePdfJob.ts index 1410949..c64cea9 100644 --- a/src/lib/server/jobs/parsePdfJob.ts +++ b/src/lib/server/jobs/parsePdfJob.ts @@ -28,7 +28,8 @@ export async function parsePdfJob(input: ParsePdfJobInput): Promise { .set({ parseStatus: 'running' }) .where(and(eq(documents.id, input.documentId), eq(documents.userId, input.userId))); - const parsed = await getCompute().parsePdfLayout({ + const compute = await getCompute(); + const parsed = await compute.parsePdfLayout({ documentId: input.documentId, namespace: input.namespace, documentObjectKey: documentKey(input.documentId, input.namespace),