@sema-agent/core
Version:
Stateless, task-oriented AI agent core
217 lines (216 loc) • 9.01 kB
JavaScript
export const MAX_TIMER_DELAY_MS = 2_147_483_647;
export function resolveStallTimeoutMs(value, knob) {
if (value === undefined)
return undefined;
if (!Number.isFinite(value) || value < 0) {
const e = new Error(`${knob} must be a non-negative finite number of milliseconds (got ${String(value)})`);
e.code = "config.stall_timeout_invalid";
throw e;
}
if (value > MAX_TIMER_DELAY_MS) {
const e = new Error(`${knob} must not exceed ${MAX_TIMER_DELAY_MS}ms (~24.8 days) — a host timer truncates a larger delay and fires immediately, so this watchdog would abort every call instead of tolerating a long one (got ${String(value)}). Pass 0 to disable it.`);
e.code = "config.stall_timeout_invalid";
throw e;
}
return value;
}
export const STALL_FIRST_BYTE_MS = 300_000;
export const STALL_FIRST_TOKEN_MS = 600_000;
export const STALL_IDLE_MS = 300_000;
export const RETIRED_TIMEOUT_KNOBS = {
construction: { connectTimeoutMs: "firstByteTimeoutMs" },
advisory: { connectMs: "firstByteMs" },
};
export function refuseRetiredTimeoutKnobs(holder, level, where) {
if (holder === undefined || holder === null)
return;
for (const [retired, replacement] of Object.entries(RETIRED_TIMEOUT_KNOBS[level])) {
if (!(retired in holder))
continue;
const e = new Error(`${where}: \`${retired}\` was removed in 7.33.0 and is refused rather than ignored — the fetch()→response-headers wait it bounded is \`${replacement}\` (0 = off); the TCP + TLS connect is the transport's own clock, tuned on the process dispatcher (setGlobalDispatcher(new Agent({ connect: { timeout } })))`);
e.code = "config.stall_timeout_retired";
throw e;
}
}
export const FIRST_BYTE_UPLOAD_BYTES_PER_SECOND = 32_768;
export const FIRST_BYTE_RETRY_BUDGET = 1;
export function resolveFirstByteRetries(value) {
if (value === undefined)
return FIRST_BYTE_RETRY_BUDGET;
if (value === 0 || value === 1)
return value;
const shown = typeof value === "string" ? JSON.stringify(value) : typeof value === "bigint" ? `${value}n` : typeof value === "object" && value !== null ? "an object" : String(value);
const e = new Error(`firstByteRetries must be 0 or 1 (got ${shown}) — 1 re-sends a first-byte stall once at a widened window (the default), 0 ends the call at the first window so the caller's own failover takes it`);
e.code = "config.stall_timeout_invalid";
throw e;
}
export const FIRST_BYTE_RETRY_WINDOW_FACTOR = 2;
export function resolveWatchdogMs(configured, advisory, engineDefault, knob) {
return resolveStallTimeoutMs(configured ?? advisory, knob) ?? engineDefault;
}
export function firstByteWindow(knobMs, bodyBytes, retryIndex) {
if (knobMs === undefined || knobMs <= 0)
return { knobMs: 0, allowanceMs: 0, windowMs: 0 };
const widened = knobMs * FIRST_BYTE_RETRY_WINDOW_FACTOR ** Math.max(0, retryIndex);
const allowanceMs = Math.ceil(Math.max(0, bodyBytes) / FIRST_BYTE_UPLOAD_BYTES_PER_SECOND) * 1000;
return { knobMs: widened, allowanceMs, windowMs: Math.min(widened + allowanceMs, MAX_TIMER_DELAY_MS) };
}
export function firstByteLaneKnobMs(knobMs) {
let total = 0;
for (let retryIndex = 0; retryIndex <= FIRST_BYTE_RETRY_BUDGET; retryIndex++)
total += firstByteWindow(knobMs, 0, retryIndex).knobMs;
return total;
}
export function createAttemptController(firstByteWindowMs, outerSignal) {
const ac = new AbortController();
let timedOut = false;
const onAbort = () => ac.abort(outerSignal?.reason);
if (outerSignal) {
if (outerSignal.aborted)
ac.abort(outerSignal.reason);
else
outerSignal.addEventListener("abort", onAbort, { once: true });
}
let timer;
if (firstByteWindowMs && firstByteWindowMs > 0) {
timer = setTimeout(() => {
timedOut = true;
ac.abort();
}, firstByteWindowMs);
}
const clearTimer = () => {
if (timer)
clearTimeout(timer);
timer = undefined;
};
return {
signal: ac.signal,
timedOut: () => timedOut,
clearTimer,
abort: () => ac.abort(),
dispose: () => {
clearTimer();
if (outerSignal)
outerSignal.removeEventListener("abort", onAbort);
},
};
}
export const BRAIN_CALL_GUARDRAIL_DEFAULT_MS = 3_600_000;
export const BRAIN_CALL_GUARDRAIL_ERROR_CODE = "brain.call_guardrail_timeout";
export class BrainCallGuardrailTimeoutError extends Error {
code = BRAIN_CALL_GUARDRAIL_ERROR_CODE;
limitMs;
constructor(limitMs) {
super(`the engine stopped waiting for a brain call that ran past its ${limitMs}ms guardrail — the call was abandoned (a Brain that ignores options.signal may still be running)`);
this.name = "BrainCallGuardrailTimeoutError";
this.limitMs = limitMs;
}
}
export function resolveBrainCallGuardrailMs(knob) {
if (knob === undefined)
return BRAIN_CALL_GUARDRAIL_DEFAULT_MS;
if (knob === false || knob === 0)
return undefined;
if (typeof knob !== "number" || !Number.isFinite(knob) || knob < 0) {
const e = new Error(`brainCallGuardrailMs must be a positive number of milliseconds, 0, or false (got ${String(knob)})`);
e.code = "config.brain_call_guardrail_invalid";
throw e;
}
if (knob > MAX_TIMER_DELAY_MS) {
const e = new Error(`brainCallGuardrailMs must not exceed ${MAX_TIMER_DELAY_MS}ms (~24.8 days) — a host timer truncates a larger delay and fires immediately, so the backstop would abort every model call (got ${String(knob)}). Pass false or 0 to turn the backstop off.`);
e.code = "config.brain_call_guardrail_invalid";
throw e;
}
return knob;
}
function armCallGuardrail(limitMs, outerSignal, ref) {
const ac = new AbortController();
if (outerSignal !== undefined) {
if (outerSignal.aborted)
ac.abort(outerSignal.reason);
}
const forwardAbort = () => {
ac.abort(outerSignal === undefined ? undefined : outerSignal.reason);
};
if (outerSignal !== undefined && !outerSignal.aborted) {
outerSignal.addEventListener("abort", forwardAbort, { once: true });
}
let expired;
let rejectExpiry = () => undefined;
const expiry = new Promise((_resolve, reject) => {
rejectExpiry = reject;
});
expiry.catch(() => undefined);
const timer = setTimeout(() => {
const err = new BrainCallGuardrailTimeoutError(limitMs);
expired = err;
ref.timedOut ??= err;
ac.abort(err);
rejectExpiry(err);
}, limitMs);
return {
signal: ac.signal,
race: async (p) => {
if (expired !== undefined)
throw expired;
const value = await Promise.race([p, expiry]);
if (expired !== undefined)
throw expired;
return value;
},
disarm: () => {
clearTimeout(timer);
if (outerSignal !== undefined)
outerSignal.removeEventListener("abort", forwardAbort);
},
};
}
export function withBrainCallGuardrail(streamFn, limitMs, ref) {
return async (model, context, options) => {
const gate = armCallGuardrail(limitMs, options?.signal, ref);
let inner;
try {
inner = await gate.race(Promise.resolve(streamFn(model, context, { ...options, signal: gate.signal })));
}
catch (err) {
gate.disarm();
throw err;
}
return {
[Symbol.asyncIterator]() {
const it = inner[Symbol.asyncIterator]();
return {
next: async () => {
let step;
try {
step = await gate.race(Promise.resolve(it.next()));
}
catch (err) {
gate.disarm();
throw err;
}
if (step.done === true)
gate.disarm();
return step;
},
return: async () => {
gate.disarm();
const release = it.return;
if (release !== undefined) {
Promise.resolve(release.call(it)).catch(() => undefined);
}
return { value: undefined, done: true };
},
};
},
result: async () => {
try {
return await gate.race(Promise.resolve(inner.result()));
}
finally {
gate.disarm();
}
},
};
};
}