mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
# Conflicts: # docs/agent-lifecycle.md # docs/architecture.md # docs/cookbook/extension-cookbook.i18n.yaml # docs/cordis-catalog/events.md # docs/cordis-catalog/services.md # docs/event-producer-consumer.md # docs/rfc/INDEX.md # docs/rfc/implemented/architecture/2026-07-05-reconstructable-requests.md # docs/rfc/implemented/architecture/2026-07-15-replay-token-meter-service.i18n.yaml # docs/rfc/implemented/architecture/2026-07-15-replay-token-meter-service.md # docs/rfc/implemented/architecture/2026-07-15-replay-token-meter-service.zh.md # docs/rfc/implemented/feature/2026-06-18-compaction-capability-seam.md # docs/rfc/implemented/feature/2026-07-07-session-prefix.md # packages/compact/compact-basic/README.md # packages/compact/compact-basic/src/config.ts # packages/compact/compact-basic/src/index.ts # packages/compact/compact-basic/src/summarizer.ts # packages/compact/compact-basic/tests/compact-basic.spec.ts # packages/compact/compact-basic/tests/compact-loop-repro.spec.ts # packages/compact/compact/README.md # packages/cordis/tool-cordis/src/api-catalog.ts # packages/core/agent-loop/src/loop.ts # packages/core/agent-loop/tests/cancel.spec.ts # packages/llm/llm-deepseek/src/adapter.ts # packages/llm/llm-pi-ai/README.md # packages/llm/llm-pi-ai/src/stream.ts # packages/llm/llm-pi-ai/tests/convert.spec.ts # packages/llm/llm/README.md # packages/llm/llm/src/index.ts # packages/llm/llm/tests/service.spec.ts # scripts/gen-doc-graphs.ts
112 lines
4.1 KiB
TypeScript
112 lines
4.1 KiB
TypeScript
/**
|
|
* Runtime defaulting and policy validation for compact-basic.
|
|
*
|
|
* @module @deepseek-ai/dsh-compact-basic/config
|
|
*/
|
|
|
|
import { deepFreeze } from '@deepseek-ai/dsh-llm'
|
|
import type { TokenMeterService } from '@deepseek-ai/dsh-token-meter'
|
|
import type { BasicCompactConfig, ResolvedConfig } from './types.ts'
|
|
|
|
/** Default request-pressure fraction of the token meter's context window. */
|
|
const DEFAULT_THRESHOLD_RATIO = 0.8
|
|
|
|
/** Default verbatim-tail fraction of the token meter's context window. */
|
|
const DEFAULT_RETAIN_RATIO = 0.16
|
|
|
|
/** Complete public configuration key set. */
|
|
const BASIC_COMPACT_CONFIG_KEYS: ReadonlySet<string> = new Set([
|
|
'thresholdRatio',
|
|
'retainTokens',
|
|
'summarizationProvider',
|
|
'summarizationModel',
|
|
'maxTokens',
|
|
'compactionRetries',
|
|
'maxOverflowRetries',
|
|
'auto',
|
|
])
|
|
|
|
/** Reject stale or misspelled keys before defaults can hide them. */
|
|
function validateConfigKeys(config: BasicCompactConfig): void {
|
|
for (const key of Object.keys(config)) {
|
|
if (!BASIC_COMPACT_CONFIG_KEYS.has(key)) {
|
|
throw new Error(
|
|
`BasicCompactConfig: unknown key "${key}" `
|
|
+ '(allowed: thresholdRatio, retainTokens, summarizationProvider, summarizationModel, '
|
|
+ 'maxTokens, compactionRetries, maxOverflowRetries, auto)',
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resolve defaults and validate the service-wide compaction policy.
|
|
* @param config - raw compact-basic configuration.
|
|
* @param tokenMeter - token meter supplying the context capacity.
|
|
* @returns a detached deeply immutable configuration.
|
|
*/
|
|
export function resolveConfig(
|
|
config: BasicCompactConfig = {},
|
|
tokenMeter: TokenMeterService,
|
|
): ResolvedConfig {
|
|
validateConfigKeys(config)
|
|
const thresholdRatio = config.thresholdRatio ?? DEFAULT_THRESHOLD_RATIO
|
|
const retainTokens = config.retainTokens
|
|
?? Math.floor(tokenMeter.contextWindow * DEFAULT_RETAIN_RATIO)
|
|
const resolved: ResolvedConfig = {
|
|
thresholdRatio,
|
|
retainTokens,
|
|
summarizationProvider: config.summarizationProvider ?? '',
|
|
summarizationModel: config.summarizationModel ?? '',
|
|
maxTokens: config.maxTokens ?? 8192,
|
|
compactionRetries: config.compactionRetries ?? 1,
|
|
maxOverflowRetries: config.maxOverflowRetries ?? 1,
|
|
auto: config.auto ?? true,
|
|
}
|
|
|
|
assertRatio('thresholdRatio', resolved.thresholdRatio)
|
|
assertNonNegativeInteger('retainTokens', resolved.retainTokens)
|
|
const thresholdTokens = Math.floor(tokenMeter.contextWindow * resolved.thresholdRatio)
|
|
if (resolved.retainTokens >= thresholdTokens) {
|
|
throw new Error(
|
|
`BasicCompactConfig: retainTokens (${resolved.retainTokens}) must be less than threshold tokens ${thresholdTokens}`,
|
|
)
|
|
}
|
|
assertPositiveInteger('maxTokens', resolved.maxTokens)
|
|
assertNonNegativeInteger('compactionRetries', resolved.compactionRetries)
|
|
assertNonNegativeInteger('maxOverflowRetries', resolved.maxOverflowRetries)
|
|
if (typeof resolved.summarizationProvider !== 'string') {
|
|
throw new Error('BasicCompactConfig: summarizationProvider must be a string')
|
|
}
|
|
if (typeof resolved.summarizationModel !== 'string') {
|
|
throw new Error('BasicCompactConfig: summarizationModel must be a string')
|
|
}
|
|
if ((resolved.summarizationProvider.length === 0) !== (resolved.summarizationModel.length === 0)) {
|
|
throw new Error(
|
|
'BasicCompactConfig: summarizationProvider and summarizationModel must both be set or both be empty',
|
|
)
|
|
}
|
|
if (typeof resolved.auto !== 'boolean') {
|
|
throw new Error('BasicCompactConfig: auto must be a boolean')
|
|
}
|
|
return deepFreeze(resolved)
|
|
}
|
|
|
|
function assertPositiveInteger(name: string, value: number): void {
|
|
if (!Number.isInteger(value) || value <= 0) {
|
|
throw new Error(`BasicCompactConfig: ${name} (${value}) must be a positive integer`)
|
|
}
|
|
}
|
|
|
|
function assertNonNegativeInteger(name: string, value: number): void {
|
|
if (!Number.isInteger(value) || value < 0) {
|
|
throw new Error(`BasicCompactConfig: ${name} (${value}) must be a non-negative integer`)
|
|
}
|
|
}
|
|
|
|
function assertRatio(name: string, value: number): void {
|
|
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0 || value > 1) {
|
|
throw new Error(`BasicCompactConfig: ${name} (${value}) must be a number in (0, 1]`)
|
|
}
|
|
}
|