compute: centralize timeout config in compute-core and use in worker

This commit is contained in:
Richard R 2026-05-21 16:15:34 -06:00
parent 0d494cf5e5
commit fbb6f484ce
3 changed files with 48 additions and 6 deletions

View file

@ -14,6 +14,7 @@
"exports": {
".": "./src/index.ts",
"./contracts": "./src/contracts.ts",
"./local-runtime": "./src/local-runtime.ts"
"./local-runtime": "./src/local-runtime.ts",
"./runtime/timeout-config": "./src/runtime/timeout-config.ts"
}
}

View file

@ -0,0 +1,39 @@
const DEFAULT_COMPUTE_WHISPER_TIMEOUT_MS = 30_000;
const DEFAULT_COMPUTE_PDF_TIMEOUT_MS = 300_000;
const DEFAULT_COMPUTE_PDF_HARD_CAP_MS = 24 * 60 * 60 * 1000;
const DEFAULT_WORKER_WAIT_BUFFER_MS = 15_000;
const DEFAULT_WORKER_WAIT_MIN_MS = 60_000;
export type ComputeTimeoutConfig = {
whisperTimeoutMs: number;
pdfTimeoutMs: number;
pdfHardCapMs: number;
};
export type ComputeOperationKind = 'whisper_align' | 'pdf_layout';
function readPositiveIntEnv(name: string, fallback: number): number {
const raw = process.env[name]?.trim();
if (!raw) return fallback;
const parsed = Number(raw);
if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
return Math.floor(parsed);
}
let timeoutConfigCache: ComputeTimeoutConfig | null = null;
export function getComputeTimeoutConfig(): ComputeTimeoutConfig {
if (timeoutConfigCache) return timeoutConfigCache;
timeoutConfigCache = {
whisperTimeoutMs: readPositiveIntEnv('COMPUTE_WHISPER_TIMEOUT_MS', DEFAULT_COMPUTE_WHISPER_TIMEOUT_MS),
pdfTimeoutMs: readPositiveIntEnv('COMPUTE_PDF_TIMEOUT_MS', DEFAULT_COMPUTE_PDF_TIMEOUT_MS),
pdfHardCapMs: DEFAULT_COMPUTE_PDF_HARD_CAP_MS,
};
return timeoutConfigCache;
}
export function getWorkerClientWaitTimeoutMs(kind: ComputeOperationKind): number {
const config = getComputeTimeoutConfig();
const timeoutMs = kind === 'pdf_layout' ? config.pdfTimeoutMs : config.whisperTimeoutMs;
return Math.max(DEFAULT_WORKER_WAIT_MIN_MS, timeoutMs + DEFAULT_WORKER_WAIT_BUFFER_MS);
}

View file

@ -27,6 +27,7 @@ import {
runPdfLayoutFromPdfBuffer,
runWhisperAlignmentFromAudioBuffer,
} from '@openreader/compute-core/local-runtime';
import { getComputeTimeoutConfig } from '@openreader/compute-core/runtime/timeout-config';
import {
type PdfLayoutJobRequest,
type PdfLayoutJobResult,
@ -56,7 +57,6 @@ const RUNNING_HEARTBEAT_MS = 5000;
const DOCUMENT_ID_REGEX = /^[a-f0-9]{64}$/i;
const SAFE_NAMESPACE_REGEX = /^[a-zA-Z0-9._-]{1,128}$/;
const WHISPER_MAX_DELIVER = 1;
const PDF_LAYOUT_HARD_CAP_MS = 24 * 60 * 60 * 1000;
const NATS_API_TIMEOUT_MS = 60_000;
interface QueuedJob<TPayload> {
@ -472,10 +472,12 @@ async function main(): Promise<void> {
const host = process.env.COMPUTE_WORKER_HOST?.trim() || '0.0.0.0';
const workerToken = requireEnv('COMPUTE_WORKER_TOKEN');
const natsUrl = requireEnv('NATS_URL');
const timeoutConfig = getComputeTimeoutConfig();
const jobConcurrency = readIntEnv('COMPUTE_JOB_CONCURRENCY', 1);
const whisperTimeoutMs = readIntEnv('COMPUTE_WHISPER_TIMEOUT_MS', 30_000);
const pdfTimeoutMs = readIntEnv('COMPUTE_PDF_TIMEOUT_MS', 5 * 60_000);
const whisperTimeoutMs = timeoutConfig.whisperTimeoutMs;
const pdfTimeoutMs = timeoutConfig.pdfTimeoutMs;
const pdfHardCapMs = timeoutConfig.pdfHardCapMs;
const pdfAttempts = readIntEnv('COMPUTE_PDF_JOB_ATTEMPTS', 1);
const prewarmModels = parseBoolEnv('COMPUTE_PREWARM_MODELS', true);
const jobsStreamMaxBytes = readIntEnv('COMPUTE_JOBS_STREAM_MAX_BYTES', 256 * 1024 * 1024);
@ -560,7 +562,7 @@ async function main(): Promise<void> {
availableCpuCores: getAvailableCpuCores(),
onnxThreadsPerJob: getOnnxThreadsPerJob(jobConcurrency),
natsApiTimeoutMs: NATS_API_TIMEOUT_MS,
pdfLayoutHardCapMs: PDF_LAYOUT_HARD_CAP_MS,
pdfLayoutHardCapMs: pdfHardCapMs,
}, 'compute runtime config');
const opIndexCodec = createJsonCodec<OpIndexEntry>();
@ -914,7 +916,7 @@ async function main(): Promise<void> {
const computeStartedAt = Date.now();
const result = await withIdleTimeoutAndHardCap({
idleTimeoutMs: Math.max(pdfTimeoutMs, 1_000),
hardCapMs: PDF_LAYOUT_HARD_CAP_MS,
hardCapMs: pdfHardCapMs,
label: 'pdf layout job',
run: async (touchProgress) => runPdfLayoutFromPdfBuffer({
documentId: parsed.documentId,