| import { parseDurationMs } from "../../../cli/parse-duration.js"; |
|
|
| export type ContextPruningToolMatch = { |
| allow?: string[]; |
| deny?: string[]; |
| }; |
| export type ContextPruningMode = "off" | "cache-ttl"; |
|
|
| export type ContextPruningConfig = { |
| mode?: ContextPruningMode; |
| |
| ttl?: string; |
| keepLastAssistants?: number; |
| softTrimRatio?: number; |
| hardClearRatio?: number; |
| minPrunableToolChars?: number; |
| tools?: ContextPruningToolMatch; |
| softTrim?: { |
| maxChars?: number; |
| headChars?: number; |
| tailChars?: number; |
| }; |
| hardClear?: { |
| enabled?: boolean; |
| placeholder?: string; |
| }; |
| }; |
|
|
| export type EffectiveContextPruningSettings = { |
| mode: Exclude<ContextPruningMode, "off">; |
| ttlMs: number; |
| keepLastAssistants: number; |
| softTrimRatio: number; |
| hardClearRatio: number; |
| minPrunableToolChars: number; |
| tools: ContextPruningToolMatch; |
| softTrim: { |
| maxChars: number; |
| headChars: number; |
| tailChars: number; |
| }; |
| hardClear: { |
| enabled: boolean; |
| placeholder: string; |
| }; |
| }; |
|
|
| export const DEFAULT_CONTEXT_PRUNING_SETTINGS: EffectiveContextPruningSettings = { |
| mode: "cache-ttl", |
| ttlMs: 5 * 60 * 1000, |
| keepLastAssistants: 3, |
| softTrimRatio: 0.3, |
| hardClearRatio: 0.5, |
| minPrunableToolChars: 50_000, |
| tools: {}, |
| softTrim: { |
| maxChars: 4_000, |
| headChars: 1_500, |
| tailChars: 1_500, |
| }, |
| hardClear: { |
| enabled: true, |
| placeholder: "[Old tool result content cleared]", |
| }, |
| }; |
|
|
| export function computeEffectiveSettings(raw: unknown): EffectiveContextPruningSettings | null { |
| if (!raw || typeof raw !== "object") { |
| return null; |
| } |
| const cfg = raw as ContextPruningConfig; |
| if (cfg.mode !== "cache-ttl") { |
| return null; |
| } |
|
|
| const s: EffectiveContextPruningSettings = structuredClone(DEFAULT_CONTEXT_PRUNING_SETTINGS); |
| s.mode = cfg.mode; |
|
|
| if (typeof cfg.ttl === "string") { |
| try { |
| s.ttlMs = parseDurationMs(cfg.ttl, { defaultUnit: "m" }); |
| } catch { |
| |
| } |
| } |
|
|
| if (typeof cfg.keepLastAssistants === "number" && Number.isFinite(cfg.keepLastAssistants)) { |
| s.keepLastAssistants = Math.max(0, Math.floor(cfg.keepLastAssistants)); |
| } |
| if (typeof cfg.softTrimRatio === "number" && Number.isFinite(cfg.softTrimRatio)) { |
| s.softTrimRatio = Math.min(1, Math.max(0, cfg.softTrimRatio)); |
| } |
| if (typeof cfg.hardClearRatio === "number" && Number.isFinite(cfg.hardClearRatio)) { |
| s.hardClearRatio = Math.min(1, Math.max(0, cfg.hardClearRatio)); |
| } |
| if (typeof cfg.minPrunableToolChars === "number" && Number.isFinite(cfg.minPrunableToolChars)) { |
| s.minPrunableToolChars = Math.max(0, Math.floor(cfg.minPrunableToolChars)); |
| } |
| if (cfg.tools) { |
| s.tools = cfg.tools; |
| } |
| if (cfg.softTrim) { |
| if (typeof cfg.softTrim.maxChars === "number" && Number.isFinite(cfg.softTrim.maxChars)) { |
| s.softTrim.maxChars = Math.max(0, Math.floor(cfg.softTrim.maxChars)); |
| } |
| if (typeof cfg.softTrim.headChars === "number" && Number.isFinite(cfg.softTrim.headChars)) { |
| s.softTrim.headChars = Math.max(0, Math.floor(cfg.softTrim.headChars)); |
| } |
| if (typeof cfg.softTrim.tailChars === "number" && Number.isFinite(cfg.softTrim.tailChars)) { |
| s.softTrim.tailChars = Math.max(0, Math.floor(cfg.softTrim.tailChars)); |
| } |
| } |
| if (cfg.hardClear) { |
| if (typeof cfg.hardClear.enabled === "boolean") { |
| s.hardClear.enabled = cfg.hardClear.enabled; |
| } |
| if (typeof cfg.hardClear.placeholder === "string" && cfg.hardClear.placeholder.trim()) { |
| s.hardClear.placeholder = cfg.hardClear.placeholder.trim(); |
| } |
| } |
|
|
| return s; |
| } |
|
|