mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
fix(agent-core): cap compaction output at 128k by default (#1156)
This commit is contained in:
parent
54baf5d07f
commit
794db55538
3 changed files with 61 additions and 2 deletions
5
.changeset/compaction-default-output-cap.md
Normal file
5
.changeset/compaction-default-output-cap.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
---
|
||||
|
||||
Cap compaction output at 128k tokens by default to avoid provider max_tokens errors.
|
||||
|
|
@ -39,6 +39,15 @@ import {
|
|||
|
||||
export const MAX_COMPACTION_RETRY_ATTEMPTS = 5;
|
||||
|
||||
/**
|
||||
* Default hard cap on compaction output tokens when `maxOutputSize` is not
|
||||
* configured on the model alias. Without this, compaction falls back to the
|
||||
* full context window size, which exceeds the `max_tokens` ceiling enforced
|
||||
* by many OpenAI-compatible providers. 128k matches the chat-completions
|
||||
* ceiling applied by the OpenAI Legacy provider.
|
||||
*/
|
||||
const DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS = 128 * 1024;
|
||||
|
||||
class CompactionTruncatedError extends Error {
|
||||
constructor() {
|
||||
super('Compaction response was truncated before producing a complete summary.');
|
||||
|
|
@ -261,13 +270,25 @@ export class FullCompaction {
|
|||
await this.triggerPreCompactHook(data, tokensBefore, signal);
|
||||
|
||||
const model = this.agent.config.model;
|
||||
const capability = this.agent.config.modelCapabilities;
|
||||
const maxContextTokens = capability.max_context_tokens;
|
||||
// When the model's context window is known and the user has not set
|
||||
// `maxOutputSize`, cap compaction output to a safe default so a large
|
||||
// context window does not push `max_tokens` past the provider's ceiling.
|
||||
// When the window is unknown (maxContextTokens === 0), leave
|
||||
// `maxOutputSize` unset so `resolveCompletionBudget` falls back to the
|
||||
// conservative unknown-context fallback.
|
||||
const defaultCompactionCap =
|
||||
maxContextTokens > 0
|
||||
? Math.min(maxContextTokens, DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS)
|
||||
: undefined;
|
||||
const provider = applyCompletionBudget({
|
||||
provider: this.agent.config.provider,
|
||||
budget: resolveCompletionBudget({
|
||||
maxOutputSize: this.agent.config.maxOutputSize,
|
||||
maxOutputSize: this.agent.config.maxOutputSize ?? defaultCompactionCap,
|
||||
reservedContextSize: this.agent.kimiConfig?.loopControl?.reservedContextSize,
|
||||
}),
|
||||
capability: this.agent.config.modelCapabilities,
|
||||
capability,
|
||||
});
|
||||
|
||||
const delays = retryBackoffDelays(MAX_COMPACTION_RETRY_ATTEMPTS);
|
||||
|
|
|
|||
|
|
@ -1811,6 +1811,39 @@ describe('FullCompaction', () => {
|
|||
expect(compactionMaxCompletionTokens).toEqual([384000]);
|
||||
});
|
||||
|
||||
it('uses default 128k hardCap when maxOutputSize is not configured', async () => {
|
||||
let callCount = 0;
|
||||
const compactionMaxCompletionTokens: unknown[] = [];
|
||||
const generate: GenerateFn = async (provider, _system, _tools, _history, callbacks) => {
|
||||
callCount += 1;
|
||||
if (callCount === 1) {
|
||||
throw new APIContextOverflowError(400, 'Context length exceeded', 'req-default-cap');
|
||||
}
|
||||
if (callCount === 2) {
|
||||
compactionMaxCompletionTokens.push(providerMaxCompletionTokens(provider));
|
||||
return textResult('Default cap compacted summary.');
|
||||
}
|
||||
await callbacks?.onMessagePart?.({
|
||||
type: 'text',
|
||||
text: 'Recovered with default cap.',
|
||||
});
|
||||
return textResult('Recovered with default cap.');
|
||||
};
|
||||
const ctx = testAgent({ generate });
|
||||
ctx.configure({
|
||||
provider: CATALOGUED_PROVIDER,
|
||||
modelCapabilities: CATALOGUED_MODEL_CAPABILITIES,
|
||||
});
|
||||
ctx.appendExchange(1, 'old user one', 'old assistant one', 20);
|
||||
ctx.newEvents();
|
||||
|
||||
await ctx.rpc.prompt({ input: [{ type: 'text', text: 'Retry with default cap' }] });
|
||||
await ctx.untilTurnEnd();
|
||||
|
||||
expect(callCount).toBe(3);
|
||||
expect(compactionMaxCompletionTokens).toEqual([128 * 1024]);
|
||||
});
|
||||
|
||||
it('ignores filtered assistant placeholders when checking the retained overflow suffix', async () => {
|
||||
let callCount = 0;
|
||||
const generate: GenerateFn = async (_provider, _system, _tools, _history, callbacks) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue