@yolk-sdk/agent 0.1.0-canary.15 → 0.1.0-canary.16
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/README.md +35 -1
- package/dist/compaction/budget.d.mts +21 -0
- package/dist/compaction/budget.d.mts.map +1 -0
- package/dist/compaction/budget.mjs +21 -0
- package/dist/compaction/budget.mjs.map +1 -0
- package/dist/compaction/estimator.d.mts +20 -0
- package/dist/compaction/estimator.d.mts.map +1 -0
- package/dist/compaction/estimator.mjs +32 -0
- package/dist/compaction/estimator.mjs.map +1 -0
- package/dist/compaction/index.d.mts +6 -0
- package/dist/compaction/index.mjs +6 -0
- package/dist/compaction/summary.d.mts +16 -0
- package/dist/compaction/summary.d.mts.map +1 -0
- package/dist/compaction/summary.mjs +24 -0
- package/dist/compaction/summary.mjs.map +1 -0
- package/dist/compaction/transformer.d.mts +21 -0
- package/dist/compaction/transformer.d.mts.map +1 -0
- package/dist/compaction/transformer.mjs +25 -0
- package/dist/compaction/transformer.mjs.map +1 -0
- package/dist/compaction/window.d.mts +58 -0
- package/dist/compaction/window.d.mts.map +1 -0
- package/dist/compaction/window.mjs +78 -0
- package/dist/compaction/window.mjs.map +1 -0
- package/package.json +7 -2
- package/src/compaction/budget.ts +43 -0
- package/src/compaction/estimator.ts +79 -0
- package/src/compaction/index.ts +49 -0
- package/src/compaction/summary.ts +70 -0
- package/src/compaction/transformer.ts +57 -0
- package/src/compaction/window.ts +172 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @yolk-sdk/agent
|
|
2
2
|
|
|
3
|
-
Domain-free agent protocol, loop, runtime, client, tools, providers, OAuth, React, skillset, and voice primitives.
|
|
3
|
+
Domain-free agent protocol, loop, runtime, client, compaction, tools, providers, OAuth, React, skillset, and voice primitives.
|
|
4
4
|
|
|
5
5
|
Root export is intentionally tiny. Import feature APIs from explicit subpaths.
|
|
6
6
|
|
|
@@ -23,6 +23,7 @@ Canary APIs are unstable. Keep all `@yolk-sdk/*` packages on the same version.
|
|
|
23
23
|
| `@yolk-sdk/agent/loop/testing` | Faux provider and tool executor test helpers |
|
|
24
24
|
| `@yolk-sdk/agent/runtime` | Transcript or append-backed runtime orchestration |
|
|
25
25
|
| `@yolk-sdk/agent/client` | HTTP/NDJSON transport, HITL resume, and client state helpers |
|
|
26
|
+
| `@yolk-sdk/agent/compaction` | Pure host-owned context budget, planning, and transformer utils |
|
|
26
27
|
| `@yolk-sdk/agent/tools` | Tool module registry, `makeTool`, task/question tool contracts |
|
|
27
28
|
| `@yolk-sdk/agent/react` | Headless React chat hook, reducer, selectors, and render model |
|
|
28
29
|
| `@yolk-sdk/agent/oauth` | Provider-neutral OAuth token and broker contracts |
|
|
@@ -48,6 +49,11 @@ import {
|
|
|
48
49
|
import { run } from '@yolk-sdk/agent/loop'
|
|
49
50
|
import { runRuntime } from '@yolk-sdk/agent/runtime'
|
|
50
51
|
import { initialAgentClientState, toolRunsFromHitlRequests } from '@yolk-sdk/agent/client'
|
|
52
|
+
import {
|
|
53
|
+
makeContextBudget,
|
|
54
|
+
makePreviewSummaryMessage,
|
|
55
|
+
makeWindowCompactionTransformer
|
|
56
|
+
} from '@yolk-sdk/agent/compaction'
|
|
51
57
|
import {
|
|
52
58
|
makeNonRecursiveTaskToolModule,
|
|
53
59
|
makeTaskToolResult,
|
|
@@ -92,6 +98,33 @@ const program = run({
|
|
|
92
98
|
// Provide LLM provider, loop config, context transformer, and tool executor layers in the host app.
|
|
93
99
|
```
|
|
94
100
|
|
|
101
|
+
## Context compaction
|
|
102
|
+
|
|
103
|
+
`@yolk-sdk/agent/compaction` provides pure scaffolding only. It does not call a model, persist
|
|
104
|
+
checkpoints, or retry overflow. Hosts own thresholds, summary policy, and durable storage.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import {
|
|
108
|
+
makeContextBudget,
|
|
109
|
+
makePreviewSummaryMessage,
|
|
110
|
+
makeWindowCompactionTransformer
|
|
111
|
+
} from '@yolk-sdk/agent/compaction'
|
|
112
|
+
|
|
113
|
+
const budget = makeContextBudget({
|
|
114
|
+
contextWindowTokens: 200_000,
|
|
115
|
+
reservedOutputTokens: 20_000,
|
|
116
|
+
warningRatio: 0.8,
|
|
117
|
+
compactionRatio: 1
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
const ContextLayer = makeWindowCompactionTransformer({
|
|
121
|
+
strategy: 'window-summary-v1',
|
|
122
|
+
thresholdTokens: budget.compactionInputTokens,
|
|
123
|
+
tailMessageCount: 16,
|
|
124
|
+
makeSummaryMessage: messages => makePreviewSummaryMessage(messages)
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
95
128
|
## Protocol content
|
|
96
129
|
|
|
97
130
|
`Content` is either plain text or ordered parts:
|
|
@@ -187,6 +220,7 @@ See `examples/next/lib/agents/workflow-runtime/text-response.ts` for host-owned
|
|
|
187
220
|
## Boundaries
|
|
188
221
|
|
|
189
222
|
- Core loop/protocol/runtime/tools have no React, Next.js, provider SDKs, auth, storage drivers, or app concepts.
|
|
223
|
+
- `@yolk-sdk/agent/compaction` is pure helper scaffolding; hosts own thresholds, summaries, checkpoints, and overflow retry.
|
|
190
224
|
- `@yolk-sdk/agent/react` is headless and uses React as an optional peer.
|
|
191
225
|
- Provider subpaths own vendor wire/auth mechanics only; hosts own token storage, refresh, routing, and policy.
|
|
192
226
|
- Loop stays stateless: transcript in, events out.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/compaction/budget.d.ts
|
|
2
|
+
type ContextBudget = {
|
|
3
|
+
readonly contextWindowTokens: number;
|
|
4
|
+
readonly reservedOutputTokens: number;
|
|
5
|
+
readonly usableInputTokens: number;
|
|
6
|
+
readonly warningInputTokens: number;
|
|
7
|
+
readonly compactionInputTokens: number;
|
|
8
|
+
};
|
|
9
|
+
type ContextBudgetInput = {
|
|
10
|
+
readonly contextWindowTokens: number;
|
|
11
|
+
readonly reservedOutputTokens: number;
|
|
12
|
+
readonly warningRatio: number;
|
|
13
|
+
readonly compactionRatio: number;
|
|
14
|
+
};
|
|
15
|
+
declare const makeContextBudget: (input: ContextBudgetInput) => ContextBudget;
|
|
16
|
+
declare const contextBudgetUsageRatio: (tokens: number, budget: ContextBudget) => number;
|
|
17
|
+
type ContextBudgetStatus = 'idle' | 'normal' | 'warning' | 'compact';
|
|
18
|
+
declare const contextBudgetStatus: (tokens: number, budget: ContextBudget) => ContextBudgetStatus;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { ContextBudget, ContextBudgetInput, ContextBudgetStatus, contextBudgetStatus, contextBudgetUsageRatio, makeContextBudget };
|
|
21
|
+
//# sourceMappingURL=budget.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.mts","names":[],"sources":["../../src/compaction/budget.ts"],"mappings":";KAAY,aAAA;EAAA,SACD,mBAAA;EAAA,SACA,oBAAA;EAAA,SACA,iBAAA;EAAA,SACA,kBAAA;EAAA,SACA,qBAAA;AAAA;AAAA,KAGC,kBAAA;EAAA,SACD,mBAAA;EAAA,SACA,oBAAA;EAAA,SACA,YAAA;EAAA,SACA,eAAA;AAAA;AAAA,cAGE,iBAAA,GAAqB,KAAA,EAAO,kBAAA,KAAqB,aAU7D;AAAA,cAEY,uBAAA,GAA2B,MAAA,UAAgB,MAAA,EAAQ,aAAa;AAAA,KAGjE,mBAAA;AAAA,cAEC,mBAAA,GAAuB,MAAA,UAAgB,MAAA,EAAQ,aAAA,KAAgB,mBAU3E"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/compaction/budget.ts
|
|
2
|
+
const makeContextBudget = (input) => {
|
|
3
|
+
const usableInputTokens = Math.max(0, input.contextWindowTokens - input.reservedOutputTokens);
|
|
4
|
+
return {
|
|
5
|
+
contextWindowTokens: input.contextWindowTokens,
|
|
6
|
+
reservedOutputTokens: input.reservedOutputTokens,
|
|
7
|
+
usableInputTokens,
|
|
8
|
+
warningInputTokens: Math.floor(usableInputTokens * input.warningRatio),
|
|
9
|
+
compactionInputTokens: Math.floor(usableInputTokens * input.compactionRatio)
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
const contextBudgetUsageRatio = (tokens, budget) => budget.usableInputTokens === 0 ? 0 : tokens / budget.usableInputTokens;
|
|
13
|
+
const contextBudgetStatus = (tokens, budget) => {
|
|
14
|
+
if (tokens <= 0) return "idle";
|
|
15
|
+
if (tokens >= budget.compactionInputTokens) return "compact";
|
|
16
|
+
return tokens >= budget.warningInputTokens ? "warning" : "normal";
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { contextBudgetStatus, contextBudgetUsageRatio, makeContextBudget };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=budget.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.mjs","names":[],"sources":["../../src/compaction/budget.ts"],"sourcesContent":["export type ContextBudget = {\n readonly contextWindowTokens: number\n readonly reservedOutputTokens: number\n readonly usableInputTokens: number\n readonly warningInputTokens: number\n readonly compactionInputTokens: number\n}\n\nexport type ContextBudgetInput = {\n readonly contextWindowTokens: number\n readonly reservedOutputTokens: number\n readonly warningRatio: number\n readonly compactionRatio: number\n}\n\nexport const makeContextBudget = (input: ContextBudgetInput): ContextBudget => {\n const usableInputTokens = Math.max(0, input.contextWindowTokens - input.reservedOutputTokens)\n\n return {\n contextWindowTokens: input.contextWindowTokens,\n reservedOutputTokens: input.reservedOutputTokens,\n usableInputTokens,\n warningInputTokens: Math.floor(usableInputTokens * input.warningRatio),\n compactionInputTokens: Math.floor(usableInputTokens * input.compactionRatio)\n }\n}\n\nexport const contextBudgetUsageRatio = (tokens: number, budget: ContextBudget) =>\n budget.usableInputTokens === 0 ? 0 : tokens / budget.usableInputTokens\n\nexport type ContextBudgetStatus = 'idle' | 'normal' | 'warning' | 'compact'\n\nexport const contextBudgetStatus = (tokens: number, budget: ContextBudget): ContextBudgetStatus => {\n if (tokens <= 0) {\n return 'idle'\n }\n\n if (tokens >= budget.compactionInputTokens) {\n return 'compact'\n }\n\n return tokens >= budget.warningInputTokens ? 'warning' : 'normal'\n}\n"],"mappings":";AAeA,MAAa,qBAAqB,UAA6C;CAC7E,MAAM,oBAAoB,KAAK,IAAI,GAAG,MAAM,sBAAsB,MAAM,oBAAoB;CAE5F,OAAO;EACL,qBAAqB,MAAM;EAC3B,sBAAsB,MAAM;EAC5B;EACA,oBAAoB,KAAK,MAAM,oBAAoB,MAAM,YAAY;EACrE,uBAAuB,KAAK,MAAM,oBAAoB,MAAM,eAAe;CAC7E;AACF;AAEA,MAAa,2BAA2B,QAAgB,WACtD,OAAO,sBAAsB,IAAI,IAAI,SAAS,OAAO;AAIvD,MAAa,uBAAuB,QAAgB,WAA+C;CACjG,IAAI,UAAU,GACZ,OAAO;CAGT,IAAI,UAAU,OAAO,uBACnB,OAAO;CAGT,OAAO,UAAU,OAAO,qBAAqB,YAAY;AAC3D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AgentMessage, Content } from "@yolk-sdk/agent/protocol";
|
|
2
|
+
|
|
3
|
+
//#region src/compaction/estimator.d.ts
|
|
4
|
+
type TokenEstimateOptions = {
|
|
5
|
+
readonly charactersPerToken?: number;
|
|
6
|
+
readonly mediaPartTokens?: number;
|
|
7
|
+
readonly messageOverheadTokens?: number;
|
|
8
|
+
};
|
|
9
|
+
type MessageTokenEstimator = (message: AgentMessage) => number;
|
|
10
|
+
type TranscriptTokenEstimator = (messages: ReadonlyArray<AgentMessage>) => number;
|
|
11
|
+
declare const defaultCharactersPerToken = 4;
|
|
12
|
+
declare const defaultMediaPartTokens = 512;
|
|
13
|
+
declare const defaultMessageOverheadTokens = 6;
|
|
14
|
+
declare const estimateTextTokens: (text: string, options?: TokenEstimateOptions) => number;
|
|
15
|
+
declare const estimateContentTokens: (content: Content, options?: TokenEstimateOptions) => number;
|
|
16
|
+
declare const estimateAgentMessageTokens: (message: AgentMessage, options?: TokenEstimateOptions) => number;
|
|
17
|
+
declare const estimateAgentMessagesTokens: (messages: ReadonlyArray<AgentMessage>, options?: TokenEstimateOptions) => number;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { MessageTokenEstimator, TokenEstimateOptions, TranscriptTokenEstimator, defaultCharactersPerToken, defaultMediaPartTokens, defaultMessageOverheadTokens, estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateContentTokens, estimateTextTokens };
|
|
20
|
+
//# sourceMappingURL=estimator.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estimator.d.mts","names":[],"sources":["../../src/compaction/estimator.ts"],"mappings":";;;KAQY,oBAAA;EAAA,SACD,kBAAA;EAAA,SACA,eAAA;EAAA,SACA,qBAAA;AAAA;AAAA,KAGC,qBAAA,IAAyB,OAAqB,EAAZ,YAAY;AAAA,KAC9C,wBAAA,IAA4B,QAAA,EAAU,aAAa,CAAC,YAAA;AAAA,cAEnD,yBAAA;AAAA,cACA,sBAAA;AAAA,cACA,4BAAA;AAAA,cAWA,kBAAA,GAAsB,IAAA,UAAc,OAAA,GAAS,oBAAyB;AAAA,cAGtE,qBAAA,GAAyB,OAAA,EAAS,OAAA,EAAS,OAAA,GAAS,oBAAyB;AAAA,cAiB7E,0BAAA,GACX,OAAA,EAAS,YAAA,EACT,OAAA,GAAS,oBAAyB;AAAA,cAuBvB,2BAAA,GACX,QAAA,EAAU,aAAA,CAAc,YAAA,GACxB,OAAA,GAAS,oBAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { assistantContent, assistantHostToolCalls, assistantReasoningText } from "@yolk-sdk/agent/protocol";
|
|
2
|
+
//#region src/compaction/estimator.ts
|
|
3
|
+
const defaultCharactersPerToken = 4;
|
|
4
|
+
const defaultMediaPartTokens = 512;
|
|
5
|
+
const defaultMessageOverheadTokens = 6;
|
|
6
|
+
const charactersPerToken = (options) => Math.max(1, options.charactersPerToken ?? 4);
|
|
7
|
+
const mediaPartTokens = (options) => Math.max(0, options.mediaPartTokens ?? 512);
|
|
8
|
+
const messageOverheadTokens = (options) => Math.max(0, options.messageOverheadTokens ?? 6);
|
|
9
|
+
const estimateTextTokens = (text, options = {}) => Math.ceil(text.length / charactersPerToken(options));
|
|
10
|
+
const estimateContentTokens = (content, options = {}) => {
|
|
11
|
+
if (typeof content === "string") return estimateTextTokens(content, options);
|
|
12
|
+
return content.reduce((total, part) => {
|
|
13
|
+
switch (part._tag) {
|
|
14
|
+
case "Text": return total + estimateTextTokens(part.text, options);
|
|
15
|
+
case "Image":
|
|
16
|
+
case "Document":
|
|
17
|
+
case "Audio": return total + mediaPartTokens(options);
|
|
18
|
+
}
|
|
19
|
+
}, 0);
|
|
20
|
+
};
|
|
21
|
+
const estimateAgentMessageTokens = (message, options = {}) => {
|
|
22
|
+
switch (message._tag) {
|
|
23
|
+
case "User":
|
|
24
|
+
case "ToolResult": return estimateContentTokens(message.content, options) + messageOverheadTokens(options);
|
|
25
|
+
case "Assistant": return estimateContentTokens(assistantContent(message), options) + estimateTextTokens(assistantReasoningText(message), options) + assistantHostToolCalls(message).reduce((total, call) => total + estimateTextTokens(call.id, options) + estimateTextTokens(call.name, options) + messageOverheadTokens(options), 0) + messageOverheadTokens(options);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const estimateAgentMessagesTokens = (messages, options = {}) => messages.reduce((total, message) => total + estimateAgentMessageTokens(message, options), 0);
|
|
29
|
+
//#endregion
|
|
30
|
+
export { defaultCharactersPerToken, defaultMediaPartTokens, defaultMessageOverheadTokens, estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateContentTokens, estimateTextTokens };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=estimator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estimator.mjs","names":[],"sources":["../../src/compaction/estimator.ts"],"sourcesContent":["import {\n assistantContent,\n assistantHostToolCalls,\n assistantReasoningText,\n type AgentMessage,\n type Content\n} from '@yolk-sdk/agent/protocol'\n\nexport type TokenEstimateOptions = {\n readonly charactersPerToken?: number\n readonly mediaPartTokens?: number\n readonly messageOverheadTokens?: number\n}\n\nexport type MessageTokenEstimator = (message: AgentMessage) => number\nexport type TranscriptTokenEstimator = (messages: ReadonlyArray<AgentMessage>) => number\n\nexport const defaultCharactersPerToken = 4\nexport const defaultMediaPartTokens = 512\nexport const defaultMessageOverheadTokens = 6\n\nconst charactersPerToken = (options: TokenEstimateOptions) =>\n Math.max(1, options.charactersPerToken ?? defaultCharactersPerToken)\n\nconst mediaPartTokens = (options: TokenEstimateOptions) =>\n Math.max(0, options.mediaPartTokens ?? defaultMediaPartTokens)\n\nconst messageOverheadTokens = (options: TokenEstimateOptions) =>\n Math.max(0, options.messageOverheadTokens ?? defaultMessageOverheadTokens)\n\nexport const estimateTextTokens = (text: string, options: TokenEstimateOptions = {}) =>\n Math.ceil(text.length / charactersPerToken(options))\n\nexport const estimateContentTokens = (content: Content, options: TokenEstimateOptions = {}) => {\n if (typeof content === 'string') {\n return estimateTextTokens(content, options)\n }\n\n return content.reduce((total, part) => {\n switch (part._tag) {\n case 'Text':\n return total + estimateTextTokens(part.text, options)\n case 'Image':\n case 'Document':\n case 'Audio':\n return total + mediaPartTokens(options)\n }\n }, 0)\n}\n\nexport const estimateAgentMessageTokens = (\n message: AgentMessage,\n options: TokenEstimateOptions = {}\n) => {\n switch (message._tag) {\n case 'User':\n case 'ToolResult':\n return estimateContentTokens(message.content, options) + messageOverheadTokens(options)\n case 'Assistant':\n return (\n estimateContentTokens(assistantContent(message), options) +\n estimateTextTokens(assistantReasoningText(message), options) +\n assistantHostToolCalls(message).reduce(\n (total, call) =>\n total +\n estimateTextTokens(call.id, options) +\n estimateTextTokens(call.name, options) +\n messageOverheadTokens(options),\n 0\n ) +\n messageOverheadTokens(options)\n )\n }\n}\n\nexport const estimateAgentMessagesTokens = (\n messages: ReadonlyArray<AgentMessage>,\n options: TokenEstimateOptions = {}\n) => messages.reduce((total, message) => total + estimateAgentMessageTokens(message, options), 0)\n"],"mappings":";;AAiBA,MAAa,4BAA4B;AACzC,MAAa,yBAAyB;AACtC,MAAa,+BAA+B;AAE5C,MAAM,sBAAsB,YAC1B,KAAK,IAAI,GAAG,QAAQ,sBAAA,CAA+C;AAErE,MAAM,mBAAmB,YACvB,KAAK,IAAI,GAAG,QAAQ,mBAAA,GAAyC;AAE/D,MAAM,yBAAyB,YAC7B,KAAK,IAAI,GAAG,QAAQ,yBAAA,CAAqD;AAE3E,MAAa,sBAAsB,MAAc,UAAgC,CAAC,MAChF,KAAK,KAAK,KAAK,SAAS,mBAAmB,OAAO,CAAC;AAErD,MAAa,yBAAyB,SAAkB,UAAgC,CAAC,MAAM;CAC7F,IAAI,OAAO,YAAY,UACrB,OAAO,mBAAmB,SAAS,OAAO;CAG5C,OAAO,QAAQ,QAAQ,OAAO,SAAS;EACrC,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO,QAAQ,mBAAmB,KAAK,MAAM,OAAO;GACtD,KAAK;GACL,KAAK;GACL,KAAK,SACH,OAAO,QAAQ,gBAAgB,OAAO;EAC1C;CACF,GAAG,CAAC;AACN;AAEA,MAAa,8BACX,SACA,UAAgC,CAAC,MAC9B;CACH,QAAQ,QAAQ,MAAhB;EACE,KAAK;EACL,KAAK,cACH,OAAO,sBAAsB,QAAQ,SAAS,OAAO,IAAI,sBAAsB,OAAO;EACxF,KAAK,aACH,OACE,sBAAsB,iBAAiB,OAAO,GAAG,OAAO,IACxD,mBAAmB,uBAAuB,OAAO,GAAG,OAAO,IAC3D,uBAAuB,OAAO,EAAE,QAC7B,OAAO,SACN,QACA,mBAAmB,KAAK,IAAI,OAAO,IACnC,mBAAmB,KAAK,MAAM,OAAO,IACrC,sBAAsB,OAAO,GAC/B,CACF,IACA,sBAAsB,OAAO;CAEnC;AACF;AAEA,MAAa,+BACX,UACA,UAAgC,CAAC,MAC9B,SAAS,QAAQ,OAAO,YAAY,QAAQ,2BAA2B,SAAS,OAAO,GAAG,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ContextBudget, ContextBudgetInput, ContextBudgetStatus, contextBudgetStatus, contextBudgetUsageRatio, makeContextBudget } from "./budget.mjs";
|
|
2
|
+
import { MessageTokenEstimator, TokenEstimateOptions, TranscriptTokenEstimator, defaultCharactersPerToken, defaultMediaPartTokens, defaultMessageOverheadTokens, estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateContentTokens, estimateTextTokens } from "./estimator.mjs";
|
|
3
|
+
import { PreviewSummaryMessageOptions, defaultPreviewSummaryHeader, defaultSummaryPreviewMaxCharacters, makePreviewSummaryContent, makePreviewSummaryMessage, previewAgentMessage, truncateSummaryPreview } from "./summary.mjs";
|
|
4
|
+
import { ApplyCompactionPlanOptions, CompactCompactionPlan, CompactionResult, CompactionSkipReason, CompletedCompactionResult, SkippedCompactionPlan, SkippedCompactionResult, WindowCompactionPlan, WindowCompactionPlanOptions, applyCompactionPlan, defaultCompactionTailMessageCount, defaultMinimumCompactionMessages, planWindowCompaction, windowTailStartIndex } from "./window.mjs";
|
|
5
|
+
import { SummaryMessageFactory, WindowCompactionOptions, compactWindowMessages, makeWindowCompactionTransformer } from "./transformer.mjs";
|
|
6
|
+
export { type ApplyCompactionPlanOptions, type CompactCompactionPlan, type CompactionResult, type CompactionSkipReason, type CompletedCompactionResult, type ContextBudget, type ContextBudgetInput, type ContextBudgetStatus, type MessageTokenEstimator, type PreviewSummaryMessageOptions, type SkippedCompactionPlan, type SkippedCompactionResult, type SummaryMessageFactory, type TokenEstimateOptions, type TranscriptTokenEstimator, type WindowCompactionOptions, type WindowCompactionPlan, type WindowCompactionPlanOptions, applyCompactionPlan, compactWindowMessages, contextBudgetStatus, contextBudgetUsageRatio, defaultCharactersPerToken, defaultCompactionTailMessageCount, defaultMediaPartTokens, defaultMessageOverheadTokens, defaultMinimumCompactionMessages, defaultPreviewSummaryHeader, defaultSummaryPreviewMaxCharacters, estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateContentTokens, estimateTextTokens, makeContextBudget, makePreviewSummaryContent, makePreviewSummaryMessage, makeWindowCompactionTransformer, planWindowCompaction, previewAgentMessage, truncateSummaryPreview, windowTailStartIndex };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { contextBudgetStatus, contextBudgetUsageRatio, makeContextBudget } from "./budget.mjs";
|
|
2
|
+
import { defaultCharactersPerToken, defaultMediaPartTokens, defaultMessageOverheadTokens, estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateContentTokens, estimateTextTokens } from "./estimator.mjs";
|
|
3
|
+
import { defaultPreviewSummaryHeader, defaultSummaryPreviewMaxCharacters, makePreviewSummaryContent, makePreviewSummaryMessage, previewAgentMessage, truncateSummaryPreview } from "./summary.mjs";
|
|
4
|
+
import { applyCompactionPlan, defaultCompactionTailMessageCount, defaultMinimumCompactionMessages, planWindowCompaction, windowTailStartIndex } from "./window.mjs";
|
|
5
|
+
import { compactWindowMessages, makeWindowCompactionTransformer } from "./transformer.mjs";
|
|
6
|
+
export { applyCompactionPlan, compactWindowMessages, contextBudgetStatus, contextBudgetUsageRatio, defaultCharactersPerToken, defaultCompactionTailMessageCount, defaultMediaPartTokens, defaultMessageOverheadTokens, defaultMinimumCompactionMessages, defaultPreviewSummaryHeader, defaultSummaryPreviewMaxCharacters, estimateAgentMessageTokens, estimateAgentMessagesTokens, estimateContentTokens, estimateTextTokens, makeContextBudget, makePreviewSummaryContent, makePreviewSummaryMessage, makeWindowCompactionTransformer, planWindowCompaction, previewAgentMessage, truncateSummaryPreview, windowTailStartIndex };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { AgentMessage, UserMessage } from "@yolk-sdk/agent/protocol";
|
|
2
|
+
|
|
3
|
+
//#region src/compaction/summary.d.ts
|
|
4
|
+
type PreviewSummaryMessageOptions = {
|
|
5
|
+
readonly header?: string;
|
|
6
|
+
readonly maxCharacters?: number;
|
|
7
|
+
};
|
|
8
|
+
declare const defaultPreviewSummaryHeader = "Earlier conversation compacted. Preserve these facts and continue from the recent messages.";
|
|
9
|
+
declare const defaultSummaryPreviewMaxCharacters = 180;
|
|
10
|
+
declare const truncateSummaryPreview: (value: string, maxCharacters: number) => string;
|
|
11
|
+
declare const previewAgentMessage: (message: AgentMessage, options?: PreviewSummaryMessageOptions) => string;
|
|
12
|
+
declare const makePreviewSummaryContent: (messages: ReadonlyArray<AgentMessage>, options?: PreviewSummaryMessageOptions) => string;
|
|
13
|
+
declare const makePreviewSummaryMessage: (messages: ReadonlyArray<AgentMessage>, options?: PreviewSummaryMessageOptions) => UserMessage;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { PreviewSummaryMessageOptions, defaultPreviewSummaryHeader, defaultSummaryPreviewMaxCharacters, makePreviewSummaryContent, makePreviewSummaryMessage, previewAgentMessage, truncateSummaryPreview };
|
|
16
|
+
//# sourceMappingURL=summary.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.d.mts","names":[],"sources":["../../src/compaction/summary.ts"],"mappings":";;;KAQY,4BAAA;EAAA,SACD,MAAA;EAAA,SACA,aAAa;AAAA;AAAA,cAGX,2BAAA;AAAA,cAGA,kCAAA;AAAA,cAEA,sBAAA,GAA0B,KAAA,UAAe,aAAqB;AAAA,cAc9D,mBAAA,GACX,OAAA,EAAS,YAAA,EACT,OAAA,GAAS,4BAAiC;AAAA,cAuB/B,yBAAA,GACX,QAAA,EAAU,aAAA,CAAc,YAAA,GACxB,OAAA,GAAS,4BAAA;AAAA,cAOE,yBAAA,GACX,QAAA,EAAU,aAAA,CAAc,YAAA,GACxB,OAAA,GAAS,4BAAA,KAAiC,WAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { UserMessage, assistantContent, contentPreview, contentText } from "@yolk-sdk/agent/protocol";
|
|
2
|
+
//#region src/compaction/summary.ts
|
|
3
|
+
const defaultPreviewSummaryHeader = "Earlier conversation compacted. Preserve these facts and continue from the recent messages.";
|
|
4
|
+
const defaultSummaryPreviewMaxCharacters = 180;
|
|
5
|
+
const truncateSummaryPreview = (value, maxCharacters) => {
|
|
6
|
+
const normalized = value.replaceAll(/\s+/g, " ").trim();
|
|
7
|
+
if (maxCharacters <= 0) return "";
|
|
8
|
+
if (normalized.length <= maxCharacters) return normalized;
|
|
9
|
+
return `${normalized.slice(0, Math.max(0, maxCharacters - 1))}…`;
|
|
10
|
+
};
|
|
11
|
+
const previewAgentMessage = (message, options = {}) => {
|
|
12
|
+
const maxCharacters = options.maxCharacters ?? 180;
|
|
13
|
+
switch (message._tag) {
|
|
14
|
+
case "User": return `User: ${truncateSummaryPreview(contentText(message.content) || contentPreview(message.content), maxCharacters)}`;
|
|
15
|
+
case "Assistant": return `Assistant: ${truncateSummaryPreview(contentText(assistantContent(message)) || contentPreview(assistantContent(message)), maxCharacters)}`;
|
|
16
|
+
case "ToolResult": return `Tool ${message.toolCallId}: ${truncateSummaryPreview(contentText(message.content) || contentPreview(message.content), maxCharacters)}`;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const makePreviewSummaryContent = (messages, options = {}) => [options.header ?? "Earlier conversation compacted. Preserve these facts and continue from the recent messages.", ...messages.map((message) => previewAgentMessage(message, options))].join("\n");
|
|
20
|
+
const makePreviewSummaryMessage = (messages, options = {}) => UserMessage.make({ content: makePreviewSummaryContent(messages, options) });
|
|
21
|
+
//#endregion
|
|
22
|
+
export { defaultPreviewSummaryHeader, defaultSummaryPreviewMaxCharacters, makePreviewSummaryContent, makePreviewSummaryMessage, previewAgentMessage, truncateSummaryPreview };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=summary.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.mjs","names":[],"sources":["../../src/compaction/summary.ts"],"sourcesContent":["import {\n UserMessage,\n assistantContent,\n contentPreview,\n contentText,\n type AgentMessage\n} from '@yolk-sdk/agent/protocol'\n\nexport type PreviewSummaryMessageOptions = {\n readonly header?: string\n readonly maxCharacters?: number\n}\n\nexport const defaultPreviewSummaryHeader =\n 'Earlier conversation compacted. Preserve these facts and continue from the recent messages.'\n\nexport const defaultSummaryPreviewMaxCharacters = 180\n\nexport const truncateSummaryPreview = (value: string, maxCharacters: number) => {\n const normalized = value.replaceAll(/\\s+/g, ' ').trim()\n\n if (maxCharacters <= 0) {\n return ''\n }\n\n if (normalized.length <= maxCharacters) {\n return normalized\n }\n\n return `${normalized.slice(0, Math.max(0, maxCharacters - 1))}…`\n}\n\nexport const previewAgentMessage = (\n message: AgentMessage,\n options: PreviewSummaryMessageOptions = {}\n) => {\n const maxCharacters = options.maxCharacters ?? defaultSummaryPreviewMaxCharacters\n\n switch (message._tag) {\n case 'User':\n return `User: ${truncateSummaryPreview(\n contentText(message.content) || contentPreview(message.content),\n maxCharacters\n )}`\n case 'Assistant':\n return `Assistant: ${truncateSummaryPreview(\n contentText(assistantContent(message)) || contentPreview(assistantContent(message)),\n maxCharacters\n )}`\n case 'ToolResult':\n return `Tool ${message.toolCallId}: ${truncateSummaryPreview(\n contentText(message.content) || contentPreview(message.content),\n maxCharacters\n )}`\n }\n}\n\nexport const makePreviewSummaryContent = (\n messages: ReadonlyArray<AgentMessage>,\n options: PreviewSummaryMessageOptions = {}\n) =>\n [\n options.header ?? defaultPreviewSummaryHeader,\n ...messages.map(message => previewAgentMessage(message, options))\n ].join('\\n')\n\nexport const makePreviewSummaryMessage = (\n messages: ReadonlyArray<AgentMessage>,\n options: PreviewSummaryMessageOptions = {}\n) => UserMessage.make({ content: makePreviewSummaryContent(messages, options) })\n"],"mappings":";;AAaA,MAAa,8BACX;AAEF,MAAa,qCAAqC;AAElD,MAAa,0BAA0B,OAAe,kBAA0B;CAC9E,MAAM,aAAa,MAAM,WAAW,QAAQ,GAAG,EAAE,KAAK;CAEtD,IAAI,iBAAiB,GACnB,OAAO;CAGT,IAAI,WAAW,UAAU,eACvB,OAAO;CAGT,OAAO,GAAG,WAAW,MAAM,GAAG,KAAK,IAAI,GAAG,gBAAgB,CAAC,CAAC,EAAE;AAChE;AAEA,MAAa,uBACX,SACA,UAAwC,CAAC,MACtC;CACH,MAAM,gBAAgB,QAAQ,iBAAA;CAE9B,QAAQ,QAAQ,MAAhB;EACE,KAAK,QACH,OAAO,SAAS,uBACd,YAAY,QAAQ,OAAO,KAAK,eAAe,QAAQ,OAAO,GAC9D,aACF;EACF,KAAK,aACH,OAAO,cAAc,uBACnB,YAAY,iBAAiB,OAAO,CAAC,KAAK,eAAe,iBAAiB,OAAO,CAAC,GAClF,aACF;EACF,KAAK,cACH,OAAO,QAAQ,QAAQ,WAAW,IAAI,uBACpC,YAAY,QAAQ,OAAO,KAAK,eAAe,QAAQ,OAAO,GAC9D,aACF;CACJ;AACF;AAEA,MAAa,6BACX,UACA,UAAwC,CAAC,MAEzC,CACE,QAAQ,UAAA,+FACR,GAAG,SAAS,KAAI,YAAW,oBAAoB,SAAS,OAAO,CAAC,CAClE,EAAE,KAAK,IAAI;AAEb,MAAa,6BACX,UACA,UAAwC,CAAC,MACtC,YAAY,KAAK,EAAE,SAAS,0BAA0B,UAAU,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TranscriptTokenEstimator } from "./estimator.mjs";
|
|
2
|
+
import { CompactionResult } from "./window.mjs";
|
|
3
|
+
import { Layer } from "effect";
|
|
4
|
+
import { AgentMessage } from "@yolk-sdk/agent/protocol";
|
|
5
|
+
import { ContextTransformer } from "@yolk-sdk/agent/loop";
|
|
6
|
+
|
|
7
|
+
//#region src/compaction/transformer.d.ts
|
|
8
|
+
type SummaryMessageFactory = (messages: ReadonlyArray<AgentMessage>) => AgentMessage;
|
|
9
|
+
type WindowCompactionOptions = {
|
|
10
|
+
readonly strategy: string;
|
|
11
|
+
readonly thresholdTokens: number;
|
|
12
|
+
readonly tailMessageCount?: number;
|
|
13
|
+
readonly minMessages?: number;
|
|
14
|
+
readonly estimateTokens?: TranscriptTokenEstimator;
|
|
15
|
+
readonly makeSummaryMessage: SummaryMessageFactory;
|
|
16
|
+
};
|
|
17
|
+
declare const compactWindowMessages: (messages: ReadonlyArray<AgentMessage>, options: WindowCompactionOptions) => CompactionResult;
|
|
18
|
+
declare const makeWindowCompactionTransformer: (options: WindowCompactionOptions) => Layer.Layer<ContextTransformer, never, never>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { SummaryMessageFactory, WindowCompactionOptions, compactWindowMessages, makeWindowCompactionTransformer };
|
|
21
|
+
//# sourceMappingURL=transformer.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.d.mts","names":[],"sources":["../../src/compaction/transformer.ts"],"mappings":";;;;;;;KAYY,qBAAA,IACV,QAAA,EAAU,aAAA,CAAc,YAAA,MACrB,YAAA;AAAA,KAEO,uBAAA;EAAA,SACD,QAAA;EAAA,SACA,eAAA;EAAA,SACA,gBAAA;EAAA,SACA,WAAA;EAAA,SACA,cAAA,GAAiB,wBAAA;EAAA,SACjB,kBAAA,EAAoB,qBAAqB;AAAA;AAAA,cAWvC,qBAAA,GACX,QAAA,EAAU,aAAA,CAAc,YAAA,GACxB,OAAA,EAAS,uBAAA,KACR,gBAAA;AAAA,cAcU,+BAAA,GAAmC,OAAA,EAAS,uBAAA,KAAuB,KAAA,CAAA,KAAA,CAAA,kBAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { applyCompactionPlan, planWindowCompaction } from "./window.mjs";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
|
+
import { ContextTransformer } from "@yolk-sdk/agent/loop";
|
|
4
|
+
//#region src/compaction/transformer.ts
|
|
5
|
+
const skippedCompactionResult = (plan) => ({
|
|
6
|
+
_tag: "Skipped",
|
|
7
|
+
reason: plan.reason,
|
|
8
|
+
messages: plan.messages,
|
|
9
|
+
events: [],
|
|
10
|
+
beforeTokens: plan.beforeTokens
|
|
11
|
+
});
|
|
12
|
+
const compactWindowMessages = (messages, options) => {
|
|
13
|
+
const plan = planWindowCompaction(messages, options);
|
|
14
|
+
if (plan._tag === "Skip") return skippedCompactionResult(plan);
|
|
15
|
+
return applyCompactionPlan(plan, {
|
|
16
|
+
strategy: options.strategy,
|
|
17
|
+
summaryMessage: options.makeSummaryMessage(plan.compactedMessages),
|
|
18
|
+
estimateTokens: options.estimateTokens
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
const makeWindowCompactionTransformer = (options) => Layer.succeed(ContextTransformer, ContextTransformer.of({ transform: (messages) => Effect.succeed(compactWindowMessages(messages, options)) }));
|
|
22
|
+
//#endregion
|
|
23
|
+
export { compactWindowMessages, makeWindowCompactionTransformer };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=transformer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.mjs","names":[],"sources":["../../src/compaction/transformer.ts"],"sourcesContent":["import { Effect, Layer } from 'effect'\nimport type { AgentMessage } from '@yolk-sdk/agent/protocol'\nimport { ContextTransformer } from '@yolk-sdk/agent/loop'\nimport {\n applyCompactionPlan,\n planWindowCompaction,\n type CompactionResult,\n type SkippedCompactionPlan,\n type SkippedCompactionResult\n} from './window.ts'\nimport type { TranscriptTokenEstimator } from './estimator.ts'\n\nexport type SummaryMessageFactory = (\n messages: ReadonlyArray<AgentMessage>\n) => AgentMessage\n\nexport type WindowCompactionOptions = {\n readonly strategy: string\n readonly thresholdTokens: number\n readonly tailMessageCount?: number\n readonly minMessages?: number\n readonly estimateTokens?: TranscriptTokenEstimator\n readonly makeSummaryMessage: SummaryMessageFactory\n}\n\nconst skippedCompactionResult = (plan: SkippedCompactionPlan): SkippedCompactionResult => ({\n _tag: 'Skipped',\n reason: plan.reason,\n messages: plan.messages,\n events: [],\n beforeTokens: plan.beforeTokens\n})\n\nexport const compactWindowMessages = (\n messages: ReadonlyArray<AgentMessage>,\n options: WindowCompactionOptions\n): CompactionResult => {\n const plan = planWindowCompaction(messages, options)\n\n if (plan._tag === 'Skip') {\n return skippedCompactionResult(plan)\n }\n\n return applyCompactionPlan(plan, {\n strategy: options.strategy,\n summaryMessage: options.makeSummaryMessage(plan.compactedMessages),\n estimateTokens: options.estimateTokens\n })\n}\n\nexport const makeWindowCompactionTransformer = (options: WindowCompactionOptions) =>\n Layer.succeed(\n ContextTransformer,\n ContextTransformer.of({\n transform: messages => Effect.succeed(compactWindowMessages(messages, options))\n })\n )\n"],"mappings":";;;;AAyBA,MAAM,2BAA2B,UAA0D;CACzF,MAAM;CACN,QAAQ,KAAK;CACb,UAAU,KAAK;CACf,QAAQ,CAAC;CACT,cAAc,KAAK;AACrB;AAEA,MAAa,yBACX,UACA,YACqB;CACrB,MAAM,OAAO,qBAAqB,UAAU,OAAO;CAEnD,IAAI,KAAK,SAAS,QAChB,OAAO,wBAAwB,IAAI;CAGrC,OAAO,oBAAoB,MAAM;EAC/B,UAAU,QAAQ;EAClB,gBAAgB,QAAQ,mBAAmB,KAAK,iBAAiB;EACjE,gBAAgB,QAAQ;CAC1B,CAAC;AACH;AAEA,MAAa,mCAAmC,YAC9C,MAAM,QACJ,oBACA,mBAAmB,GAAG,EACpB,YAAW,aAAY,OAAO,QAAQ,sBAAsB,UAAU,OAAO,CAAC,EAChF,CAAC,CACH"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { TranscriptTokenEstimator } from "./estimator.mjs";
|
|
2
|
+
import { AgentEvent, AgentMessage } from "@yolk-sdk/agent/protocol";
|
|
3
|
+
|
|
4
|
+
//#region src/compaction/window.d.ts
|
|
5
|
+
declare const defaultCompactionTailMessageCount = 16;
|
|
6
|
+
declare const defaultMinimumCompactionMessages = 2;
|
|
7
|
+
type CompactionSkipReason = 'too_few_messages' | 'below_threshold' | 'empty_window' | 'not_smaller';
|
|
8
|
+
type WindowCompactionPlanOptions = {
|
|
9
|
+
readonly thresholdTokens: number;
|
|
10
|
+
readonly tailMessageCount?: number;
|
|
11
|
+
readonly minMessages?: number;
|
|
12
|
+
readonly estimateTokens?: TranscriptTokenEstimator;
|
|
13
|
+
};
|
|
14
|
+
type SkippedCompactionPlan = {
|
|
15
|
+
readonly _tag: 'Skip';
|
|
16
|
+
readonly reason: CompactionSkipReason;
|
|
17
|
+
readonly messages: ReadonlyArray<AgentMessage>;
|
|
18
|
+
readonly beforeTokens: number;
|
|
19
|
+
};
|
|
20
|
+
type CompactCompactionPlan = {
|
|
21
|
+
readonly _tag: 'Compact';
|
|
22
|
+
readonly messages: ReadonlyArray<AgentMessage>;
|
|
23
|
+
readonly compactedMessages: ReadonlyArray<AgentMessage>;
|
|
24
|
+
readonly recentMessages: ReadonlyArray<AgentMessage>;
|
|
25
|
+
readonly tailStartIndex: number;
|
|
26
|
+
readonly beforeTokens: number;
|
|
27
|
+
};
|
|
28
|
+
type WindowCompactionPlan = SkippedCompactionPlan | CompactCompactionPlan;
|
|
29
|
+
type SkippedCompactionResult = {
|
|
30
|
+
readonly _tag: 'Skipped';
|
|
31
|
+
readonly reason: CompactionSkipReason;
|
|
32
|
+
readonly messages: ReadonlyArray<AgentMessage>;
|
|
33
|
+
readonly events: ReadonlyArray<AgentEvent>;
|
|
34
|
+
readonly beforeTokens: number;
|
|
35
|
+
};
|
|
36
|
+
type CompletedCompactionResult = {
|
|
37
|
+
readonly _tag: 'Compacted';
|
|
38
|
+
readonly strategy: string;
|
|
39
|
+
readonly messages: ReadonlyArray<AgentMessage>;
|
|
40
|
+
readonly events: ReadonlyArray<AgentEvent>;
|
|
41
|
+
readonly beforeTokens: number;
|
|
42
|
+
readonly afterTokens: number;
|
|
43
|
+
readonly compactedMessages: ReadonlyArray<AgentMessage>;
|
|
44
|
+
readonly recentMessages: ReadonlyArray<AgentMessage>;
|
|
45
|
+
readonly summaryMessage: AgentMessage;
|
|
46
|
+
};
|
|
47
|
+
type CompactionResult = SkippedCompactionResult | CompletedCompactionResult;
|
|
48
|
+
type ApplyCompactionPlanOptions = {
|
|
49
|
+
readonly strategy: string;
|
|
50
|
+
readonly summaryMessage: AgentMessage;
|
|
51
|
+
readonly estimateTokens?: TranscriptTokenEstimator;
|
|
52
|
+
};
|
|
53
|
+
declare const windowTailStartIndex: (messages: ReadonlyArray<AgentMessage>, tailMessageCount: number) => number;
|
|
54
|
+
declare const planWindowCompaction: (messages: ReadonlyArray<AgentMessage>, options: WindowCompactionPlanOptions) => WindowCompactionPlan;
|
|
55
|
+
declare const applyCompactionPlan: (plan: WindowCompactionPlan, options: ApplyCompactionPlanOptions) => CompactionResult;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { ApplyCompactionPlanOptions, CompactCompactionPlan, CompactionResult, CompactionSkipReason, CompletedCompactionResult, SkippedCompactionPlan, SkippedCompactionResult, WindowCompactionPlan, WindowCompactionPlanOptions, applyCompactionPlan, defaultCompactionTailMessageCount, defaultMinimumCompactionMessages, planWindowCompaction, windowTailStartIndex };
|
|
58
|
+
//# sourceMappingURL=window.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.d.mts","names":[],"sources":["../../src/compaction/window.ts"],"mappings":";;;;cAQa,iCAAA;AAAA,cACA,gCAAA;AAAA,KAED,oBAAA;AAAA,KAMA,2BAAA;EAAA,SACD,eAAA;EAAA,SACA,gBAAA;EAAA,SACA,WAAA;EAAA,SACA,cAAA,GAAiB,wBAAwB;AAAA;AAAA,KAGxC,qBAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,oBAAA;EAAA,SACR,QAAA,EAAU,aAAA,CAAc,YAAA;EAAA,SACxB,YAAA;AAAA;AAAA,KAGC,qBAAA;EAAA,SACD,IAAA;EAAA,SACA,QAAA,EAAU,aAAA,CAAc,YAAA;EAAA,SACxB,iBAAA,EAAmB,aAAA,CAAc,YAAA;EAAA,SACjC,cAAA,EAAgB,aAAA,CAAc,YAAA;EAAA,SAC9B,cAAA;EAAA,SACA,YAAA;AAAA;AAAA,KAGC,oBAAA,GAAuB,qBAAA,GAAwB,qBAAqB;AAAA,KAEpE,uBAAA;EAAA,SACD,IAAA;EAAA,SACA,MAAA,EAAQ,oBAAA;EAAA,SACR,QAAA,EAAU,aAAA,CAAc,YAAA;EAAA,SACxB,MAAA,EAAQ,aAAA,CAAc,UAAA;EAAA,SACtB,YAAA;AAAA;AAAA,KAGC,yBAAA;EAAA,SACD,IAAA;EAAA,SACA,QAAA;EAAA,SACA,QAAA,EAAU,aAAA,CAAc,YAAA;EAAA,SACxB,MAAA,EAAQ,aAAA,CAAc,UAAA;EAAA,SACtB,YAAA;EAAA,SACA,WAAA;EAAA,SACA,iBAAA,EAAmB,aAAA,CAAc,YAAA;EAAA,SACjC,cAAA,EAAgB,aAAA,CAAc,YAAA;EAAA,SAC9B,cAAA,EAAgB,YAAA;AAAA;AAAA,KAGf,gBAAA,GAAmB,uBAAA,GAA0B,yBAAyB;AAAA,KAEtE,0BAAA;EAAA,SACD,QAAA;EAAA,SACA,cAAA,EAAgB,YAAA;EAAA,SAChB,cAAA,GAAiB,wBAAwB;AAAA;AAAA,cAkBvC,oBAAA,GACX,QAAA,EAAU,aAAa,CAAC,YAAA,GACxB,gBAAA;AAAA,cAaW,oBAAA,GACX,QAAA,EAAU,aAAA,CAAc,YAAA,GACxB,OAAA,EAAS,2BAAA,KACR,oBAAA;AAAA,cAkCU,mBAAA,GACX,IAAA,EAAM,oBAAA,EACN,OAAA,EAAS,0BAAA,KACR,gBAAA"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { estimateAgentMessagesTokens } from "./estimator.mjs";
|
|
2
|
+
import { CompactionEnd, CompactionStart } from "@yolk-sdk/agent/protocol";
|
|
3
|
+
//#region src/compaction/window.ts
|
|
4
|
+
const defaultCompactionTailMessageCount = 16;
|
|
5
|
+
const defaultMinimumCompactionMessages = 2;
|
|
6
|
+
const estimator = (estimateTokens) => estimateTokens ?? estimateAgentMessagesTokens;
|
|
7
|
+
const skippedCompactionResult = (reason, messages, beforeTokens) => ({
|
|
8
|
+
_tag: "Skipped",
|
|
9
|
+
reason,
|
|
10
|
+
messages,
|
|
11
|
+
events: [],
|
|
12
|
+
beforeTokens
|
|
13
|
+
});
|
|
14
|
+
const windowTailStartIndex = (messages, tailMessageCount) => {
|
|
15
|
+
const tailCount = Math.min(Math.max(0, tailMessageCount), Math.max(0, messages.length - 1));
|
|
16
|
+
let index = Math.max(1, messages.length - tailCount);
|
|
17
|
+
while (index > 1 && messages[index]?._tag === "ToolResult") index -= 1;
|
|
18
|
+
return index;
|
|
19
|
+
};
|
|
20
|
+
const planWindowCompaction = (messages, options) => {
|
|
21
|
+
const beforeTokens = estimator(options.estimateTokens)(messages);
|
|
22
|
+
const minMessages = options.minMessages ?? 2;
|
|
23
|
+
if (messages.length <= minMessages) return {
|
|
24
|
+
_tag: "Skip",
|
|
25
|
+
reason: "too_few_messages",
|
|
26
|
+
messages,
|
|
27
|
+
beforeTokens
|
|
28
|
+
};
|
|
29
|
+
if (beforeTokens < options.thresholdTokens) return {
|
|
30
|
+
_tag: "Skip",
|
|
31
|
+
reason: "below_threshold",
|
|
32
|
+
messages,
|
|
33
|
+
beforeTokens
|
|
34
|
+
};
|
|
35
|
+
const tailStart = windowTailStartIndex(messages, options.tailMessageCount ?? 16);
|
|
36
|
+
const compactedMessages = messages.slice(0, tailStart);
|
|
37
|
+
const recentMessages = messages.slice(tailStart);
|
|
38
|
+
if (compactedMessages.length === 0 || recentMessages.length === 0) return {
|
|
39
|
+
_tag: "Skip",
|
|
40
|
+
reason: "empty_window",
|
|
41
|
+
messages,
|
|
42
|
+
beforeTokens
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
_tag: "Compact",
|
|
46
|
+
messages,
|
|
47
|
+
compactedMessages,
|
|
48
|
+
recentMessages,
|
|
49
|
+
tailStartIndex: tailStart,
|
|
50
|
+
beforeTokens
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
const applyCompactionPlan = (plan, options) => {
|
|
54
|
+
if (plan._tag === "Skip") return skippedCompactionResult(plan.reason, plan.messages, plan.beforeTokens);
|
|
55
|
+
const estimateTokens = estimator(options.estimateTokens);
|
|
56
|
+
const messages = [options.summaryMessage, ...plan.recentMessages];
|
|
57
|
+
const afterTokens = estimateTokens(messages);
|
|
58
|
+
if (afterTokens >= plan.beforeTokens) return skippedCompactionResult("not_smaller", plan.messages, plan.beforeTokens);
|
|
59
|
+
return {
|
|
60
|
+
_tag: "Compacted",
|
|
61
|
+
strategy: options.strategy,
|
|
62
|
+
messages,
|
|
63
|
+
events: [CompactionStart.make({ strategy: options.strategy }), CompactionEnd.make({
|
|
64
|
+
strategy: options.strategy,
|
|
65
|
+
beforeTokens: plan.beforeTokens,
|
|
66
|
+
afterTokens
|
|
67
|
+
})],
|
|
68
|
+
beforeTokens: plan.beforeTokens,
|
|
69
|
+
afterTokens,
|
|
70
|
+
compactedMessages: plan.compactedMessages,
|
|
71
|
+
recentMessages: plan.recentMessages,
|
|
72
|
+
summaryMessage: options.summaryMessage
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
export { applyCompactionPlan, defaultCompactionTailMessageCount, defaultMinimumCompactionMessages, planWindowCompaction, windowTailStartIndex };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=window.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.mjs","names":[],"sources":["../../src/compaction/window.ts"],"sourcesContent":["import {\n CompactionEnd,\n CompactionStart,\n type AgentEvent,\n type AgentMessage\n} from '@yolk-sdk/agent/protocol'\nimport { estimateAgentMessagesTokens, type TranscriptTokenEstimator } from './estimator.ts'\n\nexport const defaultCompactionTailMessageCount = 16\nexport const defaultMinimumCompactionMessages = 2\n\nexport type CompactionSkipReason =\n | 'too_few_messages'\n | 'below_threshold'\n | 'empty_window'\n | 'not_smaller'\n\nexport type WindowCompactionPlanOptions = {\n readonly thresholdTokens: number\n readonly tailMessageCount?: number\n readonly minMessages?: number\n readonly estimateTokens?: TranscriptTokenEstimator\n}\n\nexport type SkippedCompactionPlan = {\n readonly _tag: 'Skip'\n readonly reason: CompactionSkipReason\n readonly messages: ReadonlyArray<AgentMessage>\n readonly beforeTokens: number\n}\n\nexport type CompactCompactionPlan = {\n readonly _tag: 'Compact'\n readonly messages: ReadonlyArray<AgentMessage>\n readonly compactedMessages: ReadonlyArray<AgentMessage>\n readonly recentMessages: ReadonlyArray<AgentMessage>\n readonly tailStartIndex: number\n readonly beforeTokens: number\n}\n\nexport type WindowCompactionPlan = SkippedCompactionPlan | CompactCompactionPlan\n\nexport type SkippedCompactionResult = {\n readonly _tag: 'Skipped'\n readonly reason: CompactionSkipReason\n readonly messages: ReadonlyArray<AgentMessage>\n readonly events: ReadonlyArray<AgentEvent>\n readonly beforeTokens: number\n}\n\nexport type CompletedCompactionResult = {\n readonly _tag: 'Compacted'\n readonly strategy: string\n readonly messages: ReadonlyArray<AgentMessage>\n readonly events: ReadonlyArray<AgentEvent>\n readonly beforeTokens: number\n readonly afterTokens: number\n readonly compactedMessages: ReadonlyArray<AgentMessage>\n readonly recentMessages: ReadonlyArray<AgentMessage>\n readonly summaryMessage: AgentMessage\n}\n\nexport type CompactionResult = SkippedCompactionResult | CompletedCompactionResult\n\nexport type ApplyCompactionPlanOptions = {\n readonly strategy: string\n readonly summaryMessage: AgentMessage\n readonly estimateTokens?: TranscriptTokenEstimator\n}\n\nconst estimator = (estimateTokens?: TranscriptTokenEstimator) =>\n estimateTokens ?? estimateAgentMessagesTokens\n\nconst skippedCompactionResult = (\n reason: CompactionSkipReason,\n messages: ReadonlyArray<AgentMessage>,\n beforeTokens: number\n): SkippedCompactionResult => ({\n _tag: 'Skipped',\n reason,\n messages,\n events: [],\n beforeTokens\n})\n\nexport const windowTailStartIndex = (\n messages: ReadonlyArray<AgentMessage>,\n tailMessageCount: number\n) => {\n const tailCount = Math.min(Math.max(0, tailMessageCount), Math.max(0, messages.length - 1))\n const initialIndex = Math.max(1, messages.length - tailCount)\n let index = initialIndex\n\n while (index > 1 && messages[index]?._tag === 'ToolResult') {\n index -= 1\n }\n\n return index\n}\n\nexport const planWindowCompaction = (\n messages: ReadonlyArray<AgentMessage>,\n options: WindowCompactionPlanOptions\n): WindowCompactionPlan => {\n const estimateTokens = estimator(options.estimateTokens)\n const beforeTokens = estimateTokens(messages)\n const minMessages = options.minMessages ?? defaultMinimumCompactionMessages\n\n if (messages.length <= minMessages) {\n return { _tag: 'Skip', reason: 'too_few_messages', messages, beforeTokens }\n }\n\n if (beforeTokens < options.thresholdTokens) {\n return { _tag: 'Skip', reason: 'below_threshold', messages, beforeTokens }\n }\n\n const tailStart = windowTailStartIndex(\n messages,\n options.tailMessageCount ?? defaultCompactionTailMessageCount\n )\n const compactedMessages = messages.slice(0, tailStart)\n const recentMessages = messages.slice(tailStart)\n\n if (compactedMessages.length === 0 || recentMessages.length === 0) {\n return { _tag: 'Skip', reason: 'empty_window', messages, beforeTokens }\n }\n\n return {\n _tag: 'Compact',\n messages,\n compactedMessages,\n recentMessages,\n tailStartIndex: tailStart,\n beforeTokens\n }\n}\n\nexport const applyCompactionPlan = (\n plan: WindowCompactionPlan,\n options: ApplyCompactionPlanOptions\n): CompactionResult => {\n if (plan._tag === 'Skip') {\n return skippedCompactionResult(plan.reason, plan.messages, plan.beforeTokens)\n }\n\n const estimateTokens = estimator(options.estimateTokens)\n const messages = [options.summaryMessage, ...plan.recentMessages]\n const afterTokens = estimateTokens(messages)\n\n if (afterTokens >= plan.beforeTokens) {\n return skippedCompactionResult('not_smaller', plan.messages, plan.beforeTokens)\n }\n\n return {\n _tag: 'Compacted',\n strategy: options.strategy,\n messages,\n events: [\n CompactionStart.make({ strategy: options.strategy }),\n CompactionEnd.make({\n strategy: options.strategy,\n beforeTokens: plan.beforeTokens,\n afterTokens\n })\n ],\n beforeTokens: plan.beforeTokens,\n afterTokens,\n compactedMessages: plan.compactedMessages,\n recentMessages: plan.recentMessages,\n summaryMessage: options.summaryMessage\n }\n}\n"],"mappings":";;;AAQA,MAAa,oCAAoC;AACjD,MAAa,mCAAmC;AA6DhD,MAAM,aAAa,mBACjB,kBAAkB;AAEpB,MAAM,2BACJ,QACA,UACA,kBAC6B;CAC7B,MAAM;CACN;CACA;CACA,QAAQ,CAAC;CACT;AACF;AAEA,MAAa,wBACX,UACA,qBACG;CACH,MAAM,YAAY,KAAK,IAAI,KAAK,IAAI,GAAG,gBAAgB,GAAG,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC,CAAC;CAE1F,IAAI,QADiB,KAAK,IAAI,GAAG,SAAS,SAAS,SAC5B;CAEvB,OAAO,QAAQ,KAAK,SAAS,QAAQ,SAAS,cAC5C,SAAS;CAGX,OAAO;AACT;AAEA,MAAa,wBACX,UACA,YACyB;CAEzB,MAAM,eADiB,UAAU,QAAQ,cACP,EAAE,QAAQ;CAC5C,MAAM,cAAc,QAAQ,eAAA;CAE5B,IAAI,SAAS,UAAU,aACrB,OAAO;EAAE,MAAM;EAAQ,QAAQ;EAAoB;EAAU;CAAa;CAG5E,IAAI,eAAe,QAAQ,iBACzB,OAAO;EAAE,MAAM;EAAQ,QAAQ;EAAmB;EAAU;CAAa;CAG3E,MAAM,YAAY,qBAChB,UACA,QAAQ,oBAAA,EACV;CACA,MAAM,oBAAoB,SAAS,MAAM,GAAG,SAAS;CACrD,MAAM,iBAAiB,SAAS,MAAM,SAAS;CAE/C,IAAI,kBAAkB,WAAW,KAAK,eAAe,WAAW,GAC9D,OAAO;EAAE,MAAM;EAAQ,QAAQ;EAAgB;EAAU;CAAa;CAGxE,OAAO;EACL,MAAM;EACN;EACA;EACA;EACA,gBAAgB;EAChB;CACF;AACF;AAEA,MAAa,uBACX,MACA,YACqB;CACrB,IAAI,KAAK,SAAS,QAChB,OAAO,wBAAwB,KAAK,QAAQ,KAAK,UAAU,KAAK,YAAY;CAG9E,MAAM,iBAAiB,UAAU,QAAQ,cAAc;CACvD,MAAM,WAAW,CAAC,QAAQ,gBAAgB,GAAG,KAAK,cAAc;CAChE,MAAM,cAAc,eAAe,QAAQ;CAE3C,IAAI,eAAe,KAAK,cACtB,OAAO,wBAAwB,eAAe,KAAK,UAAU,KAAK,YAAY;CAGhF,OAAO;EACL,MAAM;EACN,UAAU,QAAQ;EAClB;EACA,QAAQ,CACN,gBAAgB,KAAK,EAAE,UAAU,QAAQ,SAAS,CAAC,GACnD,cAAc,KAAK;GACjB,UAAU,QAAQ;GAClB,cAAc,KAAK;GACnB;EACF,CAAC,CACH;EACA,cAAc,KAAK;EACnB;EACA,mBAAmB,KAAK;EACxB,gBAAgB,KAAK;EACrB,gBAAgB,QAAQ;CAC1B;AACF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yolk-sdk/agent",
|
|
3
|
-
"version": "0.1.0-canary.
|
|
4
|
-
"description": "Protocol, loop, runtime, client, tools, React, providers, OAuth, skillset, and voice primitives for building Yolk agents.",
|
|
3
|
+
"version": "0.1.0-canary.16",
|
|
4
|
+
"description": "Protocol, loop, runtime, client, compaction, tools, React, providers, OAuth, skillset, and voice primitives for building Yolk agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -37,6 +37,11 @@
|
|
|
37
37
|
"import": "./dist/client/index.mjs",
|
|
38
38
|
"default": "./dist/client/index.mjs"
|
|
39
39
|
},
|
|
40
|
+
"./compaction": {
|
|
41
|
+
"types": "./dist/compaction/index.d.mts",
|
|
42
|
+
"import": "./dist/compaction/index.mjs",
|
|
43
|
+
"default": "./dist/compaction/index.mjs"
|
|
44
|
+
},
|
|
40
45
|
"./loop": {
|
|
41
46
|
"types": "./dist/loop/index.d.mts",
|
|
42
47
|
"import": "./dist/loop/index.mjs",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export type ContextBudget = {
|
|
2
|
+
readonly contextWindowTokens: number
|
|
3
|
+
readonly reservedOutputTokens: number
|
|
4
|
+
readonly usableInputTokens: number
|
|
5
|
+
readonly warningInputTokens: number
|
|
6
|
+
readonly compactionInputTokens: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type ContextBudgetInput = {
|
|
10
|
+
readonly contextWindowTokens: number
|
|
11
|
+
readonly reservedOutputTokens: number
|
|
12
|
+
readonly warningRatio: number
|
|
13
|
+
readonly compactionRatio: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const makeContextBudget = (input: ContextBudgetInput): ContextBudget => {
|
|
17
|
+
const usableInputTokens = Math.max(0, input.contextWindowTokens - input.reservedOutputTokens)
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
contextWindowTokens: input.contextWindowTokens,
|
|
21
|
+
reservedOutputTokens: input.reservedOutputTokens,
|
|
22
|
+
usableInputTokens,
|
|
23
|
+
warningInputTokens: Math.floor(usableInputTokens * input.warningRatio),
|
|
24
|
+
compactionInputTokens: Math.floor(usableInputTokens * input.compactionRatio)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const contextBudgetUsageRatio = (tokens: number, budget: ContextBudget) =>
|
|
29
|
+
budget.usableInputTokens === 0 ? 0 : tokens / budget.usableInputTokens
|
|
30
|
+
|
|
31
|
+
export type ContextBudgetStatus = 'idle' | 'normal' | 'warning' | 'compact'
|
|
32
|
+
|
|
33
|
+
export const contextBudgetStatus = (tokens: number, budget: ContextBudget): ContextBudgetStatus => {
|
|
34
|
+
if (tokens <= 0) {
|
|
35
|
+
return 'idle'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (tokens >= budget.compactionInputTokens) {
|
|
39
|
+
return 'compact'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return tokens >= budget.warningInputTokens ? 'warning' : 'normal'
|
|
43
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assistantContent,
|
|
3
|
+
assistantHostToolCalls,
|
|
4
|
+
assistantReasoningText,
|
|
5
|
+
type AgentMessage,
|
|
6
|
+
type Content
|
|
7
|
+
} from '@yolk-sdk/agent/protocol'
|
|
8
|
+
|
|
9
|
+
export type TokenEstimateOptions = {
|
|
10
|
+
readonly charactersPerToken?: number
|
|
11
|
+
readonly mediaPartTokens?: number
|
|
12
|
+
readonly messageOverheadTokens?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type MessageTokenEstimator = (message: AgentMessage) => number
|
|
16
|
+
export type TranscriptTokenEstimator = (messages: ReadonlyArray<AgentMessage>) => number
|
|
17
|
+
|
|
18
|
+
export const defaultCharactersPerToken = 4
|
|
19
|
+
export const defaultMediaPartTokens = 512
|
|
20
|
+
export const defaultMessageOverheadTokens = 6
|
|
21
|
+
|
|
22
|
+
const charactersPerToken = (options: TokenEstimateOptions) =>
|
|
23
|
+
Math.max(1, options.charactersPerToken ?? defaultCharactersPerToken)
|
|
24
|
+
|
|
25
|
+
const mediaPartTokens = (options: TokenEstimateOptions) =>
|
|
26
|
+
Math.max(0, options.mediaPartTokens ?? defaultMediaPartTokens)
|
|
27
|
+
|
|
28
|
+
const messageOverheadTokens = (options: TokenEstimateOptions) =>
|
|
29
|
+
Math.max(0, options.messageOverheadTokens ?? defaultMessageOverheadTokens)
|
|
30
|
+
|
|
31
|
+
export const estimateTextTokens = (text: string, options: TokenEstimateOptions = {}) =>
|
|
32
|
+
Math.ceil(text.length / charactersPerToken(options))
|
|
33
|
+
|
|
34
|
+
export const estimateContentTokens = (content: Content, options: TokenEstimateOptions = {}) => {
|
|
35
|
+
if (typeof content === 'string') {
|
|
36
|
+
return estimateTextTokens(content, options)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return content.reduce((total, part) => {
|
|
40
|
+
switch (part._tag) {
|
|
41
|
+
case 'Text':
|
|
42
|
+
return total + estimateTextTokens(part.text, options)
|
|
43
|
+
case 'Image':
|
|
44
|
+
case 'Document':
|
|
45
|
+
case 'Audio':
|
|
46
|
+
return total + mediaPartTokens(options)
|
|
47
|
+
}
|
|
48
|
+
}, 0)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const estimateAgentMessageTokens = (
|
|
52
|
+
message: AgentMessage,
|
|
53
|
+
options: TokenEstimateOptions = {}
|
|
54
|
+
) => {
|
|
55
|
+
switch (message._tag) {
|
|
56
|
+
case 'User':
|
|
57
|
+
case 'ToolResult':
|
|
58
|
+
return estimateContentTokens(message.content, options) + messageOverheadTokens(options)
|
|
59
|
+
case 'Assistant':
|
|
60
|
+
return (
|
|
61
|
+
estimateContentTokens(assistantContent(message), options) +
|
|
62
|
+
estimateTextTokens(assistantReasoningText(message), options) +
|
|
63
|
+
assistantHostToolCalls(message).reduce(
|
|
64
|
+
(total, call) =>
|
|
65
|
+
total +
|
|
66
|
+
estimateTextTokens(call.id, options) +
|
|
67
|
+
estimateTextTokens(call.name, options) +
|
|
68
|
+
messageOverheadTokens(options),
|
|
69
|
+
0
|
|
70
|
+
) +
|
|
71
|
+
messageOverheadTokens(options)
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const estimateAgentMessagesTokens = (
|
|
77
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
78
|
+
options: TokenEstimateOptions = {}
|
|
79
|
+
) => messages.reduce((total, message) => total + estimateAgentMessageTokens(message, options), 0)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export {
|
|
2
|
+
contextBudgetStatus,
|
|
3
|
+
contextBudgetUsageRatio,
|
|
4
|
+
makeContextBudget
|
|
5
|
+
} from './budget.ts'
|
|
6
|
+
export type { ContextBudget, ContextBudgetInput, ContextBudgetStatus } from './budget.ts'
|
|
7
|
+
export {
|
|
8
|
+
defaultCharactersPerToken,
|
|
9
|
+
defaultMediaPartTokens,
|
|
10
|
+
defaultMessageOverheadTokens,
|
|
11
|
+
estimateAgentMessageTokens,
|
|
12
|
+
estimateAgentMessagesTokens,
|
|
13
|
+
estimateContentTokens,
|
|
14
|
+
estimateTextTokens
|
|
15
|
+
} from './estimator.ts'
|
|
16
|
+
export type {
|
|
17
|
+
MessageTokenEstimator,
|
|
18
|
+
TokenEstimateOptions,
|
|
19
|
+
TranscriptTokenEstimator
|
|
20
|
+
} from './estimator.ts'
|
|
21
|
+
export {
|
|
22
|
+
defaultPreviewSummaryHeader,
|
|
23
|
+
defaultSummaryPreviewMaxCharacters,
|
|
24
|
+
makePreviewSummaryContent,
|
|
25
|
+
makePreviewSummaryMessage,
|
|
26
|
+
previewAgentMessage,
|
|
27
|
+
truncateSummaryPreview
|
|
28
|
+
} from './summary.ts'
|
|
29
|
+
export type { PreviewSummaryMessageOptions } from './summary.ts'
|
|
30
|
+
export {
|
|
31
|
+
applyCompactionPlan,
|
|
32
|
+
defaultCompactionTailMessageCount,
|
|
33
|
+
defaultMinimumCompactionMessages,
|
|
34
|
+
planWindowCompaction,
|
|
35
|
+
windowTailStartIndex
|
|
36
|
+
} from './window.ts'
|
|
37
|
+
export type {
|
|
38
|
+
ApplyCompactionPlanOptions,
|
|
39
|
+
CompactCompactionPlan,
|
|
40
|
+
CompactionResult,
|
|
41
|
+
CompactionSkipReason,
|
|
42
|
+
CompletedCompactionResult,
|
|
43
|
+
SkippedCompactionPlan,
|
|
44
|
+
SkippedCompactionResult,
|
|
45
|
+
WindowCompactionPlan,
|
|
46
|
+
WindowCompactionPlanOptions
|
|
47
|
+
} from './window.ts'
|
|
48
|
+
export { compactWindowMessages, makeWindowCompactionTransformer } from './transformer.ts'
|
|
49
|
+
export type { SummaryMessageFactory, WindowCompactionOptions } from './transformer.ts'
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UserMessage,
|
|
3
|
+
assistantContent,
|
|
4
|
+
contentPreview,
|
|
5
|
+
contentText,
|
|
6
|
+
type AgentMessage
|
|
7
|
+
} from '@yolk-sdk/agent/protocol'
|
|
8
|
+
|
|
9
|
+
export type PreviewSummaryMessageOptions = {
|
|
10
|
+
readonly header?: string
|
|
11
|
+
readonly maxCharacters?: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const defaultPreviewSummaryHeader =
|
|
15
|
+
'Earlier conversation compacted. Preserve these facts and continue from the recent messages.'
|
|
16
|
+
|
|
17
|
+
export const defaultSummaryPreviewMaxCharacters = 180
|
|
18
|
+
|
|
19
|
+
export const truncateSummaryPreview = (value: string, maxCharacters: number) => {
|
|
20
|
+
const normalized = value.replaceAll(/\s+/g, ' ').trim()
|
|
21
|
+
|
|
22
|
+
if (maxCharacters <= 0) {
|
|
23
|
+
return ''
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (normalized.length <= maxCharacters) {
|
|
27
|
+
return normalized
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return `${normalized.slice(0, Math.max(0, maxCharacters - 1))}…`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const previewAgentMessage = (
|
|
34
|
+
message: AgentMessage,
|
|
35
|
+
options: PreviewSummaryMessageOptions = {}
|
|
36
|
+
) => {
|
|
37
|
+
const maxCharacters = options.maxCharacters ?? defaultSummaryPreviewMaxCharacters
|
|
38
|
+
|
|
39
|
+
switch (message._tag) {
|
|
40
|
+
case 'User':
|
|
41
|
+
return `User: ${truncateSummaryPreview(
|
|
42
|
+
contentText(message.content) || contentPreview(message.content),
|
|
43
|
+
maxCharacters
|
|
44
|
+
)}`
|
|
45
|
+
case 'Assistant':
|
|
46
|
+
return `Assistant: ${truncateSummaryPreview(
|
|
47
|
+
contentText(assistantContent(message)) || contentPreview(assistantContent(message)),
|
|
48
|
+
maxCharacters
|
|
49
|
+
)}`
|
|
50
|
+
case 'ToolResult':
|
|
51
|
+
return `Tool ${message.toolCallId}: ${truncateSummaryPreview(
|
|
52
|
+
contentText(message.content) || contentPreview(message.content),
|
|
53
|
+
maxCharacters
|
|
54
|
+
)}`
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const makePreviewSummaryContent = (
|
|
59
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
60
|
+
options: PreviewSummaryMessageOptions = {}
|
|
61
|
+
) =>
|
|
62
|
+
[
|
|
63
|
+
options.header ?? defaultPreviewSummaryHeader,
|
|
64
|
+
...messages.map(message => previewAgentMessage(message, options))
|
|
65
|
+
].join('\n')
|
|
66
|
+
|
|
67
|
+
export const makePreviewSummaryMessage = (
|
|
68
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
69
|
+
options: PreviewSummaryMessageOptions = {}
|
|
70
|
+
) => UserMessage.make({ content: makePreviewSummaryContent(messages, options) })
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Effect, Layer } from 'effect'
|
|
2
|
+
import type { AgentMessage } from '@yolk-sdk/agent/protocol'
|
|
3
|
+
import { ContextTransformer } from '@yolk-sdk/agent/loop'
|
|
4
|
+
import {
|
|
5
|
+
applyCompactionPlan,
|
|
6
|
+
planWindowCompaction,
|
|
7
|
+
type CompactionResult,
|
|
8
|
+
type SkippedCompactionPlan,
|
|
9
|
+
type SkippedCompactionResult
|
|
10
|
+
} from './window.ts'
|
|
11
|
+
import type { TranscriptTokenEstimator } from './estimator.ts'
|
|
12
|
+
|
|
13
|
+
export type SummaryMessageFactory = (
|
|
14
|
+
messages: ReadonlyArray<AgentMessage>
|
|
15
|
+
) => AgentMessage
|
|
16
|
+
|
|
17
|
+
export type WindowCompactionOptions = {
|
|
18
|
+
readonly strategy: string
|
|
19
|
+
readonly thresholdTokens: number
|
|
20
|
+
readonly tailMessageCount?: number
|
|
21
|
+
readonly minMessages?: number
|
|
22
|
+
readonly estimateTokens?: TranscriptTokenEstimator
|
|
23
|
+
readonly makeSummaryMessage: SummaryMessageFactory
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const skippedCompactionResult = (plan: SkippedCompactionPlan): SkippedCompactionResult => ({
|
|
27
|
+
_tag: 'Skipped',
|
|
28
|
+
reason: plan.reason,
|
|
29
|
+
messages: plan.messages,
|
|
30
|
+
events: [],
|
|
31
|
+
beforeTokens: plan.beforeTokens
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
export const compactWindowMessages = (
|
|
35
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
36
|
+
options: WindowCompactionOptions
|
|
37
|
+
): CompactionResult => {
|
|
38
|
+
const plan = planWindowCompaction(messages, options)
|
|
39
|
+
|
|
40
|
+
if (plan._tag === 'Skip') {
|
|
41
|
+
return skippedCompactionResult(plan)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return applyCompactionPlan(plan, {
|
|
45
|
+
strategy: options.strategy,
|
|
46
|
+
summaryMessage: options.makeSummaryMessage(plan.compactedMessages),
|
|
47
|
+
estimateTokens: options.estimateTokens
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const makeWindowCompactionTransformer = (options: WindowCompactionOptions) =>
|
|
52
|
+
Layer.succeed(
|
|
53
|
+
ContextTransformer,
|
|
54
|
+
ContextTransformer.of({
|
|
55
|
+
transform: messages => Effect.succeed(compactWindowMessages(messages, options))
|
|
56
|
+
})
|
|
57
|
+
)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CompactionEnd,
|
|
3
|
+
CompactionStart,
|
|
4
|
+
type AgentEvent,
|
|
5
|
+
type AgentMessage
|
|
6
|
+
} from '@yolk-sdk/agent/protocol'
|
|
7
|
+
import { estimateAgentMessagesTokens, type TranscriptTokenEstimator } from './estimator.ts'
|
|
8
|
+
|
|
9
|
+
export const defaultCompactionTailMessageCount = 16
|
|
10
|
+
export const defaultMinimumCompactionMessages = 2
|
|
11
|
+
|
|
12
|
+
export type CompactionSkipReason =
|
|
13
|
+
| 'too_few_messages'
|
|
14
|
+
| 'below_threshold'
|
|
15
|
+
| 'empty_window'
|
|
16
|
+
| 'not_smaller'
|
|
17
|
+
|
|
18
|
+
export type WindowCompactionPlanOptions = {
|
|
19
|
+
readonly thresholdTokens: number
|
|
20
|
+
readonly tailMessageCount?: number
|
|
21
|
+
readonly minMessages?: number
|
|
22
|
+
readonly estimateTokens?: TranscriptTokenEstimator
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type SkippedCompactionPlan = {
|
|
26
|
+
readonly _tag: 'Skip'
|
|
27
|
+
readonly reason: CompactionSkipReason
|
|
28
|
+
readonly messages: ReadonlyArray<AgentMessage>
|
|
29
|
+
readonly beforeTokens: number
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type CompactCompactionPlan = {
|
|
33
|
+
readonly _tag: 'Compact'
|
|
34
|
+
readonly messages: ReadonlyArray<AgentMessage>
|
|
35
|
+
readonly compactedMessages: ReadonlyArray<AgentMessage>
|
|
36
|
+
readonly recentMessages: ReadonlyArray<AgentMessage>
|
|
37
|
+
readonly tailStartIndex: number
|
|
38
|
+
readonly beforeTokens: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type WindowCompactionPlan = SkippedCompactionPlan | CompactCompactionPlan
|
|
42
|
+
|
|
43
|
+
export type SkippedCompactionResult = {
|
|
44
|
+
readonly _tag: 'Skipped'
|
|
45
|
+
readonly reason: CompactionSkipReason
|
|
46
|
+
readonly messages: ReadonlyArray<AgentMessage>
|
|
47
|
+
readonly events: ReadonlyArray<AgentEvent>
|
|
48
|
+
readonly beforeTokens: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type CompletedCompactionResult = {
|
|
52
|
+
readonly _tag: 'Compacted'
|
|
53
|
+
readonly strategy: string
|
|
54
|
+
readonly messages: ReadonlyArray<AgentMessage>
|
|
55
|
+
readonly events: ReadonlyArray<AgentEvent>
|
|
56
|
+
readonly beforeTokens: number
|
|
57
|
+
readonly afterTokens: number
|
|
58
|
+
readonly compactedMessages: ReadonlyArray<AgentMessage>
|
|
59
|
+
readonly recentMessages: ReadonlyArray<AgentMessage>
|
|
60
|
+
readonly summaryMessage: AgentMessage
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type CompactionResult = SkippedCompactionResult | CompletedCompactionResult
|
|
64
|
+
|
|
65
|
+
export type ApplyCompactionPlanOptions = {
|
|
66
|
+
readonly strategy: string
|
|
67
|
+
readonly summaryMessage: AgentMessage
|
|
68
|
+
readonly estimateTokens?: TranscriptTokenEstimator
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const estimator = (estimateTokens?: TranscriptTokenEstimator) =>
|
|
72
|
+
estimateTokens ?? estimateAgentMessagesTokens
|
|
73
|
+
|
|
74
|
+
const skippedCompactionResult = (
|
|
75
|
+
reason: CompactionSkipReason,
|
|
76
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
77
|
+
beforeTokens: number
|
|
78
|
+
): SkippedCompactionResult => ({
|
|
79
|
+
_tag: 'Skipped',
|
|
80
|
+
reason,
|
|
81
|
+
messages,
|
|
82
|
+
events: [],
|
|
83
|
+
beforeTokens
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
export const windowTailStartIndex = (
|
|
87
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
88
|
+
tailMessageCount: number
|
|
89
|
+
) => {
|
|
90
|
+
const tailCount = Math.min(Math.max(0, tailMessageCount), Math.max(0, messages.length - 1))
|
|
91
|
+
const initialIndex = Math.max(1, messages.length - tailCount)
|
|
92
|
+
let index = initialIndex
|
|
93
|
+
|
|
94
|
+
while (index > 1 && messages[index]?._tag === 'ToolResult') {
|
|
95
|
+
index -= 1
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return index
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const planWindowCompaction = (
|
|
102
|
+
messages: ReadonlyArray<AgentMessage>,
|
|
103
|
+
options: WindowCompactionPlanOptions
|
|
104
|
+
): WindowCompactionPlan => {
|
|
105
|
+
const estimateTokens = estimator(options.estimateTokens)
|
|
106
|
+
const beforeTokens = estimateTokens(messages)
|
|
107
|
+
const minMessages = options.minMessages ?? defaultMinimumCompactionMessages
|
|
108
|
+
|
|
109
|
+
if (messages.length <= minMessages) {
|
|
110
|
+
return { _tag: 'Skip', reason: 'too_few_messages', messages, beforeTokens }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (beforeTokens < options.thresholdTokens) {
|
|
114
|
+
return { _tag: 'Skip', reason: 'below_threshold', messages, beforeTokens }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const tailStart = windowTailStartIndex(
|
|
118
|
+
messages,
|
|
119
|
+
options.tailMessageCount ?? defaultCompactionTailMessageCount
|
|
120
|
+
)
|
|
121
|
+
const compactedMessages = messages.slice(0, tailStart)
|
|
122
|
+
const recentMessages = messages.slice(tailStart)
|
|
123
|
+
|
|
124
|
+
if (compactedMessages.length === 0 || recentMessages.length === 0) {
|
|
125
|
+
return { _tag: 'Skip', reason: 'empty_window', messages, beforeTokens }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
_tag: 'Compact',
|
|
130
|
+
messages,
|
|
131
|
+
compactedMessages,
|
|
132
|
+
recentMessages,
|
|
133
|
+
tailStartIndex: tailStart,
|
|
134
|
+
beforeTokens
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const applyCompactionPlan = (
|
|
139
|
+
plan: WindowCompactionPlan,
|
|
140
|
+
options: ApplyCompactionPlanOptions
|
|
141
|
+
): CompactionResult => {
|
|
142
|
+
if (plan._tag === 'Skip') {
|
|
143
|
+
return skippedCompactionResult(plan.reason, plan.messages, plan.beforeTokens)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const estimateTokens = estimator(options.estimateTokens)
|
|
147
|
+
const messages = [options.summaryMessage, ...plan.recentMessages]
|
|
148
|
+
const afterTokens = estimateTokens(messages)
|
|
149
|
+
|
|
150
|
+
if (afterTokens >= plan.beforeTokens) {
|
|
151
|
+
return skippedCompactionResult('not_smaller', plan.messages, plan.beforeTokens)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
_tag: 'Compacted',
|
|
156
|
+
strategy: options.strategy,
|
|
157
|
+
messages,
|
|
158
|
+
events: [
|
|
159
|
+
CompactionStart.make({ strategy: options.strategy }),
|
|
160
|
+
CompactionEnd.make({
|
|
161
|
+
strategy: options.strategy,
|
|
162
|
+
beforeTokens: plan.beforeTokens,
|
|
163
|
+
afterTokens
|
|
164
|
+
})
|
|
165
|
+
],
|
|
166
|
+
beforeTokens: plan.beforeTokens,
|
|
167
|
+
afterTokens,
|
|
168
|
+
compactedMessages: plan.compactedMessages,
|
|
169
|
+
recentMessages: plan.recentMessages,
|
|
170
|
+
summaryMessage: options.summaryMessage
|
|
171
|
+
}
|
|
172
|
+
}
|