agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
59 lines • 2.75 kB
JavaScript
/**
* BreakpointCacheStrategy — for a provider that caches a prefix only where
* the request marks it (`promptCaching.mode: 'breakpoints'`; Anthropic's
* `cache_control`).
*
* Chosen by CAPABILITY (`cacheStrategyFor`), never by provider name: built
* from the provider's own declaration, so `withRetry(anthropic())` or an
* application's routing wrapper caches exactly like the adapter it wraps.
*
* What it DOES:
* - clamp the candidate markers to the declared `maxBreakpoints`, keeping
* the first N in slot order (the most stable prefixes first);
* - put them on `LLMRequest.cacheMarkers`, where the adapter reads them;
* - read cache metrics off the PORT usage the adapter normalised —
* `not applicable` when the declaration says the adapter reports none.
* What it does NOT: touch the wire. The adapter owns `cache_control`
* (`adapters/llm/anthropicCacheWire.ts`), and content is never edited.
*/
import { notApplicable } from '../../lib/claim/claim.js';
import { readPortCacheUsage } from '../portUsage.js';
export class BreakpointCacheStrategy {
name = 'breakpoints';
maxBreakpoints;
reportsUsage;
who;
fields;
/**
* @param declared the provider's `promptCaching` declaration
* @param who names the adapter in the meter's evidence sentences,
* e.g. `"the 'anthropic' provider"`
*/
constructor(declared, who = 'the provider') {
this.maxBreakpoints = declared.maxBreakpoints;
this.reportsUsage = declared.reportsUsage;
this.who = who;
this.fields = declared.fields;
}
async prepareRequest(req, candidates, ctx) {
// The agent-side kill switch, honoured here too: the CacheGate should
// already have emptied the candidates, but a buggy gate must not be able
// to put markers on a request whose run said caching is off.
if (ctx.cachingDisabled || candidates.length === 0) {
return { request: req, markersApplied: [] };
}
const supported = this.fields === undefined
? candidates
: candidates.filter((marker) => this.fields?.includes(marker.field));
const markersApplied = supported.length <= this.maxBreakpoints ? supported : supported.slice(0, this.maxBreakpoints);
return { request: { ...req, cacheMarkers: markersApplied }, markersApplied };
}
extractMetrics(usage) {
if (!this.reportsUsage) {
return notApplicable(`${this.who} declares that it does not report cache token counts ` +
'(promptCaching.reportsUsage: false)');
}
return readPortCacheUsage(usage, this.who);
}
}
//# sourceMappingURL=BreakpointCacheStrategy.js.map