fix(compute): migrate compute backend initialization to async and update consumers

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.
This commit is contained in:
Richard R 2026-05-19 19:38:36 -06:00
parent e52b754106
commit 01cb95d8e7
3 changed files with 22 additions and 12 deletions

View file

@ -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<typeof getCompute> | 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,

View file

@ -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<ComputeBackend> | null = null;
function createBackend(): ComputeBackend {
async function createBackend(): Promise<ComputeBackend> {
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<ComputeBackend> {
if (!backendPromise) {
backendPromise = createBackend().catch((error) => {
backendPromise = null;
throw error;
});
}
return backendPromise;
}
export function isComputeAvailable(): boolean {

View file

@ -28,7 +28,8 @@ export async function parsePdfJob(input: ParsePdfJobInput): Promise<void> {
.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),