evOo-local/packages/adapters/src/pi-runtime-limits.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

90 lines
2.8 KiB
TypeScript

import { DEFAULT_MODEL_MAX_TOKENS } from "@rakazo/contracts";
/** Hung completions must fail before the typical 5-minute run lease. */
export const MODEL_STREAM_TIMEOUT_MS = 120_000;
/** One retry keeps a transient blip from killing the turn without outliving the lease. */
export const MODEL_STREAM_MAX_RETRIES = 1;
export const TOOL_RESULT_TEXT_LIMIT = 12_000;
export function clipToolResultText(text: string, limit: number = TOOL_RESULT_TEXT_LIMIT): string {
return text.length > limit ? `${text.slice(0, limit)}…` : text;
}
/**
* Share one remaining character budget across all text parts.
* Later text is omitted once the aggregate limit is exhausted; non-text parts stay.
*/
export function clipToolResultContent<T>(
content: T[],
limit: number = TOOL_RESULT_TEXT_LIMIT,
): T[] {
let remaining = limit;
const clipped: T[] = [];
for (const part of content) {
if (
!part ||
typeof part !== "object" ||
!("type" in part) ||
(part as { type?: unknown }).type !== "text" ||
!("text" in part)
) {
clipped.push(part);
continue;
}
if (remaining <= 0) continue;
const text = String((part as { text: unknown }).text);
if (text.length <= remaining) {
clipped.push(part);
remaining -= text.length;
continue;
}
clipped.push({ ...part, text: clipToolResultText(text, remaining) });
remaining = 0;
}
return clipped;
}
/** Prompt tokens providers bill, including cache read/write rather than the uncached remainder. */
export function billedPromptTokens(usage: {
input?: number;
output?: number;
cacheRead?: number;
cacheWrite?: number;
}): { inputTokens: number; outputTokens: number } {
return {
inputTokens:
nonNegativeCount(usage.input) +
nonNegativeCount(usage.cacheRead) +
nonNegativeCount(usage.cacheWrite),
outputTokens: nonNegativeCount(usage.output),
};
}
/**
* Completions default to a modest output cap so OpenRouter-style providers do not
* hold credit for a model card's 128k ceiling. A configured maxTokens is the escape.
*/
export function resolveCompletionMaxTokens(
modelMaxTokens?: number,
configuredMaxTokens?: number,
optionsMaxTokens?: number,
): number {
const userCap =
typeof configuredMaxTokens === "number" && configuredMaxTokens >= 1
? configuredMaxTokens
: DEFAULT_MODEL_MAX_TOKENS;
const optionCap =
typeof optionsMaxTokens === "number" && optionsMaxTokens >= 1
? Math.min(optionsMaxTokens, userCap)
: userCap;
if (typeof modelMaxTokens === "number" && modelMaxTokens >= 1) {
return Math.min(modelMaxTokens, optionCap);
}
return optionCap;
}
function nonNegativeCount(value: number | undefined): number {
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
}