diff --git a/src/lib/server/compute/index.ts b/src/lib/server/compute/index.ts index 25633e4..ecfea22 100644 --- a/src/lib/server/compute/index.ts +++ b/src/lib/server/compute/index.ts @@ -1,24 +1,10 @@ -import type { ComputeBackend, ComputeMode } from '@/lib/server/compute/types'; -import { isComputeModeAvailable, readComputeMode } from '@/lib/server/compute/mode'; -import { WorkerComputeBackend } from '@/lib/server/compute/worker'; - -declare const __OPENREADER_COMPUTE_MODE__: 'local' | 'worker' | 'none'; +import type { ComputeBackend } from '@/lib/server/compute/types'; +import { isWorkerClientConfigAvailable, WorkerComputeBackend } from '@/lib/server/compute/worker'; let backendPromise: Promise | null = null; async function createBackend(): Promise { - const bundledMode = - typeof __OPENREADER_COMPUTE_MODE__ === 'undefined' ? 'none' : __OPENREADER_COMPUTE_MODE__; - - if (bundledMode === 'worker') return new WorkerComputeBackend(); - if (bundledMode === 'local') { - const { LocalComputeBackend } = await import('@/lib/server/compute/local'); - return new LocalComputeBackend(); - } - const mode: ComputeMode = readComputeMode(); - if (mode === 'worker') return new WorkerComputeBackend(); - const { LocalComputeBackend } = await import('@/lib/server/compute/local'); - return new LocalComputeBackend(); + return new WorkerComputeBackend(); } export async function getCompute(): Promise { @@ -32,5 +18,5 @@ export async function getCompute(): Promise { } export function isComputeAvailable(): boolean { - return isComputeModeAvailable(); + return isWorkerClientConfigAvailable(); } diff --git a/src/lib/server/compute/index.worker.ts b/src/lib/server/compute/index.worker.ts deleted file mode 100644 index fbbaf7b..0000000 --- a/src/lib/server/compute/index.worker.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ComputeBackend } from '@/lib/server/compute/types'; -import { WorkerComputeBackend } from '@/lib/server/compute/worker'; - -let backendPromise: Promise | null = null; - -export async function getCompute(): Promise { - if (!backendPromise) { - backendPromise = Promise.resolve() - .then(() => new WorkerComputeBackend()) - .catch((error) => { - backendPromise = null; - throw error; - }); - } - return backendPromise; -} - -export function isComputeAvailable(): boolean { - return true; -} diff --git a/src/lib/server/compute/local.ts b/src/lib/server/compute/local.ts deleted file mode 100644 index 001ed1e..0000000 --- a/src/lib/server/compute/local.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { ComputeBackend, PdfLayoutInput, WhisperAlignInput, WhisperAlignResult } from '@/lib/server/compute/types'; -import { LOCAL_COMPUTE_LIMITER } from '@/lib/server/compute/concurrency-limiter'; -import { getDocumentBlob } from '@/lib/server/documents/blobstore'; -import { getTtsSegmentAudioObject } from '@/lib/server/tts/segments-blobstore'; -import { - getComputeTimeoutConfig, - withIdleTimeoutAndHardCap, - withTimeout, -} from '@openreader/compute-core'; -import type { PdfLayoutProgress } from '@openreader/compute-core/api-contracts'; -import { - runPdfLayoutFromPdfBuffer, - runWhisperAlignmentFromAudioBuffer, -} from '@openreader/compute-core/local-runtime'; - -export class LocalComputeBackend implements ComputeBackend { - readonly mode = 'local' as const; - private readonly timeoutConfig = getComputeTimeoutConfig(); - - async alignWords(input: WhisperAlignInput): Promise { - return LOCAL_COMPUTE_LIMITER.run(async () => { - let audioBuffer = input.audioBuffer ?? null; - if (!audioBuffer && input.audioObjectKey) { - const bytes = new Uint8Array(await getTtsSegmentAudioObject(input.audioObjectKey)); - audioBuffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); - } - if (!audioBuffer) { - throw new Error('Local compute alignment requires audioBuffer or audioObjectKey'); - } - return withTimeout( - runWhisperAlignmentFromAudioBuffer({ - audioBuffer, - text: input.text, - cacheKey: input.cacheKey, - lang: input.lang, - }), - this.timeoutConfig.whisperTimeoutMs, - 'local whisper alignment job', - ); - }); - } - - async parsePdfLayout(input: PdfLayoutInput) { - return LOCAL_COMPUTE_LIMITER.run(async () => { - let pdfBytes = input.pdfBytes ?? null; - if (!pdfBytes && input.documentId && typeof input.namespace !== 'undefined') { - const bytes = new Uint8Array(await getDocumentBlob(input.documentId, input.namespace)); - pdfBytes = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength); - } - if (!pdfBytes) { - throw new Error('Local compute PDF layout requires pdfBytes or (documentId + namespace)'); - } - - return { - parsed: (await withIdleTimeoutAndHardCap({ - idleTimeoutMs: Math.max(this.timeoutConfig.pdfTimeoutMs, 1_000), - hardCapMs: this.timeoutConfig.pdfHardCapMs, - label: 'local pdf layout job', - run: async (touchProgress) => runPdfLayoutFromPdfBuffer({ - documentId: input.documentId, - pdfBytes, - onPageParsed: (page) => { - touchProgress(); - const progress: PdfLayoutProgress = { - totalPages: page.totalPages, - pagesParsed: page.pageNumber, - currentPage: page.pageNumber, - phase: 'infer', - }; - return input.onProgress?.(progress); - }, - }), - })).parsed, - }; - }); - } -} diff --git a/src/lib/server/compute/mode.ts b/src/lib/server/compute/mode.ts deleted file mode 100644 index 5c2b83d..0000000 --- a/src/lib/server/compute/mode.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { ComputeMode } from '@/lib/server/compute/types'; - -export function readComputeMode(): ComputeMode { - const envValue = process.env.COMPUTE_MODE; - const raw = (envValue || '').trim().toLowerCase(); - if (!raw) return 'local'; - if (raw === 'local' || raw === 'worker') return raw; - throw new Error( - `Invalid COMPUTE_MODE="${envValue}". Expected "local" or "worker".`, - ); -} - -export function isComputeModeAvailable(): boolean { - // Throws on invalid values so startup/runtime config fails fast. - readComputeMode(); - return true; -} diff --git a/src/lib/server/compute/types.ts b/src/lib/server/compute/types.ts index f93a8f3..5f21b57 100644 --- a/src/lib/server/compute/types.ts +++ b/src/lib/server/compute/types.ts @@ -6,7 +6,7 @@ import type { import type { PdfLayoutProgress, WhisperAlignJobBase } from '@openreader/compute-core/api-contracts'; import type { PdfLayoutJobResult, WorkerOperationState } from '@openreader/compute-core/api-contracts'; -export type ComputeMode = 'local' | 'worker'; +export type ComputeMode = 'worker'; export interface WhisperAlignInput extends WhisperAlignJobBase { audioBuffer?: TTSAudioBuffer; @@ -32,7 +32,7 @@ export type PdfLayoutResult = | { parsed?: never; parsedObjectKey: string }; export interface ComputeBackend { - mode: ComputeMode; + mode: 'worker'; alignWords(input: WhisperAlignInput): Promise; parsePdfLayout(input: PdfLayoutInput): Promise; } diff --git a/src/lib/server/compute/worker.ts b/src/lib/server/compute/worker.ts index 3be3897..3f4dcf5 100644 --- a/src/lib/server/compute/worker.ts +++ b/src/lib/server/compute/worker.ts @@ -93,7 +93,7 @@ function opSummary(value: unknown): Record { function readRequiredEnv(name: string): string { const value = process.env[name]?.trim(); - if (!value) throw new Error(`${name} is required when COMPUTE_MODE=worker`); + if (!value) throw new Error(`${name} is required for compute worker client`); return value; } @@ -127,6 +127,15 @@ export function getWorkerClientConfigFromEnv(): { baseUrl: string; token: string }; } +export function isWorkerClientConfigAvailable(): boolean { + try { + getWorkerClientConfigFromEnv(); + return true; + } catch { + return false; + } +} + function parseRetryAfterMs(value: string | null): number | null { if (!value) return null; const asNum = Number(value); diff --git a/src/lib/server/runtime-config.ts b/src/lib/server/runtime-config.ts index 14be70f..87889d8 100644 --- a/src/lib/server/runtime-config.ts +++ b/src/lib/server/runtime-config.ts @@ -6,7 +6,7 @@ import { type RuntimeConfigKey, type RuntimeConfigSource, } from '@/lib/server/admin/settings'; -import { isComputeModeAvailable } from '@/lib/server/compute/mode'; +import { isComputeAvailable } from '@/lib/server/compute'; export type ResolvedRuntimeConfig = RuntimeConfig & { computeAvailable: boolean; @@ -29,7 +29,7 @@ export async function getResolvedRuntimeConfig(): Promise const values = await getRuntimeConfig(); return { ...values, - computeAvailable: isComputeModeAvailable(), + computeAvailable: isComputeAvailable(), }; }