agentfootprint 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/llm/BrowserAnthropicProvider.js +68 -0
- package/dist/adapters/llm/BrowserAnthropicProvider.js.map +1 -1
- package/dist/cache/CacheDecisionSubflow.js +172 -0
- package/dist/cache/CacheDecisionSubflow.js.map +1 -0
- package/dist/cache/CacheGateDecider.js +122 -0
- package/dist/cache/CacheGateDecider.js.map +1 -0
- package/dist/cache/applyCachePolicy.js +55 -0
- package/dist/cache/applyCachePolicy.js.map +1 -0
- package/dist/cache/cacheRecorder.js +120 -0
- package/dist/cache/cacheRecorder.js.map +1 -0
- package/dist/cache/index.js +47 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/strategies/AnthropicCacheStrategy.js +102 -0
- package/dist/cache/strategies/AnthropicCacheStrategy.js.map +1 -0
- package/dist/cache/strategies/BedrockCacheStrategy.js +81 -0
- package/dist/cache/strategies/BedrockCacheStrategy.js.map +1 -0
- package/dist/cache/strategies/NoOpCacheStrategy.js +40 -0
- package/dist/cache/strategies/NoOpCacheStrategy.js.map +1 -0
- package/dist/cache/strategies/OpenAICacheStrategy.js +75 -0
- package/dist/cache/strategies/OpenAICacheStrategy.js.map +1 -0
- package/dist/cache/strategyRegistry.js +80 -0
- package/dist/cache/strategyRegistry.js.map +1 -0
- package/dist/cache/types.js +25 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/conventions.js +18 -0
- package/dist/conventions.js.map +1 -1
- package/dist/core/Agent.js +157 -4
- package/dist/core/Agent.js.map +1 -1
- package/dist/esm/adapters/llm/BrowserAnthropicProvider.js +68 -0
- package/dist/esm/adapters/llm/BrowserAnthropicProvider.js.map +1 -1
- package/dist/esm/cache/CacheDecisionSubflow.js +166 -0
- package/dist/esm/cache/CacheDecisionSubflow.js.map +1 -0
- package/dist/esm/cache/CacheGateDecider.js +116 -0
- package/dist/esm/cache/CacheGateDecider.js.map +1 -0
- package/dist/esm/cache/applyCachePolicy.js +50 -0
- package/dist/esm/cache/applyCachePolicy.js.map +1 -0
- package/dist/esm/cache/cacheRecorder.js +116 -0
- package/dist/esm/cache/cacheRecorder.js.map +1 -0
- package/dist/esm/cache/index.js +36 -0
- package/dist/esm/cache/index.js.map +1 -0
- package/dist/esm/cache/strategies/AnthropicCacheStrategy.js +98 -0
- package/dist/esm/cache/strategies/AnthropicCacheStrategy.js.map +1 -0
- package/dist/esm/cache/strategies/BedrockCacheStrategy.js +77 -0
- package/dist/esm/cache/strategies/BedrockCacheStrategy.js.map +1 -0
- package/dist/esm/cache/strategies/NoOpCacheStrategy.js +36 -0
- package/dist/esm/cache/strategies/NoOpCacheStrategy.js.map +1 -0
- package/dist/esm/cache/strategies/OpenAICacheStrategy.js +71 -0
- package/dist/esm/cache/strategies/OpenAICacheStrategy.js.map +1 -0
- package/dist/esm/cache/strategyRegistry.js +73 -0
- package/dist/esm/cache/strategyRegistry.js.map +1 -0
- package/dist/esm/cache/types.js +24 -0
- package/dist/esm/cache/types.js.map +1 -0
- package/dist/esm/conventions.js +18 -0
- package/dist/esm/conventions.js.map +1 -1
- package/dist/esm/core/Agent.js +157 -4
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/index.js +9 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/injection-engine/factories/defineFact.js +3 -0
- package/dist/esm/lib/injection-engine/factories/defineFact.js.map +1 -1
- package/dist/esm/lib/injection-engine/factories/defineInstruction.js +3 -0
- package/dist/esm/lib/injection-engine/factories/defineInstruction.js.map +1 -1
- package/dist/esm/lib/injection-engine/factories/defineSkill.js +5 -0
- package/dist/esm/lib/injection-engine/factories/defineSkill.js.map +1 -1
- package/dist/esm/lib/injection-engine/factories/defineSteering.js +3 -0
- package/dist/esm/lib/injection-engine/factories/defineSteering.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/injection-engine/factories/defineFact.js +3 -0
- package/dist/lib/injection-engine/factories/defineFact.js.map +1 -1
- package/dist/lib/injection-engine/factories/defineInstruction.js +3 -0
- package/dist/lib/injection-engine/factories/defineInstruction.js.map +1 -1
- package/dist/lib/injection-engine/factories/defineSkill.js +5 -0
- package/dist/lib/injection-engine/factories/defineSkill.js.map +1 -1
- package/dist/lib/injection-engine/factories/defineSteering.js +3 -0
- package/dist/lib/injection-engine/factories/defineSteering.js.map +1 -1
- package/dist/types/adapters/types.d.ts +11 -0
- package/dist/types/adapters/types.d.ts.map +1 -1
- package/dist/types/cache/CacheDecisionSubflow.d.ts +89 -0
- package/dist/types/cache/CacheDecisionSubflow.d.ts.map +1 -0
- package/dist/types/cache/CacheGateDecider.d.ts +114 -0
- package/dist/types/cache/CacheGateDecider.d.ts.map +1 -0
- package/dist/types/cache/applyCachePolicy.d.ts +38 -0
- package/dist/types/cache/applyCachePolicy.d.ts.map +1 -0
- package/dist/types/cache/cacheRecorder.d.ts +86 -0
- package/dist/types/cache/cacheRecorder.d.ts.map +1 -0
- package/dist/types/cache/index.d.ts +34 -0
- package/dist/types/cache/index.d.ts.map +1 -0
- package/dist/types/cache/strategies/AnthropicCacheStrategy.d.ts +39 -0
- package/dist/types/cache/strategies/AnthropicCacheStrategy.d.ts.map +1 -0
- package/dist/types/cache/strategies/BedrockCacheStrategy.d.ts +34 -0
- package/dist/types/cache/strategies/BedrockCacheStrategy.d.ts.map +1 -0
- package/dist/types/cache/strategies/NoOpCacheStrategy.d.ts +30 -0
- package/dist/types/cache/strategies/NoOpCacheStrategy.d.ts.map +1 -0
- package/dist/types/cache/strategies/OpenAICacheStrategy.d.ts +37 -0
- package/dist/types/cache/strategies/OpenAICacheStrategy.d.ts.map +1 -0
- package/dist/types/cache/strategyRegistry.d.ts +46 -0
- package/dist/types/cache/strategyRegistry.d.ts.map +1 -0
- package/dist/types/cache/types.d.ts +244 -0
- package/dist/types/cache/types.d.ts.map +1 -0
- package/dist/types/conventions.d.ts +18 -0
- package/dist/types/conventions.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts +86 -2
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/factories/defineFact.d.ts +9 -0
- package/dist/types/lib/injection-engine/factories/defineFact.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/factories/defineInstruction.d.ts +11 -0
- package/dist/types/lib/injection-engine/factories/defineInstruction.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/factories/defineSkill.d.ts +15 -0
- package/dist/types/lib/injection-engine/factories/defineSkill.d.ts.map +1 -1
- package/dist/types/lib/injection-engine/factories/defineSteering.d.ts +12 -0
- package/dist/types/lib/injection-engine/factories/defineSteering.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BedrockCacheStrategy — model-aware strategy for AWS Bedrock.
|
|
3
|
+
*
|
|
4
|
+
* Bedrock hosts multiple model families. Cache support varies:
|
|
5
|
+
* - Claude on Bedrock → identical mechanics to direct Anthropic
|
|
6
|
+
* (`cache_control: { type: 'ephemeral' }` markers, 4-marker
|
|
7
|
+
* limit). Strategy delegates to Anthropic-shaped behavior.
|
|
8
|
+
* - Llama / Mistral / Cohere on Bedrock → no cache support today
|
|
9
|
+
* (as of 2026-04-30). Strategy passes through, returns no metrics.
|
|
10
|
+
*
|
|
11
|
+
* Auto-detection: inspects `req.model` to decide. Claude model IDs
|
|
12
|
+
* start with `'anthropic.claude'` on Bedrock (e.g.,
|
|
13
|
+
* `anthropic.claude-3-5-sonnet-20240620-v1:0`).
|
|
14
|
+
*
|
|
15
|
+
* Auto-registers under provider name `'bedrock'`.
|
|
16
|
+
*
|
|
17
|
+
* Per the Phase 1 review (Reviewer 6 — Provider SDK expert): for
|
|
18
|
+
* non-Claude Bedrock models the strategy reports `enabled: false` in
|
|
19
|
+
* its capabilities so the CacheDecision subflow can short-circuit
|
|
20
|
+
* marker emission (potential v2.7 optimization). Today markers still
|
|
21
|
+
* emit and we drop them silently in prepareRequest.
|
|
22
|
+
*/
|
|
23
|
+
import { registerCacheStrategy } from '../strategyRegistry.js';
|
|
24
|
+
/** Match Bedrock-Claude model ids: `anthropic.claude-...` */
|
|
25
|
+
const BEDROCK_CLAUDE_RE = /^anthropic\.claude/i;
|
|
26
|
+
/** Anthropic's 4-marker limit applies to Bedrock-Claude too. */
|
|
27
|
+
const BEDROCK_MAX_MARKERS = 4;
|
|
28
|
+
const BEDROCK_CAPABILITIES = Object.freeze({
|
|
29
|
+
// We say `enabled: true` at the capability level because Bedrock-
|
|
30
|
+
// Claude DOES support caching. Bedrock-Llama/Mistral land in the
|
|
31
|
+
// model-aware code path inside prepareRequest (no markers applied).
|
|
32
|
+
enabled: true,
|
|
33
|
+
maxMarkers: BEDROCK_MAX_MARKERS,
|
|
34
|
+
ttls: ['short', 'long'],
|
|
35
|
+
fields: ['system', 'tools', 'messages'],
|
|
36
|
+
automatic: false,
|
|
37
|
+
});
|
|
38
|
+
export class BedrockCacheStrategy {
|
|
39
|
+
providerName = 'bedrock';
|
|
40
|
+
capabilities = BEDROCK_CAPABILITIES;
|
|
41
|
+
async prepareRequest(req, candidates, ctx) {
|
|
42
|
+
if (ctx.cachingDisabled || candidates.length === 0) {
|
|
43
|
+
return { request: req, markersApplied: [] };
|
|
44
|
+
}
|
|
45
|
+
// Model-aware: only Claude on Bedrock supports cache_control.
|
|
46
|
+
// Other model families silently drop the markers.
|
|
47
|
+
if (!BEDROCK_CLAUDE_RE.test(req.model)) {
|
|
48
|
+
return { request: req, markersApplied: [] };
|
|
49
|
+
}
|
|
50
|
+
const markersApplied = candidates.length <= BEDROCK_MAX_MARKERS
|
|
51
|
+
? candidates
|
|
52
|
+
: candidates.slice(0, BEDROCK_MAX_MARKERS);
|
|
53
|
+
return {
|
|
54
|
+
request: { ...req, cacheMarkers: markersApplied },
|
|
55
|
+
markersApplied,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
extractMetrics(usage) {
|
|
59
|
+
// Bedrock returns the SAME usage shape as Anthropic for Claude
|
|
60
|
+
// models — same cache_creation_input_tokens / cache_read_input_tokens
|
|
61
|
+
// fields. Reuse identical extraction.
|
|
62
|
+
if (!usage || typeof usage !== 'object')
|
|
63
|
+
return undefined;
|
|
64
|
+
const u = usage;
|
|
65
|
+
const cacheRead = u.cache_read_input_tokens ?? 0;
|
|
66
|
+
const cacheWrite = u.cache_creation_input_tokens ?? 0;
|
|
67
|
+
if (cacheRead === 0 && cacheWrite === 0)
|
|
68
|
+
return undefined;
|
|
69
|
+
return {
|
|
70
|
+
cacheReadTokens: cacheRead,
|
|
71
|
+
cacheWriteTokens: cacheWrite,
|
|
72
|
+
freshInputTokens: u.input_tokens ?? 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
registerCacheStrategy(new BedrockCacheStrategy());
|
|
77
|
+
//# sourceMappingURL=BedrockCacheStrategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BedrockCacheStrategy.js","sourceRoot":"","sources":["../../../../src/cache/strategies/BedrockCacheStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAUH,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,6DAA6D;AAC7D,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAEhD,gEAAgE;AAChE,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B,MAAM,oBAAoB,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC5D,kEAAkE;IAClE,iEAAiE;IACjE,oEAAoE;IACpE,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,mBAAmB;IAC/B,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,CAAkC;IACxD,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAiD;IACvF,SAAS,EAAE,KAAK;CACjB,CAAC,CAAC;AAEH,MAAM,OAAO,oBAAoB;IACtB,YAAY,GAAG,SAAS,CAAC;IACzB,YAAY,GAAG,oBAAoB,CAAC;IAE7C,KAAK,CAAC,cAAc,CAClB,GAAe,EACf,UAAkC,EAClC,GAAyB;QAKzB,IAAI,GAAG,CAAC,eAAe,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;QAC9C,CAAC;QACD,8DAA8D;QAC9D,kDAAkD;QAClD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;QAC9C,CAAC;QACD,MAAM,cAAc,GAClB,UAAU,CAAC,MAAM,IAAI,mBAAmB;YACtC,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAC/C,OAAO;YACL,OAAO,EAAE,EAAE,GAAG,GAAG,EAAE,YAAY,EAAE,cAAc,EAAE;YACjD,cAAc;SACf,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,KAAc;QAC3B,+DAA+D;QAC/D,sEAAsE;QACtE,sCAAsC;QACtC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,CAAC,GAAG,KAIT,CAAC;QACF,MAAM,SAAS,GAAG,CAAC,CAAC,uBAAuB,IAAI,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,CAAC,CAAC,2BAA2B,IAAI,CAAC,CAAC;QACtD,IAAI,SAAS,KAAK,CAAC,IAAI,UAAU,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAC1D,OAAO;YACL,eAAe,EAAE,SAAS;YAC1B,gBAAgB,EAAE,UAAU;YAC5B,gBAAgB,EAAE,CAAC,CAAC,YAAY,IAAI,CAAC;SACtC,CAAC;IACJ,CAAC;CACF;AAED,qBAAqB,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NoOpCacheStrategy — fallback strategy for providers without cache
|
|
3
|
+
* support (Mock, unknown providers, intentional opt-out).
|
|
4
|
+
*
|
|
5
|
+
* Returns the request unchanged; reports no metrics. The
|
|
6
|
+
* `capabilities.enabled` flag is `false` so the CacheDecision subflow
|
|
7
|
+
* could choose to skip emitting markers entirely (potential v2.7
|
|
8
|
+
* optimization), though current Phase 4+5 always emit markers and
|
|
9
|
+
* let the strategy decide what to do with them.
|
|
10
|
+
*
|
|
11
|
+
* Always-available default. Registered against the special wildcard
|
|
12
|
+
* `'*'` so any unrecognized provider name falls back to NoOp.
|
|
13
|
+
*/
|
|
14
|
+
const NOOP_CAPABILITIES = Object.freeze({
|
|
15
|
+
enabled: false,
|
|
16
|
+
maxMarkers: 0,
|
|
17
|
+
ttls: [],
|
|
18
|
+
fields: [],
|
|
19
|
+
automatic: false,
|
|
20
|
+
});
|
|
21
|
+
export class NoOpCacheStrategy {
|
|
22
|
+
/**
|
|
23
|
+
* Wildcard provider name. The strategy registry treats this as the
|
|
24
|
+
* fallback for any provider that doesn't have a specific strategy
|
|
25
|
+
* registered.
|
|
26
|
+
*/
|
|
27
|
+
providerName = '*';
|
|
28
|
+
capabilities = NOOP_CAPABILITIES;
|
|
29
|
+
async prepareRequest(req, _candidates, _ctx) {
|
|
30
|
+
return { request: req, markersApplied: [] };
|
|
31
|
+
}
|
|
32
|
+
extractMetrics(_usage) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=NoOpCacheStrategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NoOpCacheStrategy.js","sourceRoot":"","sources":["../../../../src/cache/strategies/NoOpCacheStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH,MAAM,iBAAiB,GAAsB,MAAM,CAAC,MAAM,CAAC;IACzD,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,CAAC;IACb,IAAI,EAAE,EAAmC;IACzC,MAAM,EAAE,EAAkD;IAC1D,SAAS,EAAE,KAAK;CACjB,CAAC,CAAC;AAEH,MAAM,OAAO,iBAAiB;IAC5B;;;;OAIG;IACM,YAAY,GAAG,GAAG,CAAC;IACnB,YAAY,GAAG,iBAAiB,CAAC;IAE1C,KAAK,CAAC,cAAc,CAClB,GAAe,EACf,WAAmC,EACnC,IAA0B;QAK1B,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAC9C,CAAC;IAED,cAAc,CAAC,MAAe;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAICacheStrategy — metrics-only strategy for OpenAI providers.
|
|
3
|
+
*
|
|
4
|
+
* OpenAI auto-caches request prefixes ≥1024 tokens at 50% off.
|
|
5
|
+
* No client-side opt-in markers needed (and no way to influence
|
|
6
|
+
* cache behavior from the client). The strategy:
|
|
7
|
+
*
|
|
8
|
+
* - **prepareRequest**: pass-through. We can't tell OpenAI what
|
|
9
|
+
* to cache; they decide automatically. Markers are silently
|
|
10
|
+
* dropped (the 80% case for OpenAI consumers is "I declared
|
|
11
|
+
* cache: 'always' for my injections" — that's still meaningful
|
|
12
|
+
* because (a) it's portable across providers, (b) for OpenAI
|
|
13
|
+
* the auto-cache may still hit on stable prefixes regardless).
|
|
14
|
+
* - **extractMetrics**: reads `prompt_tokens_details.cached_tokens`
|
|
15
|
+
* from OpenAI's usage response so cacheRecorder can surface
|
|
16
|
+
* hit rates / dollar savings.
|
|
17
|
+
*
|
|
18
|
+
* Auto-registers on module import for: 'openai', 'browser-openai'.
|
|
19
|
+
*
|
|
20
|
+
* Documentation note for consumers (Phase 12 docs): the `cache:`
|
|
21
|
+
* directive on injection definitions is portable but has NO LOCAL
|
|
22
|
+
* EFFECT on OpenAI runs — the provider auto-caches based on prefix
|
|
23
|
+
* length. The directive still ships correctly with the agent and
|
|
24
|
+
* lights up automatically when you swap to Anthropic / Bedrock.
|
|
25
|
+
*/
|
|
26
|
+
import { registerCacheStrategy } from '../strategyRegistry.js';
|
|
27
|
+
const OPENAI_CAPABILITIES = Object.freeze({
|
|
28
|
+
// `enabled: true` because metrics ARE extracted (cacheRecorder shows
|
|
29
|
+
// hit rates). The `automatic: true` flag tells consumers the markers
|
|
30
|
+
// are inert here — OpenAI decides what to cache, not us.
|
|
31
|
+
enabled: true,
|
|
32
|
+
maxMarkers: 0,
|
|
33
|
+
ttls: ['short'],
|
|
34
|
+
fields: [],
|
|
35
|
+
automatic: true,
|
|
36
|
+
});
|
|
37
|
+
export class OpenAICacheStrategy {
|
|
38
|
+
providerName = 'openai';
|
|
39
|
+
capabilities = OPENAI_CAPABILITIES;
|
|
40
|
+
async prepareRequest(req, _candidates, _ctx) {
|
|
41
|
+
// Pass-through. OpenAI auto-caches; no opt-in needed.
|
|
42
|
+
return { request: req, markersApplied: [] };
|
|
43
|
+
}
|
|
44
|
+
extractMetrics(usage) {
|
|
45
|
+
if (!usage || typeof usage !== 'object')
|
|
46
|
+
return undefined;
|
|
47
|
+
const u = usage;
|
|
48
|
+
const cached = u.prompt_tokens_details?.cached_tokens ?? 0;
|
|
49
|
+
if (cached === 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
const totalPrompt = u.prompt_tokens ?? cached;
|
|
52
|
+
return {
|
|
53
|
+
cacheReadTokens: cached,
|
|
54
|
+
cacheWriteTokens: 0, // OpenAI doesn't charge a write premium
|
|
55
|
+
freshInputTokens: Math.max(0, totalPrompt - cached),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Auto-register for both server-side and browser variants.
|
|
60
|
+
{
|
|
61
|
+
const strategy = new OpenAICacheStrategy();
|
|
62
|
+
registerCacheStrategy(strategy);
|
|
63
|
+
const browserStrategy = {
|
|
64
|
+
providerName: 'browser-openai',
|
|
65
|
+
capabilities: strategy.capabilities,
|
|
66
|
+
prepareRequest: strategy.prepareRequest.bind(strategy),
|
|
67
|
+
extractMetrics: strategy.extractMetrics.bind(strategy),
|
|
68
|
+
};
|
|
69
|
+
registerCacheStrategy(browserStrategy);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=OpenAICacheStrategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAICacheStrategy.js","sourceRoot":"","sources":["../../../../src/cache/strategies/OpenAICacheStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAUH,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,mBAAmB,GAAsB,MAAM,CAAC,MAAM,CAAC;IAC3D,qEAAqE;IACrE,qEAAqE;IACrE,yDAAyD;IACzD,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,CAAC;IACb,IAAI,EAAE,CAAC,OAAO,CAAkC;IAChD,MAAM,EAAE,EAAkD;IAC1D,SAAS,EAAE,IAAI;CAChB,CAAC,CAAC;AAEH,MAAM,OAAO,mBAAmB;IACrB,YAAY,GAAG,QAAQ,CAAC;IACxB,YAAY,GAAG,mBAAmB,CAAC;IAE5C,KAAK,CAAC,cAAc,CAClB,GAAe,EACf,WAAmC,EACnC,IAA0B;QAK1B,sDAAsD;QACtD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC;IAC9C,CAAC;IAED,cAAc,CAAC,KAAc;QAC3B,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAC1D,MAAM,CAAC,GAAG,KAGT,CAAC;QACF,MAAM,MAAM,GAAG,CAAC,CAAC,qBAAqB,EAAE,aAAa,IAAI,CAAC,CAAC;QAC3D,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACnC,MAAM,WAAW,GAAG,CAAC,CAAC,aAAa,IAAI,MAAM,CAAC;QAC9C,OAAO;YACL,eAAe,EAAE,MAAM;YACvB,gBAAgB,EAAE,CAAC,EAAE,wCAAwC;YAC7D,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;SACpD,CAAC;IACJ,CAAC;CACF;AAED,2DAA2D;AAC3D,CAAC;IACC,MAAM,QAAQ,GAAG,IAAI,mBAAmB,EAAE,CAAC;IAC3C,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,eAAe,GAAkB;QACrC,YAAY,EAAE,gBAAgB;QAC9B,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;QACtD,cAAc,EAAE,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;KACvD,CAAC;IACF,qBAAqB,CAAC,eAAe,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy registry — maps provider name → CacheStrategy.
|
|
3
|
+
*
|
|
4
|
+
* Auto-resolution at agent build time: agentfootprint inspects
|
|
5
|
+
* `provider.name` and looks up the registered strategy for that
|
|
6
|
+
* name. Falls back to `NoOpCacheStrategy` (registered under wildcard
|
|
7
|
+
* `'*'`) when the provider isn't recognized.
|
|
8
|
+
*
|
|
9
|
+
* Phases shipping registered strategies:
|
|
10
|
+
* - v2.6 Phase 6 (this phase): NoOp
|
|
11
|
+
* - v2.6 Phase 7: AnthropicCacheStrategy ('anthropic',
|
|
12
|
+
* 'browser-anthropic')
|
|
13
|
+
* - v2.6 Phase 8: OpenAICacheStrategy ('openai', 'browser-openai'),
|
|
14
|
+
* BedrockCacheStrategy ('bedrock')
|
|
15
|
+
* - v2.7+ : GeminiCacheStrategy (handle-based, async, deferred)
|
|
16
|
+
*
|
|
17
|
+
* Consumers can register their own strategy via
|
|
18
|
+
* `registerCacheStrategy(strategy)`. Useful for in-house LLM proxies
|
|
19
|
+
* or test mocks.
|
|
20
|
+
*/
|
|
21
|
+
import { NoOpCacheStrategy } from './strategies/NoOpCacheStrategy.js';
|
|
22
|
+
/**
|
|
23
|
+
* Registry singleton. Populated by individual strategy modules
|
|
24
|
+
* importing this and calling `registerCacheStrategy` at module load
|
|
25
|
+
* time, OR by the consumer at agent build time.
|
|
26
|
+
*
|
|
27
|
+
* Contains the wildcard `'*'` → NoOp entry by default; never empty.
|
|
28
|
+
*/
|
|
29
|
+
const REGISTRY = new Map([['*', new NoOpCacheStrategy()]]);
|
|
30
|
+
/**
|
|
31
|
+
* Look up a CacheStrategy by provider name. Falls back to the
|
|
32
|
+
* wildcard NoOp strategy if no match.
|
|
33
|
+
*
|
|
34
|
+
* Lookup is case-insensitive on the provider name.
|
|
35
|
+
*/
|
|
36
|
+
export function getDefaultCacheStrategy(providerName) {
|
|
37
|
+
const exact = REGISTRY.get(providerName);
|
|
38
|
+
if (exact !== undefined)
|
|
39
|
+
return exact;
|
|
40
|
+
const lower = providerName.toLowerCase();
|
|
41
|
+
if (lower !== providerName) {
|
|
42
|
+
const lowercased = REGISTRY.get(lower);
|
|
43
|
+
if (lowercased !== undefined)
|
|
44
|
+
return lowercased;
|
|
45
|
+
}
|
|
46
|
+
// Fallback wildcard always present
|
|
47
|
+
return REGISTRY.get('*');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Register (or replace) a strategy for a provider name. Called by
|
|
51
|
+
* strategy modules (v2.6 Phase 7+) at module load OR by consumers
|
|
52
|
+
* needing a custom backend. Replacing an existing strategy is allowed
|
|
53
|
+
* — the most-recent registration wins.
|
|
54
|
+
*/
|
|
55
|
+
export function registerCacheStrategy(strategy) {
|
|
56
|
+
REGISTRY.set(strategy.providerName, strategy);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Read-only view of registered strategy names. Useful for diagnostics
|
|
60
|
+
* (e.g., logging "we have strategies for: anthropic, openai, *").
|
|
61
|
+
*/
|
|
62
|
+
export function listRegisteredStrategies() {
|
|
63
|
+
return [...REGISTRY.keys()];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Internal helper for tests: reset the registry to the default
|
|
67
|
+
* (wildcard → NoOp only). Not exported from the public barrel.
|
|
68
|
+
*/
|
|
69
|
+
export function _resetRegistryForTests() {
|
|
70
|
+
REGISTRY.clear();
|
|
71
|
+
REGISTRY.set('*', new NoOpCacheStrategy());
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=strategyRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategyRegistry.js","sourceRoot":"","sources":["../../../src/cache/strategyRegistry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAwB,CAAC,CAAC,GAAG,EAAE,IAAI,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;AAElF;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,YAAoB;IAC1D,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,KAAK,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IACzC,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;QAC3B,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,UAAU,CAAC;IAClD,CAAC;IACD,mCAAmC;IACnC,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;AAC5B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAuB;IAC3D,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB;IACpC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjB,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,iBAAiB,EAAE,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache layer — public types.
|
|
3
|
+
*
|
|
4
|
+
* Three layers, each with one responsibility:
|
|
5
|
+
*
|
|
6
|
+
* 1. CONSUMER DSL — `CachePolicy` field on every injection factory.
|
|
7
|
+
* Declarative, like GraphQL schema input. Says WHAT should be
|
|
8
|
+
* cacheable. Examples: `cache: 'always'`, `cache: 'while-active'`.
|
|
9
|
+
*
|
|
10
|
+
* 2. AGNOSTIC MARKERS — `CacheMarker[]` produced by the
|
|
11
|
+
* `CacheDecision` subflow at runtime. Provider-independent
|
|
12
|
+
* identification of "cacheable prefix in field X up to index Y".
|
|
13
|
+
*
|
|
14
|
+
* 3. PROVIDER STRATEGY — one `CacheStrategy` implementation per
|
|
15
|
+
* provider (Anthropic / OpenAI / Bedrock / NoOp). Translates
|
|
16
|
+
* agnostic markers to provider-specific wire format AND extracts
|
|
17
|
+
* cache metrics from the provider's response.
|
|
18
|
+
*
|
|
19
|
+
* The interfaces are read-only / immutable by convention. Strategies
|
|
20
|
+
* MUST be stateless across runs; per-run state lives in the
|
|
21
|
+
* `CacheStrategyContext` passed into `prepareRequest`.
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/cache/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
|
package/dist/esm/conventions.js
CHANGED
|
@@ -28,6 +28,9 @@ export const SUBFLOW_IDS = {
|
|
|
28
28
|
MERGE: 'sf-merge',
|
|
29
29
|
/** Final-answer composition inside Agent. */
|
|
30
30
|
FINAL: 'sf-final',
|
|
31
|
+
/** Cache decision subflow (v2.6). Walks activeInjections, emits
|
|
32
|
+
* agnostic CacheMarker[]. Provider-independent. */
|
|
33
|
+
CACHE_DECISION: 'sf-cache-decision',
|
|
31
34
|
};
|
|
32
35
|
/** Stage IDs — plain function stages that builders mount. */
|
|
33
36
|
export const STAGE_IDS = {
|
|
@@ -37,6 +40,21 @@ export const STAGE_IDS = {
|
|
|
37
40
|
FORMAT_MERGE: 'format-merge',
|
|
38
41
|
MERGE_LLM: 'merge-llm',
|
|
39
42
|
EXTRACT_MERGE: 'extract-merge',
|
|
43
|
+
/** Updates the rolling skill-history window before CacheGate
|
|
44
|
+
* evaluates skill-churn (v2.6). */
|
|
45
|
+
UPDATE_SKILL_HISTORY: 'update-skill-history',
|
|
46
|
+
/** CacheGate decider stage — routes to apply-markers / no-markers
|
|
47
|
+
* based on kill switch / hit rate / skill churn (v2.6). */
|
|
48
|
+
CACHE_GATE: 'cache-gate',
|
|
49
|
+
/** CacheGate branch (routing key) when markers SHOULD be applied
|
|
50
|
+
* this iteration. Pass-through stage; markers stay in scope. (v2.6) */
|
|
51
|
+
APPLY_MARKERS: 'apply-markers',
|
|
52
|
+
/** CacheGate branch (routing key) when markers should be SKIPPED
|
|
53
|
+
* this iteration. Stage clears scope.cacheMarkers. (v2.6) */
|
|
54
|
+
SKIP_CACHING: 'no-markers',
|
|
55
|
+
/** BuildLLMRequest stage — calls strategy.prepareRequest to apply
|
|
56
|
+
* markers to the wire request (v2.6). */
|
|
57
|
+
BUILD_LLM_REQUEST: 'build-llm-request',
|
|
40
58
|
};
|
|
41
59
|
// ─── Type guards ─────────────────────────────────────────────────────
|
|
42
60
|
/** True when a subflow id corresponds to one of the 3 context slots. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/conventions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,gEAAgE;AAChE,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB;0EACsE;IACtE,gBAAgB,EAAE,qBAAqB;IACvC,+DAA+D;IAC/D,aAAa,EAAE,kBAAkB;IACjC,6BAA6B;IAC7B,QAAQ,EAAE,aAAa;IACvB,0BAA0B;IAC1B,KAAK,EAAE,UAAU;IACjB,2CAA2C;IAC3C,KAAK,EAAE,UAAU;IACjB,uDAAuD;IACvD,UAAU,EAAE,eAAe;IAC3B,kCAAkC;IAClC,KAAK,EAAE,UAAU;IACjB,6CAA6C;IAC7C,KAAK,EAAE,UAAU;
|
|
1
|
+
{"version":3,"file":"conventions.js","sourceRoot":"","sources":["../../src/conventions.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/core/Agent.js
CHANGED
|
@@ -21,6 +21,9 @@ import { FlowChartExecutor, flowChart, } from 'footprintjs';
|
|
|
21
21
|
// with it (default behavior re-introduces duplicate tool names that
|
|
22
22
|
// LLM providers reject).
|
|
23
23
|
import { ArrayMergeMode } from 'footprintjs/advanced';
|
|
24
|
+
import { cacheDecisionSubflow } from '../cache/CacheDecisionSubflow.js';
|
|
25
|
+
import { cacheGateDecide, updateSkillHistory as updateSkillHistoryStage, } from '../cache/CacheGateDecider.js';
|
|
26
|
+
import { getDefaultCacheStrategy } from '../cache/strategyRegistry.js';
|
|
24
27
|
import { isPauseRequest } from './pause.js';
|
|
25
28
|
import { emitCostTick } from './cost.js';
|
|
26
29
|
import { STAGE_IDS, SUBFLOW_IDS } from '../conventions.js';
|
|
@@ -55,6 +58,27 @@ export class Agent extends RunnerBase {
|
|
|
55
58
|
maxTokens;
|
|
56
59
|
maxIterations;
|
|
57
60
|
systemPromptValue;
|
|
61
|
+
/**
|
|
62
|
+
* Cache policy for the base system prompt (set via
|
|
63
|
+
* `.system(text, { cache })`). Default `'always'` — base prompt is
|
|
64
|
+
* stable per-turn, ideal cache anchor. CacheDecision subflow reads
|
|
65
|
+
* this when computing the SystemPrompt slot's cache markers.
|
|
66
|
+
*/
|
|
67
|
+
systemPromptCachePolicy;
|
|
68
|
+
/**
|
|
69
|
+
* Global cache kill switch from `Agent.create({ caching: 'off' })`.
|
|
70
|
+
* Threaded into agent scope at seed-time as `scope.cachingDisabled`;
|
|
71
|
+
* read by the CacheGate decider every iteration (highest-priority rule).
|
|
72
|
+
*/
|
|
73
|
+
cachingDisabledValue;
|
|
74
|
+
/**
|
|
75
|
+
* Provider-specific CacheStrategy. Auto-resolved from
|
|
76
|
+
* `getDefaultCacheStrategy(provider.name)` at agent build time
|
|
77
|
+
* unless the consumer explicitly passes one via builder option.
|
|
78
|
+
* Phase 7+ implementations (Anthropic, OpenAI, Bedrock) register
|
|
79
|
+
* themselves in the strategyRegistry on import.
|
|
80
|
+
*/
|
|
81
|
+
cacheStrategy;
|
|
58
82
|
registry;
|
|
59
83
|
/**
|
|
60
84
|
* The Injection list — Skills, Steering, Instructions, Facts (and
|
|
@@ -119,7 +143,7 @@ export class Agent extends RunnerBase {
|
|
|
119
143
|
* dispatch correctly when their visible-set changes mid-turn.
|
|
120
144
|
*/
|
|
121
145
|
externalToolProvider;
|
|
122
|
-
constructor(opts, systemPromptValue, registry, voice, injections = [], memories = [], outputSchemaParser, toolProvider) {
|
|
146
|
+
constructor(opts, systemPromptValue, registry, voice, injections = [], memories = [], outputSchemaParser, toolProvider, systemPromptCachePolicy = 'always', cachingDisabled = false, cacheStrategy) {
|
|
123
147
|
super();
|
|
124
148
|
this.provider = opts.provider;
|
|
125
149
|
this.name = opts.name ?? 'Agent';
|
|
@@ -129,6 +153,11 @@ export class Agent extends RunnerBase {
|
|
|
129
153
|
this.maxTokens = opts.maxTokens;
|
|
130
154
|
this.maxIterations = clampIterations(opts.maxIterations ?? 10);
|
|
131
155
|
this.systemPromptValue = systemPromptValue;
|
|
156
|
+
this.systemPromptCachePolicy = systemPromptCachePolicy;
|
|
157
|
+
this.cachingDisabledValue = cachingDisabled;
|
|
158
|
+
// Auto-resolve strategy from provider.name unless caller overrides.
|
|
159
|
+
// NoOp is the wildcard fallback so unknown providers stay safe.
|
|
160
|
+
this.cacheStrategy = cacheStrategy ?? getDefaultCacheStrategy(opts.provider.name);
|
|
132
161
|
this.registry = registry;
|
|
133
162
|
this.injections = injections;
|
|
134
163
|
this.memories = memories;
|
|
@@ -158,6 +187,15 @@ export class Agent extends RunnerBase {
|
|
|
158
187
|
toFlowChart() {
|
|
159
188
|
return this.buildChart();
|
|
160
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Cache policy for the base system prompt. Read by the CacheDecision
|
|
192
|
+
* subflow (v2.6 Phase 4) to know how to treat the SystemPrompt slot's
|
|
193
|
+
* cache markers. Exposed as a method (not direct field access) so
|
|
194
|
+
* the Agent's encapsulation boundary stays clean.
|
|
195
|
+
*/
|
|
196
|
+
getSystemPromptCachePolicy() {
|
|
197
|
+
return this.systemPromptCachePolicy;
|
|
198
|
+
}
|
|
161
199
|
/**
|
|
162
200
|
* The footprintjs `RuntimeSnapshot` from the most recent `run()` /
|
|
163
201
|
* `resume()`. Feeds Lens's Trace tab (ExplainableShell `runtimeSnapshot`
|
|
@@ -310,6 +348,15 @@ export class Agent extends RunnerBase {
|
|
|
310
348
|
const pricingTable = this.pricingTable;
|
|
311
349
|
const costBudget = this.costBudget;
|
|
312
350
|
const permissionChecker = this.permissionChecker;
|
|
351
|
+
// Cache layer (v2.6) — capture for the seed + chart-build closures.
|
|
352
|
+
// `systemPromptCachePolicy` is fed into the CacheDecision subflow's
|
|
353
|
+
// inputMapper. `cacheStrategy` is consulted by BuildLLMRequest at
|
|
354
|
+
// run-time (Phase 7+ for the actual prepareRequest call). For
|
|
355
|
+
// Phase 6b the chart mounts the stages but BuildLLMRequest is a
|
|
356
|
+
// pass-through; Phase 7 lights up the strategy call.
|
|
357
|
+
const systemPromptCachePolicy = this.systemPromptCachePolicy;
|
|
358
|
+
const cachingDisabled = this.cachingDisabledValue;
|
|
359
|
+
const cacheStrategy = this.cacheStrategy;
|
|
313
360
|
const seed = (scope) => {
|
|
314
361
|
const args = scope.$getArgs();
|
|
315
362
|
scope.userMessage = args.message;
|
|
@@ -347,6 +394,17 @@ export class Agent extends RunnerBase {
|
|
|
347
394
|
scope.activeInjections = [];
|
|
348
395
|
scope.activatedInjectionIds = [];
|
|
349
396
|
scope.dynamicToolSchemas = toolSchemas;
|
|
397
|
+
// Cache layer state (v2.6) — initialized to inert defaults.
|
|
398
|
+
// CacheDecision subflow populates `cacheMarkers` per iteration;
|
|
399
|
+
// UpdateSkillHistory + CacheGate consume `cachingDisabled`,
|
|
400
|
+
// `recentHitRate`, `skillHistory`. Empty defaults mean the
|
|
401
|
+
// CacheGate falls through to 'apply-markers' on iter 1 (no
|
|
402
|
+
// history yet → no churn detected; recentHitRate undefined →
|
|
403
|
+
// hit-rate floor doesn't fire).
|
|
404
|
+
scope.cacheMarkers = [];
|
|
405
|
+
scope.cachingDisabled = cachingDisabled;
|
|
406
|
+
scope.recentHitRate = undefined;
|
|
407
|
+
scope.skillHistory = [];
|
|
350
408
|
typedEmit(scope, 'agentfootprint.agent.turn_start', {
|
|
351
409
|
turnIndex: 0,
|
|
352
410
|
userPrompt: args.message,
|
|
@@ -501,7 +559,7 @@ export class Agent extends RunnerBase {
|
|
|
501
559
|
// Falls back to the static schemas at startup before the tools
|
|
502
560
|
// slot has run for the first time.
|
|
503
561
|
const activeToolSchemas = scope.dynamicToolSchemas ?? toolSchemas;
|
|
504
|
-
const
|
|
562
|
+
const baseRequest = {
|
|
505
563
|
...(systemPrompt.length > 0 && { systemPrompt }),
|
|
506
564
|
messages,
|
|
507
565
|
...(activeToolSchemas.length > 0 && { tools: activeToolSchemas }),
|
|
@@ -509,6 +567,18 @@ export class Agent extends RunnerBase {
|
|
|
509
567
|
...(temperature !== undefined && { temperature }),
|
|
510
568
|
...(maxTokens !== undefined && { maxTokens }),
|
|
511
569
|
};
|
|
570
|
+
// v2.6+ — call cache strategy to attach provider-specific cache
|
|
571
|
+
// hints. CacheGate has already routed (apply-markers / no-markers)
|
|
572
|
+
// and populated scope.cacheMarkers accordingly. Strategy.prepareRequest
|
|
573
|
+
// is a pass-through for empty markers.
|
|
574
|
+
const cacheMarkers = scope.cacheMarkers ?? [];
|
|
575
|
+
const cachePrepared = await cacheStrategy.prepareRequest(baseRequest, cacheMarkers, {
|
|
576
|
+
iteration,
|
|
577
|
+
iterationsRemaining: Math.max(0, maxIterations - iteration),
|
|
578
|
+
recentHitRate: scope.recentHitRate,
|
|
579
|
+
cachingDisabled: scope.cachingDisabled ?? false,
|
|
580
|
+
});
|
|
581
|
+
const llmRequest = cachePrepared.request;
|
|
512
582
|
// Streaming-first: when the provider implements `stream()` we
|
|
513
583
|
// consume chunk-by-chunk so consumers (Lens commentary, chat
|
|
514
584
|
// UIs) see tokens as they arrive instead of waiting for the
|
|
@@ -960,6 +1030,44 @@ export class Agent extends RunnerBase {
|
|
|
960
1030
|
// concatenate.
|
|
961
1031
|
arrayMerge: ArrayMergeMode.Replace,
|
|
962
1032
|
})
|
|
1033
|
+
// ── Cache layer (v2.6) ─────────────────────────────────────
|
|
1034
|
+
// CacheDecision subflow walks `activeInjections` + evaluates
|
|
1035
|
+
// each `cache:` directive, emits provider-agnostic
|
|
1036
|
+
// `CacheMarker[]` to scope. Pure transform; no IO.
|
|
1037
|
+
//
|
|
1038
|
+
// CRITICAL: arrayMerge: ArrayMergeMode.Replace — same lesson
|
|
1039
|
+
// as the v2.5.1 InjectionEngine fix. The default footprintjs
|
|
1040
|
+
// behavior CONCATENATES arrays from child to parent;
|
|
1041
|
+
// `cacheMarkers` MUST replace each iteration, not accumulate.
|
|
1042
|
+
.addSubFlowChartNext(SUBFLOW_IDS.CACHE_DECISION, cacheDecisionSubflow, 'CacheDecision', {
|
|
1043
|
+
inputMapper: (parent) => ({
|
|
1044
|
+
activeInjections: parent.activeInjections ?? [],
|
|
1045
|
+
iteration: parent.iteration ?? 1,
|
|
1046
|
+
maxIterations: parent.maxIterations ?? maxIterations,
|
|
1047
|
+
userMessage: parent.userMessage ?? '',
|
|
1048
|
+
...(parent.lastToolResult !== undefined && {
|
|
1049
|
+
lastToolName: parent.lastToolResult?.toolName,
|
|
1050
|
+
}),
|
|
1051
|
+
cumulativeInputTokens: parent.totalInputTokens ?? 0,
|
|
1052
|
+
systemPromptCachePolicy,
|
|
1053
|
+
cachingDisabled: parent.cachingDisabled ?? false,
|
|
1054
|
+
}),
|
|
1055
|
+
outputMapper: (sf) => ({ cacheMarkers: sf.cacheMarkers }),
|
|
1056
|
+
arrayMerge: ArrayMergeMode.Replace,
|
|
1057
|
+
})
|
|
1058
|
+
.addFunction('UpdateSkillHistory', updateSkillHistoryStage, STAGE_IDS.UPDATE_SKILL_HISTORY, 'Update skill-history rolling window for CacheGate churn detection')
|
|
1059
|
+
.addDeciderFunction('CacheGate', cacheGateDecide, STAGE_IDS.CACHE_GATE, 'Gate cache-marker application: kill switch / hit-rate / skill-churn')
|
|
1060
|
+
.addFunctionBranch(STAGE_IDS.APPLY_MARKERS, 'ApplyMarkers',
|
|
1061
|
+
// Pass-through stage — markers stay in scope as-is.
|
|
1062
|
+
// BuildLLMRequest (Phase 7+) reads them on the next stage.
|
|
1063
|
+
() => undefined, 'Proceed with cache markers from CacheDecision')
|
|
1064
|
+
.addFunctionBranch(STAGE_IDS.SKIP_CACHING, 'SkipCaching',
|
|
1065
|
+
// Clear markers so BuildLLMRequest sees an empty list and
|
|
1066
|
+
// makes the request unmodified.
|
|
1067
|
+
(scope) => {
|
|
1068
|
+
scope.cacheMarkers = [];
|
|
1069
|
+
}, 'Skip caching this iteration')
|
|
1070
|
+
.end()
|
|
963
1071
|
.addFunction('IterationStart', iterationStart, 'iteration-start', 'Iteration begin marker')
|
|
964
1072
|
.addFunction('CallLLM', callLLM, STAGE_IDS.CALL_LLM, 'LLM invocation')
|
|
965
1073
|
.addDeciderFunction('Route', routeDecider, SUBFLOW_IDS.ROUTE, 'ReAct routing')
|
|
@@ -1006,6 +1114,26 @@ export class Agent extends RunnerBase {
|
|
|
1006
1114
|
export class AgentBuilder {
|
|
1007
1115
|
opts;
|
|
1008
1116
|
systemPromptValue = '';
|
|
1117
|
+
/**
|
|
1118
|
+
* Cache policy for the base system prompt. Set via the optional
|
|
1119
|
+
* 2nd argument to `.system(text, { cache })`. Default `'always'` —
|
|
1120
|
+
* the base prompt is stable per-turn and an ideal cache anchor.
|
|
1121
|
+
*/
|
|
1122
|
+
systemPromptCachePolicy = 'always';
|
|
1123
|
+
/**
|
|
1124
|
+
* Global cache kill switch. Set via `Agent.create({ caching: 'off' })`
|
|
1125
|
+
* (handled in `AgentOptions` propagation). Defaults to `false`
|
|
1126
|
+
* (caching enabled). When `true`, the CacheGate decider routes to
|
|
1127
|
+
* `'no-markers'` every iteration regardless of other rules.
|
|
1128
|
+
*/
|
|
1129
|
+
cachingDisabledValue = false;
|
|
1130
|
+
/**
|
|
1131
|
+
* Optional explicit CacheStrategy override. Default: undefined,
|
|
1132
|
+
* which means the agent auto-resolves from
|
|
1133
|
+
* `getDefaultCacheStrategy(provider.name)` at construction. Power
|
|
1134
|
+
* users override here for custom backends or test mocks.
|
|
1135
|
+
*/
|
|
1136
|
+
cacheStrategyOverride;
|
|
1009
1137
|
registry = [];
|
|
1010
1138
|
injectionList = [];
|
|
1011
1139
|
memoryList = [];
|
|
@@ -1042,9 +1170,34 @@ export class AgentBuilder {
|
|
|
1042
1170
|
thinkingOverrides = {};
|
|
1043
1171
|
constructor(opts) {
|
|
1044
1172
|
this.opts = opts;
|
|
1173
|
+
// Cache layer: opts.caching === 'off' propagates to scope's
|
|
1174
|
+
// `cachingDisabled` kill switch read by CacheGate. opts.cacheStrategy
|
|
1175
|
+
// overrides the registry-resolved default.
|
|
1176
|
+
if (opts.caching === 'off')
|
|
1177
|
+
this.cachingDisabledValue = true;
|
|
1178
|
+
if (opts.cacheStrategy !== undefined)
|
|
1179
|
+
this.cacheStrategyOverride = opts.cacheStrategy;
|
|
1045
1180
|
}
|
|
1046
|
-
|
|
1181
|
+
/**
|
|
1182
|
+
* Set the base system prompt.
|
|
1183
|
+
*
|
|
1184
|
+
* @param prompt - The system prompt text. Stable per-turn.
|
|
1185
|
+
* @param options - Optional config. `cache` controls how the
|
|
1186
|
+
* CacheDecision subflow treats this prompt block:
|
|
1187
|
+
* - `'always'` (default) — cache the base prompt as a stable
|
|
1188
|
+
* prefix anchor. Highest cache-hit rate; recommended for
|
|
1189
|
+
* production agents whose system prompt rarely changes.
|
|
1190
|
+
* - `'never'` — skip caching. Use if the prompt contains volatile
|
|
1191
|
+
* content (timestamps, per-request user IDs).
|
|
1192
|
+
* - `'while-active'` — semantically equivalent to `'always'` for
|
|
1193
|
+
* the base prompt (it's always active by definition).
|
|
1194
|
+
* - `{ until }` — conditional invalidation (e.g., flush after iter 5).
|
|
1195
|
+
*/
|
|
1196
|
+
system(prompt, options) {
|
|
1047
1197
|
this.systemPromptValue = prompt;
|
|
1198
|
+
if (options?.cache !== undefined) {
|
|
1199
|
+
this.systemPromptCachePolicy = options.cache;
|
|
1200
|
+
}
|
|
1048
1201
|
return this;
|
|
1049
1202
|
}
|
|
1050
1203
|
tool(tool) {
|
|
@@ -1358,7 +1511,7 @@ export class AgentBuilder {
|
|
|
1358
1511
|
const opts = this.maxIterationsOverride !== undefined
|
|
1359
1512
|
? { ...this.opts, maxIterations: this.maxIterationsOverride }
|
|
1360
1513
|
: this.opts;
|
|
1361
|
-
const agent = new Agent(opts, this.systemPromptValue, this.registry, voice, this.injectionList, this.memoryList, this.outputSchemaParser, this.toolProviderRef);
|
|
1514
|
+
const agent = new Agent(opts, this.systemPromptValue, this.registry, voice, this.injectionList, this.memoryList, this.outputSchemaParser, this.toolProviderRef, this.systemPromptCachePolicy, this.cachingDisabledValue, this.cacheStrategyOverride);
|
|
1362
1515
|
// Attach builder-collected recorders so they receive events from
|
|
1363
1516
|
// the very first run. Mirrors what consumers would do post-build
|
|
1364
1517
|
// via `agent.attach(rec)`; the builder method is purely sugar.
|