test: align retention checks and catalogs with token budgets

This commit is contained in:
creatixchu committed 2026-09-21 16:50:35 +08:00
1 parent ab102138c8
commit c4c18ef0d4
16 files changed
+32 -23

No files matched your search

+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/persistence-catalog.md
persistence-catalog.md: 45fe3d7549b98a1c199e3bba1565fbf2c11b3620
persistence-catalog.zh.md: f55524fdf2f0c4dc3167a89249be8e703bfca7c3
persistence-catalog.md: d8d681934326b55ae743e71eb315582af2190104
persistence-catalog.zh.md: 59c56b8e949d245c5ae95d59bf239f3a8069c932
+1 -1
View File
@@ -4929,7 +4929,7 @@ Sources: [`packages/llm/llm/src/types.ts:127`](../packages/llm/llm/src/types.ts)
SHA-256: `3cfc3a56502da1f8c6153c2c56657bab4b749056968634dd0991d325ec1c919f`
Sources: [`packages/core/tools/src/index.ts:689`](../packages/core/tools/src/index.ts)
Sources: [`packages/core/tools/src/index.ts:699`](../packages/core/tools/src/index.ts)
| Property | Presence | Type |
|---|---|---|
+1 -1
View File
@@ -4931,7 +4931,7 @@ SHA-256: `43e311235aa537df1bacdd92d144df8a952337a76e9ac892cdf670f1a797bad8`
SHA-256: `3cfc3a56502da1f8c6153c2c56657bab4b749056968634dd0991d325ec1c919f`
来源:[`packages/core/tools/src/index.ts:689`](../packages/core/tools/src/index.ts)
来源:[`packages/core/tools/src/index.ts:699`](../packages/core/tools/src/index.ts)
| 属性 | 存在性 | 类型 |
|---|---|---|
+1 -1
View File
@@ -36275,7 +36275,7 @@
"packages/core/tools/src/index.ts#ToolRestriction"
],
"sources": [
"packages/core/tools/src/index.ts:689"
"packages/core/tools/src/index.ts:699"
]
},
{
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/tool-execution-pipeline.md
tool-execution-pipeline.md: c0abeb8e8de3710cd718b1136af480395ca64101
tool-execution-pipeline.zh.md: 01816ca1b68b95e4789dffa00fa3c3c0d2881cdc
tool-execution-pipeline.md: 5e9b2079e1a5dde2a0423fdf6d7743e0ccbcc1e0
tool-execution-pipeline.zh.md: 34bb84da5d5ecf2883c25220360cc50d2a1a105a
+1 -1
View File
@@ -3,7 +3,7 @@
# Tool Execution Pipeline
This graph shows where policy, hooks, sandboxing, filesystem guards, result rewriting, final-outcome observation, and UI rendering run without changing the loop. The `tools/pre-execute` waterfall runs first, monotonic guards run next, and the `tools/execute` and `tools/post-execute` waterfalls follow; the three waterfalls may transform a call. Definition-owned `finalizeContent` and `tools/result` run afterward.
This graph shows where policy, hooks, sandboxing, filesystem guards, result rewriting, final-outcome observation, and UI rendering run without changing the loop. The `tools/pre-execute` waterfall runs first, monotonic guards run next, and the `tools/execute` and `tools/post-execute` waterfalls follow; the three waterfalls may transform a call. Definition-owned `projectContent` installs prepared content before post-execute; `finalizeContent` and `tools/result` run afterward.
```mermaid
flowchart TD
+1 -1
View File
@@ -5,7 +5,7 @@
[English](tool-execution-pipeline.md) | 中文
此图展示策略、钩子、沙箱、文件系统守卫、结果重写、最终结果观察和 UI 渲染在不改变循环的情况下何时运行。`tools/pre-execute` waterfall(瀑布式事件)首先运行,随后是单调守卫,然后运行 `tools/execute` 和 `tools/post-execute` waterfall;这三个 waterfall 可以改写一次调用。由定义自身控制的 `finalizeContent` 和 `tools/result` 在此之后运行。
此图展示策略、钩子、沙箱、文件系统守卫、结果重写、最终结果观察和 UI 渲染在不改变循环的情况下何时运行。`tools/pre-execute` waterfall(瀑布式事件)首先运行,随后是单调守卫,然后运行 `tools/execute` 和 `tools/post-execute` waterfall;这三个 waterfall 可以改写一次调用。由定义自身控制的 `projectContent` 在执行后策略之前安装已准备内容;`finalizeContent` 和 `tools/result` 在此之后运行。
```mermaid
flowchart TD
@@ -7068,7 +7068,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'ToolDefinition',
declaration: 'export interface ToolDefinition extends ToolSchema {\n readonly output: ToolOutputDefinition;\n execute(args: unknown, exec: ToolRunContext): Promise<unknown>;\n finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;\n timeoutMs?: number;\n isConcurrencySafe?(args: unknown): boolean;\n presentCall?(args: unknown): ToolCallView | undefined;\n presentResult?(args: unknown, result: ToolResult): ToolResultView | undefined;\n}',
declaration: 'export interface ToolDefinition extends ToolSchema {\n readonly output: ToolOutputDefinition;\n execute(args: unknown, exec: ToolRunContext): Promise<unknown>;\n projectContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;\n finalizeContent?(exec: Readonly<ToolExecution>, result: Readonly<ToolExecutionResult>): ContentBlock[] | undefined;\n timeoutMs?: number;\n isConcurrencySafe?(args: unknown): boolean;\n presentCall?(args: unknown): ToolCallView | undefined;\n presentResult?(args: unknown, result: ToolResult): ToolResultView | undefined;\n}',
},
{
name: 'ToolDispatchExecution',
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/llm/token-meter/README.md
README.md: 268f75f1acda25440e08207e4ab13033ec397f5f
README.zh.md: fca37db43282b54a42666583824f215d5de8e930
README.md: e6d957bd14560d967fb221b147d87d7db42197ea
README.zh.md: 526be7b2e399bcf6f4638ece9a7315044fa48c55
+2 -2
View File
@@ -11,8 +11,6 @@ English | [中文](README.zh.md)
Use `ctx.tokenMeter` to estimate a session's current request and context pressure or price one message. Measurements replay the durable session log, remain deterministic, and make no model calls, so compaction, occupancy displays, and telemetry can share one result. When session projections are available, consumers can read `tokenUsage`, `contextPressure`, and `contextBreakdown`; text and routes without image pricing use an approximate fixed heuristic, declared visual-token pricing applies when available, and files are priced as model-visible handle text. Provider-reported usage is reused only for an identical request envelope; the package adds no model-visible content and makes no loop decisions.
`./estimate` exports the stateless text/content estimators used by tool-result retention. Model adapters supply request-image costs through `imageRequestPricing`.
## Table of Contents
- [Use this package](#use-this-package)
@@ -104,6 +102,8 @@ Each `measure()` call synchronizes the fold to the current durable tail, then re
`contextBreakdown` retains plain-JSON `{ seq, heuristicTokens, system }` entries in surface order and reuses the measurement plan/commit fold. Its state and surface transitions are O(current retained surface), not O(1) and not O(total historical log); replaced entries and message bodies are not retained. State version 5 invalidates checkpoints priced with offload metadata. `contextPressure` remains the scalar shadow-price consumer: replacements without adjacent claims contribute zero delta. The usage fold retains one last-sample slot because legal logs never report usage for an earlier step after a later step reports usage.
`./estimate` exports the stateless text/content estimators used by tool-result retention. Model adapters supply request-image costs through `imageRequestPricing`.
</details>
-----
+2 -2
View File
@@ -11,8 +11,6 @@ kind: "package-reference"
使用 `ctx.tokenMeter` 估算会话当前的请求与上下文压力,或为单条消息计价。测量会回放持久会话日志,结果确定且不进行模型调用,因此压缩、占用显示与遥测可以共享同一结果。会话投影可用时,消费方可以读取 `tokenUsage`、`contextPressure` 与 `contextBreakdown`;文本和没有图片定价的路由采用近似的固定启发式规则,存在声明时应用视觉 token 定价,文件则按模型可见的句柄文本计价。只有请求 envelope 完全相同时才复用提供方报告的用量;本包不添加模型可见内容,也不在 loop 中做决策。
`./estimate` 导出无服务状态的文字与内容估算函数,供工具结果保留复用。图片的实际请求成本由模型适配器的 `imageRequestPricing` 提供。
## 目录
- [使用本包](#use-this-package)
@@ -104,6 +102,8 @@ const price = ctx.tokenMeter.estimateMessage(message)
`contextBreakdown` 按 surface 顺序保留纯 JSON 的 `{ seq, heuristicTokens, system }` 条目,并复用测量服务的 plan/commit fold。其状态与 surface 转换成本为 O(当前保留 surface),不是 O(1),也不是 O(完整历史日志);被替换条目和消息正文不保留。状态版本 5 使计入省略元数据的检查点失效。`contextPressure` 仍是标量影子价消费方:没有相邻 claim 的替换贡献零增量。用量 fold 保留一个最后样本槽,因为合法日志不会在更晚步骤报告用量后再次报告更早步骤的用量。
`./estimate` 导出无服务状态的文字与内容估算函数,供工具结果保留复用。图片的实际请求成本由模型适配器的 `imageRequestPricing` 提供。
</details>
-----
@@ -698,7 +698,7 @@ describe('tool execution', () => {
})
expect(result.isError).toBe(false)
expect(result.content.map(block => block.type)).toEqual(['text', 'image'])
expect(textAt(result.content)).toBe(blocks[0].text?.slice(0, 20))
expect(textAt(result.content)).toBe('long text'.repeat(1000).slice(0, 20))
if (result.isError) throw new Error('expected MCP success')
expect(result.value).toEqual({ content: blocks })
} finally {
+2 -1
View File
@@ -53,6 +53,7 @@
"@deepseek-ai/dsh-web-fetch-http": "workspace:^",
"@deepseek-ai/dsh-web-search-exa": "workspace:^",
"@types/turndown": "^5.0.6",
"@deepseek-ai/dsh-scope": "workspace:^"
"@deepseek-ai/dsh-scope": "workspace:^",
"@deepseek-ai/dsh-token-meter": "workspace:^"
}
}
+5 -3
View File
@@ -14,6 +14,7 @@ import { mkdtempSync, readFileSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { Context } from '@deepseek-ai/cordis'
import { estimateContent } from '@deepseek-ai/dsh-token-meter/estimate'
import { ToolCallId } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
@@ -37,7 +38,7 @@ let spillRoot: string
let ctx: Context
const BODY = 'X'.repeat(4000) // formatted result is well over the policy cap
const MAX_INLINE_BYTES = 1000 // leaves room for a head/tail preview beside the notice
const MAX_INLINE_TOKENS = 250 // leaves room for a head/tail preview beside the notice
beforeEach(async () => {
vi.spyOn(publicHttpNetwork, 'resolve').mockResolvedValue([{ address: '127.0.0.1', family: 4 }])
@@ -55,12 +56,13 @@ beforeEach(async () => {
// policy cap is what triggers the spill (the Agent Note's separation of concerns).
await ctx.plugin(WebFetchLocal, { maxBodyChars: 500_000 })
await ctx.plugin(LocalSpillStore, { root: spillRoot })
await ctx.plugin(SpillPolicy, { maxInlineTokens: MAX_INLINE_BYTES })
await ctx.plugin(SpillPolicy, { maxInlineTokens: MAX_INLINE_TOKENS })
await ctx.plugin(ToolWeb)
})
afterEach(async () => {
vi.restoreAllMocks()
await ctx.fiber.dispose()
await new Promise<void>(resolve => server.close(() => { resolve() }))
rmSync(spillRoot, { recursive: true, force: true })
})
@@ -80,7 +82,7 @@ describe('web_fetch spill showcase', () => {
// Model-facing text is a preview + notice within the cap, NOT the full body.
expect(text.length).toBeLessThan(BODY.length)
expect(Buffer.byteLength(text, 'utf8')).toBeLessThanOrEqual(MAX_INLINE_BYTES)
expect(estimateContent([{ type: 'text', text }])).toBeLessThanOrEqual(MAX_INLINE_TOKENS)
expect(text).toContain(`Fetched ${base}`) // the head of the formatted result survives
expect(text).toContain('Full formatted result stored at:')
expect(text).toContain('Use read with offset/limit, or grep this path')
+3
View File
@@ -12352,6 +12352,9 @@ importers:
'@deepseek-ai/dsh-system-prompt':
specifier: workspace:^
version: link:../../core/system-prompt
'@deepseek-ai/dsh-token-meter':
specifier: workspace:^
version: link:../../llm/token-meter
'@deepseek-ai/dsh-tool-call-timeout-policy':
specifier: workspace:^
version: link:../../guard/timeout-policy
+5 -2
View File
@@ -1511,7 +1511,7 @@ function renderToolPipeline(): string {
const maintenance = 'curated Mermaid flow; exact tool schemas and event signatures live in generated catalogs'
return [
...generatedHeader('Tool Execution Pipeline'),
'This graph shows where policy, hooks, sandboxing, filesystem guards, result rewriting, final-outcome observation, and UI rendering run without changing the loop. The `tools/pre-execute` waterfall runs first, monotonic guards run next, and the `tools/execute` and `tools/post-execute` waterfalls follow; the three waterfalls may transform a call. Definition-owned `finalizeContent` and `tools/result` run afterward.',
'This graph shows where policy, hooks, sandboxing, filesystem guards, result rewriting, final-outcome observation, and UI rendering run without changing the loop. The `tools/pre-execute` waterfall runs first, monotonic guards run next, and the `tools/execute` and `tools/post-execute` waterfalls follow; the three waterfalls may transform a call. Definition-owned `projectContent` installs prepared content before post-execute; `finalizeContent` and `tools/result` run afterward.',
'',
'```mermaid',
'flowchart TD',
@@ -1526,6 +1526,7 @@ function renderToolPipeline(): string {
' toolBody["Registered tool execute() body"]',
` fsGate["${mermaidCode('fs/write-intent')} or ${mermaidCode('fs/edit-intent')}<br/>tool-fs mutations only"]`,
` owned["Tool-owned session events<br/>${mermaidCode('todo/write')}, ${mermaidCode('fs/observed')}, ${mermaidCode('hook/invoked')}, ${mermaidCode('hook/result')}, ${mermaidCode('tool/ptc-dispatch')}"]`,
' project["ToolDefinition.projectContent<br/>execution-prepared text and images"]',
` post["${mermaidCode('tools/post-execute')} waterfall<br/>accept, block, replace, add context"]`,
' normalized["Registry outer normalization<br/>pipeline/result snapshot throws become isError"]',
' finalize["ToolDefinition.finalizeContent<br/>last content-only invariant"]',
@@ -1553,7 +1554,9 @@ function renderToolPipeline(): string {
' fsGate --> toolBody',
' toolBody --> owned',
' toolBody --> around',
' around --> post',
' around --> project',
' project --> post',
' project -.->|throw| normalized',
' around -.->|wrapper throws| normalized',
' post -.->|throw| normalized',
' post --> finalize',