mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fix(compaction): 摘要收缩改按路由价并补齐定价访问路径
ds-review-bot 首轮意见修复: - 摘要收缩比较改用所选节点的路由价 shadowedRouteTokenCount,修复图片消息启发式价低于带框摘要时压缩被误拒;日志影子价仍为启发式 - DeepSeek 定价经序列化器同一套 access 解析构建句柄与占位文本,消除逐图数十 token 的低估;uncatalogued 分支 JSDoc 指明复现 projectImagesForTextModel 替换 - llm-replay 在加载时拒绝纯文本模型上的 imageRequestTokens 声明 - contextBreakdown 的 README 与 JSDoc 改为等于 heuristicTokens 之和,不再声称等于路由价 surfaceTokens
This commit is contained in:
@@ -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/compaction/compaction-basic/README.md
|
||||
README.md: b83b7a4ebafdf329fb91bc2c0f9f353f12c4a360
|
||||
README.zh.md: 33bedb6cb76283eeed31c38c1a36f2129fa98660
|
||||
README.md: e45228080db414c503420d22f5faca3daf1d3966
|
||||
README.zh.md: 1ff7bede73f36ec81d1dba0f2b414736ade09457
|
||||
|
||||
@@ -10,7 +10,7 @@ This package owns the Service Provider role of the compaction capability — see
|
||||
|
||||
This backend owns the compaction policy:
|
||||
|
||||
- **Measurement** — the singleton `ctx.tokenMeter` prices the latest canonical logged envelope and current surface at one consumed-log revision, under the routed model's declared request-image pricing when its adapter declares one. Step-boundary pressure therefore includes the actual system prompt, tools, routing, assistant completion, tool results, buffered context, steering, and route-priced image history; trigger, recent-tail retention, and range selection all read the same per-node prices, while the logged shadow price of a replaced range stays on the route-independent fixed heuristic so pure projection folds remain consistent.
|
||||
- **Measurement** — the singleton `ctx.tokenMeter` prices the latest canonical logged envelope and current surface at one consumed-log revision, under the routed model's declared request-image pricing when its adapter declares one. Step-boundary pressure therefore includes the actual system prompt, tools, routing, assistant completion, tool results, buffered context, steering, and route-priced image history; trigger, recent-tail retention, range selection, and the summary-shrink comparison all read the same route-priced per-node figures, while the logged shadow price of a replaced range stays on the route-independent fixed heuristic so pure projection folds remain consistent.
|
||||
- **Routed policy** — proactive pressure resolves capacity from the adapter that owns the latest durable provider/model route, then scales the default policy plus an optional exact-target override into concrete token budgets. Model discovery remains advisory and is not consulted.
|
||||
- **Model-free pruning** — after pressure or canonical overflow qualifies, the optional [`ctx.toolResultPruner`](../compaction-tool-result-pruner/README.md) service rewrites oversized tool results before range selection. Compact-basic remeasures through `ctx.tokenMeter`, skips summarization when pressure becomes safe, and otherwise summarizes the pruned surface. Below-pressure step checks never prune.
|
||||
- **Retention** — compact the oldest whole surface units while preserving a recent tail and balanced tool-call/result cuts through the [`dsh-compaction` boundary helpers](../compaction/README.md#tool-pairing-boundaries). Turn boundaries do not protect old steps inside a runaway turn. An open indivisible tail declines until it closes. The optional pruner can repair an oversized closed tool unit when its text-bearing result is the removable bulk; indivisible non-tool units and non-prunable tool remainders remain out of scope.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
该后端拥有压缩策略:
|
||||
|
||||
- **测量**:单例 `ctx.tokenMeter` 会在同一个已消费日志 revision 上,计量最新一份规范化已记录 envelope 与当前表层的 token 用量;当路由模型的适配器声明了请求图片定价时,按该定价计量。因此,步骤边界的压力计量会包含实际系统提示词、工具、路由、assistant 完成、工具结果、缓冲上下文、steering(中途引导)与按路由定价的图片历史;触发、近期尾部保留与范围选择读取同一套逐节点价格,而被替换范围记录的影子价保持在与路由无关的固定启发式规则上,使纯投影 fold 保持一致。
|
||||
- **测量**:单例 `ctx.tokenMeter` 会在同一个已消费日志 revision 上,计量最新一份规范化已记录 envelope 与当前表层的 token 用量;当路由模型的适配器声明了请求图片定价时,按该定价计量。因此,步骤边界的压力计量会包含实际系统提示词、工具、路由、assistant 完成、工具结果、缓冲上下文、steering(中途引导)与按路由定价的图片历史;触发、近期尾部保留、范围选择与摘要收缩比较读取同一套路由定价的逐节点数字,而被替换范围记录的影子价保持在与路由无关的固定启发式规则上,使纯投影 fold 保持一致。
|
||||
- **路由策略**:主动压力从拥有最新持久提供方/模型路由的适配器解析容量,再将默认策略与可选的精确目标覆盖缩放为具体 token 预算。模型发现仍仅供参考,不参与此处的策略解析。
|
||||
- **不依赖模型的剪枝**:在压力或规范溢出符合条件后,可选的 [`ctx.toolResultPruner`](../compaction-tool-result-pruner/README.zh.md) 服务会在选择范围之前改写超大工具结果。Compact-basic 通过 `ctx.tokenMeter` 重新测量;如果压力已回到安全范围,就跳过摘要,否则对已剪枝的表层进行摘要。低于压力的步骤检查绝不剪枝。
|
||||
- **保留**:压缩最旧的完整表层单元,同时保留近期尾部,并通过 [`dsh-compaction` 边界 helper](../compaction/README.zh.md#tool-pairing-boundaries) 将切分点调整到工具调用/结果配对平衡的位置。轮次边界不会保护失控轮次内的旧步骤。尚未闭合且不可分的尾部会在闭合前拒绝压缩。当闭合的超大工具单元以文本型结果为可移除主体时,可选 pruner 可以修复它;不可分的非工具单元与不可剪枝的工具剩余部分不在范围内。
|
||||
|
||||
@@ -43,6 +43,8 @@ interface PreparedCompaction extends SurfaceSelection {
|
||||
readonly measurement: TokenMeasurement
|
||||
readonly selectedNodes: TokenMeasurement['nodes']
|
||||
readonly shadowedTokenCount: number
|
||||
/** Route-priced total of the selected span; the shrink comparison's unit. */
|
||||
readonly shadowedRouteTokenCount: number
|
||||
readonly input: SummarizationInput
|
||||
}
|
||||
|
||||
@@ -353,8 +355,10 @@ function prepareCompaction(
|
||||
selectedNodes,
|
||||
// The shadow-price protocol prices replacements with the fixed heuristic
|
||||
// so the O(1) projection fold stays in agreement with its own appends;
|
||||
// retention and range selection read the route-priced `tokens` instead.
|
||||
// retention, range selection, and the shrink comparison read the
|
||||
// route-priced `tokens` instead.
|
||||
shadowedTokenCount: selectedNodes.reduce((total, node) => total + node.heuristicTokens, 0),
|
||||
shadowedRouteTokenCount: selectedNodes.reduce((total, node) => total + node.tokens, 0),
|
||||
input: buildSummarizationInput(session, selection.shadowedSeqs),
|
||||
}
|
||||
}
|
||||
@@ -373,10 +377,13 @@ async function summarizeCompaction(
|
||||
content: frameSummary(summaryResult.summary),
|
||||
source: compactCheckpointSource(compactionId, sourceCommandId),
|
||||
})
|
||||
// The checkpoint is text-only, so its fixed-heuristic price IS its route
|
||||
// price; comparing it against the span's route price asks the real
|
||||
// question — does the replacement lower the next request's pressure.
|
||||
const framedSummaryTokenCount = dependencies.meter.estimateMessage(checkpointMessage)
|
||||
if (framedSummaryTokenCount >= prepared.shadowedTokenCount) {
|
||||
if (framedSummaryTokenCount >= prepared.shadowedRouteTokenCount) {
|
||||
throw new Error(
|
||||
`summary is not smaller than the shadowed content (${framedSummaryTokenCount} estimated framed tokens >= ${prepared.shadowedTokenCount})`,
|
||||
`summary is not smaller than the shadowed content (${framedSummaryTokenCount} estimated framed tokens >= ${prepared.shadowedRouteTokenCount})`,
|
||||
)
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AttachmentId } from '@deepseek-ai/dsh-attachment'
|
||||
import BasicCompactionEngine from '@deepseek-ai/dsh-compaction-basic'
|
||||
import type { BasicCompactionConfig } from '@deepseek-ai/dsh-compaction-basic'
|
||||
import { selectCompactableRange } from '@deepseek-ai/dsh-compaction-basic/src/region.ts'
|
||||
import { frameSummary } from '@deepseek-ai/dsh-compaction-basic/src/summarizer.ts'
|
||||
import type { SummarizationInput, SummaryResult } from '@deepseek-ai/dsh-compaction-basic/src/summarizer.ts'
|
||||
import { CompactionId, toolPairingBalancedAfter, toolPairingBalancedBefore } from '@deepseek-ai/dsh-compaction'
|
||||
import {
|
||||
@@ -1966,6 +1967,33 @@ describe('route-priced image pressure', () => {
|
||||
expect(range).not.toBeNull()
|
||||
})
|
||||
|
||||
it('accepts a summary larger than the span heuristic when the route price shrinks', async () => {
|
||||
// A single short image message prices below a framed summary under the
|
||||
// fixed heuristic but far above it under the route: the shrink comparison
|
||||
// must ask whether the replacement lowers route pressure.
|
||||
const ctx = pricedContext(1_000)
|
||||
const session = imageConversation(1)
|
||||
const before = ctx.tokenMeter.measure(session)
|
||||
const imageNode = before.nodes[0]!
|
||||
const compact = new TestCompactionEngine(ctx, { auto: false })
|
||||
compact.summary = [{
|
||||
type: 'text',
|
||||
text: 'summary text sized between the heuristic and route prices of the shadowed image message, '
|
||||
+ 'long enough that the fixed heuristic alone would reject it as not smaller '
|
||||
+ 'while the route-priced comparison accepts the pressure reduction.',
|
||||
}]
|
||||
const framed = ctx.tokenMeter.estimateMessage(createUserMessage({
|
||||
content: frameSummary(compact.summary),
|
||||
source: { kind: 'plugin', plugin: 'test' },
|
||||
}))
|
||||
expect(framed).toBeGreaterThan(imageNode.heuristicTokens)
|
||||
expect(framed).toBeLessThan(imageNode.tokens)
|
||||
|
||||
const result = await compact.compactRegion(imageNode.seq, imageNode.seq, agent(session), SIGNAL)
|
||||
expect(result.shadowedSeqs).toEqual([imageNode.seq])
|
||||
expect(result.shadowedTokenCount).toBe(imageNode.heuristicTokens)
|
||||
})
|
||||
|
||||
it('triggers pressure compaction from routed visual tokens and logs heuristic shadow prices', async () => {
|
||||
const ctx = pricedContext(1_000)
|
||||
const session = imageConversation()
|
||||
|
||||
Reference in New Issue
Block a user