feat: add cache-ttl pruning mode
This commit is contained in:
52
src/agents/pi-embedded-runner/cache-ttl.ts
Normal file
52
src/agents/pi-embedded-runner/cache-ttl.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
type CustomEntryLike = { type?: unknown; customType?: unknown; data?: unknown };
|
||||
|
||||
export const CACHE_TTL_CUSTOM_TYPE = "clawdbot.cache-ttl";
|
||||
|
||||
export type CacheTtlEntryData = {
|
||||
timestamp: number;
|
||||
provider?: string;
|
||||
modelId?: string;
|
||||
};
|
||||
|
||||
export function isCacheTtlEligibleProvider(provider: string, modelId: string): boolean {
|
||||
const normalizedProvider = provider.toLowerCase();
|
||||
const normalizedModelId = modelId.toLowerCase();
|
||||
if (normalizedProvider === "anthropic") return true;
|
||||
if (normalizedProvider === "openrouter" && normalizedModelId.startsWith("anthropic/"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
export function readLastCacheTtlTimestamp(sessionManager: unknown): number | null {
|
||||
const sm = sessionManager as { getEntries?: () => CustomEntryLike[] };
|
||||
if (!sm?.getEntries) return null;
|
||||
try {
|
||||
const entries = sm.getEntries();
|
||||
let last: number | null = null;
|
||||
for (let i = entries.length - 1; i >= 0; i--) {
|
||||
const entry = entries[i];
|
||||
if (entry?.type !== "custom" || entry?.customType !== CACHE_TTL_CUSTOM_TYPE) continue;
|
||||
const data = entry?.data as Partial<CacheTtlEntryData> | undefined;
|
||||
const ts = typeof data?.timestamp === "number" ? data.timestamp : null;
|
||||
if (ts && Number.isFinite(ts)) {
|
||||
last = ts;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function appendCacheTtlTimestamp(sessionManager: unknown, data: CacheTtlEntryData): void {
|
||||
const sm = sessionManager as {
|
||||
appendCustomEntry?: (customType: string, data: unknown) => void;
|
||||
};
|
||||
if (!sm?.appendCustomEntry) return;
|
||||
try {
|
||||
sm.appendCustomEntry(CACHE_TTL_CUSTOM_TYPE, data);
|
||||
} catch {
|
||||
// ignore persistence failures
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { setContextPruningRuntime } from "../pi-extensions/context-pruning/runti
|
||||
import { computeEffectiveSettings } from "../pi-extensions/context-pruning/settings.js";
|
||||
import { makeToolPrunablePredicate } from "../pi-extensions/context-pruning/tools.js";
|
||||
import { ensurePiCompactionReserveTokens } from "../pi-settings.js";
|
||||
import { isCacheTtlEligibleProvider, readLastCacheTtlTimestamp } from "./cache-ttl.js";
|
||||
|
||||
function resolvePiExtensionPath(id: string): string {
|
||||
const self = fileURLToPath(import.meta.url);
|
||||
@@ -43,7 +44,8 @@ function buildContextPruningExtension(params: {
|
||||
model: Model<Api> | undefined;
|
||||
}): { additionalExtensionPaths?: string[] } {
|
||||
const raw = params.cfg?.agents?.defaults?.contextPruning;
|
||||
if (raw?.mode !== "adaptive" && raw?.mode !== "aggressive") return {};
|
||||
if (raw?.mode !== "cache-ttl") return {};
|
||||
if (!isCacheTtlEligibleProvider(params.provider, params.modelId)) return {};
|
||||
|
||||
const settings = computeEffectiveSettings(raw);
|
||||
if (!settings) return {};
|
||||
@@ -52,6 +54,7 @@ function buildContextPruningExtension(params: {
|
||||
settings,
|
||||
contextWindowTokens: resolveContextWindowTokens(params),
|
||||
isToolPrunable: makeToolPrunablePredicate(settings.tools),
|
||||
lastCacheTouchAt: readLastCacheTtlTimestamp(params.sessionManager),
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -21,7 +21,7 @@ export function resolveExtraParams(params: {
|
||||
return modelConfig?.params ? { ...modelConfig.params } : undefined;
|
||||
}
|
||||
|
||||
type CacheControlTtl = "5m" | "1h";
|
||||
type CacheControlTtl = "5m";
|
||||
|
||||
function resolveCacheControlTtl(
|
||||
extraParams: Record<string, unknown> | undefined,
|
||||
@@ -29,7 +29,7 @@ function resolveCacheControlTtl(
|
||||
modelId: string,
|
||||
): CacheControlTtl | undefined {
|
||||
const raw = extraParams?.cacheControlTtl;
|
||||
if (raw !== "5m" && raw !== "1h") return undefined;
|
||||
if (raw !== "5m") return undefined;
|
||||
if (provider === "anthropic") return raw;
|
||||
if (provider === "openrouter" && modelId.startsWith("anthropic/")) return raw;
|
||||
return undefined;
|
||||
|
||||
@@ -36,8 +36,10 @@ describe("injectHistoryImagesIntoMessages", () => {
|
||||
const didMutate = injectHistoryImagesIntoMessages(messages, new Map([[0, [image]]]));
|
||||
|
||||
expect(didMutate).toBe(false);
|
||||
const content = messages[0]?.content as unknown[] | undefined;
|
||||
expect(content).toBeDefined();
|
||||
const content = messages[0]?.content;
|
||||
if (!Array.isArray(content)) {
|
||||
throw new Error("expected array content");
|
||||
}
|
||||
expect(content).toHaveLength(2);
|
||||
});
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ import { resolveDefaultModelForAgent } from "../../model-selection.js";
|
||||
import { isAbortError } from "../abort.js";
|
||||
import { buildEmbeddedExtensionPaths } from "../extensions.js";
|
||||
import { applyExtraParamsToAgent } from "../extra-params.js";
|
||||
import { appendCacheTtlTimestamp, isCacheTtlEligibleProvider } from "../cache-ttl.js";
|
||||
import {
|
||||
logToolSchemasForGoogle,
|
||||
sanitizeSessionHistory,
|
||||
@@ -685,6 +686,17 @@ export async function runEmbeddedAttempt(
|
||||
note: `images: prompt=${imageResult.images.length} history=${imageResult.historyImagesByIndex.size}`,
|
||||
});
|
||||
|
||||
const shouldTrackCacheTtl =
|
||||
params.config?.agents?.defaults?.contextPruning?.mode === "cache-ttl" &&
|
||||
isCacheTtlEligibleProvider(params.provider, params.modelId);
|
||||
if (shouldTrackCacheTtl) {
|
||||
appendCacheTtlTimestamp(sessionManager, {
|
||||
timestamp: Date.now(),
|
||||
provider: params.provider,
|
||||
modelId: params.modelId,
|
||||
});
|
||||
}
|
||||
|
||||
// Only pass images option if there are actually images to pass
|
||||
// This avoids potential issues with models that don't expect the images parameter
|
||||
if (imageResult.images.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user