Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input
# Conflicts: # docs/architecture.i18n.yaml # docs/architecture.md # docs/architecture.zh.md # docs/config-catalog.md # docs/core-data-structures/core.i18n.yaml # docs/core-data-structures/llm-streaming.i18n.yaml # docs/module-graph.md # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # packages/README.i18n.yaml # packages/client/connection/src/client/fixture.ts # packages/client/connection/src/index.ts # packages/client/runtime/README.i18n.yaml # packages/client/runtime/README.md # packages/client/runtime/README.zh.md # packages/client/runtime/src/client/sessions/conversation.ts # packages/client/ui-conversation/README.i18n.yaml # packages/client/ui-conversation/src/client/apply.ts # packages/client/ui-conversation/src/client/chat/ChatView.tsx # packages/client/ui-conversation/src/client/chat/MessageItem.tsx # packages/client/ui-conversation/src/client/contract/slots.ts # packages/client/ui-trajectory/tests/views.spec.tsx # packages/compact/compact-basic/README.i18n.yaml # packages/cordis/tool-cordis/src/api-catalog.ts # packages/host/apiproxy/src/api-proxy.ts # packages/host/apiproxy/src/api/index.ts # packages/host/apiproxy/src/api/sessions.ts # packages/host/apiproxy/src/index.ts # packages/host/apiproxy/tests/fetch-carrier.spec.ts # packages/llm/llm-deepseek/src/adapter.ts # packages/llm/llm-deepseek/tests/adapter.spec.ts # packages/llm/llm-deepseek/tests/serialize.spec.ts # packages/llm/llm-pi-ai/README.i18n.yaml # packages/llm/llm-pi-ai/src/adapter.ts # packages/llm/llm-pi-ai/src/index.ts # packages/llm/llm-pi-ai/tests/adapter.spec.ts # packages/llm/llm/src/types.ts # packages/ui/tui/README.i18n.yaml # packages/ui/tui/src/index.ts # packages/ui/tui/tests/tui.spec.ts
This commit is contained in:
@@ -111,6 +111,8 @@ export class BasicCompactService extends CompactService {
|
||||
readonly config: ResolvedConfig
|
||||
|
||||
private readonly warnedPressureConfigTargets = new Set<string>()
|
||||
private readonly overflowRetries = new WeakMap<Agent, number>()
|
||||
private readonly overflowAgents = new WeakMap<Session, Agent>()
|
||||
|
||||
constructor(ctx: Context, config: BasicCompactConfig = {}) {
|
||||
super(ctx)
|
||||
@@ -119,8 +121,8 @@ export class BasicCompactService extends CompactService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the automatic post-step pressure and context-overflow recovery
|
||||
* listeners. `compactIfNeeded` stays dynamically dispatched so subclass
|
||||
* Register automatic between-step pressure and model-request overflow
|
||||
* recovery. `compactIfNeeded` stays dynamically dispatched so subclass
|
||||
* overrides are honored at event time.
|
||||
*/
|
||||
private _registerAutomaticCompaction(): void {
|
||||
@@ -133,7 +135,7 @@ export class BasicCompactService extends CompactService {
|
||||
)
|
||||
}
|
||||
|
||||
ctx.on('agent/post-step', async (
|
||||
ctx.on('agent/step', async (
|
||||
agent: Agent,
|
||||
_turn: number,
|
||||
_step: number,
|
||||
@@ -142,35 +144,47 @@ export class BasicCompactService extends CompactService {
|
||||
if (signal.aborted) return
|
||||
try {
|
||||
const result = await this.compactIfNeeded(agent, 'pressure', signal)
|
||||
if (result !== null) logResult(result, 'post-step pressure')
|
||||
if (result !== null) logResult(result, 'step pressure')
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof TargetPressureConfigError) {
|
||||
if (this.warnedPressureConfigTargets.has(error.targetKey)) return
|
||||
this.warnedPressureConfigTargets.add(error.targetKey)
|
||||
}
|
||||
const message = error instanceof Error ? error.message : String(error)
|
||||
ctx.logger.warn(`post-step compaction failed: ${message}; continuing the turn`)
|
||||
ctx.logger.warn(`step compaction failed: ${message}; continuing the turn`)
|
||||
}
|
||||
})
|
||||
|
||||
ctx.on('agent/settled', (agent) => {
|
||||
this.overflowRetries.delete(agent)
|
||||
})
|
||||
|
||||
// A successful response starts a fresh overflow-recovery sequence even
|
||||
// when tool calls continue the same turn into another request.
|
||||
ctx.on('session/event', (session, event) => {
|
||||
if (event.type !== 'assistant/message') return
|
||||
const agent = this.overflowAgents.get(session)
|
||||
if (agent !== undefined) this.overflowRetries.delete(agent)
|
||||
})
|
||||
|
||||
ctx.on('agent/request-error', async (
|
||||
agent,
|
||||
_turn,
|
||||
_step,
|
||||
_error,
|
||||
failure,
|
||||
priorFailures,
|
||||
_priorFailures,
|
||||
_retryPolicy,
|
||||
signal,
|
||||
next,
|
||||
) => {
|
||||
const priorOverflowFailures = priorFailures.filter(
|
||||
item => item.code === CONTEXT_WINDOW_EXCEEDED_CODE,
|
||||
).length
|
||||
if (failure.code !== CONTEXT_WINDOW_EXCEEDED_CODE || signal.aborted) return next()
|
||||
this.overflowAgents.set(agent.session, agent)
|
||||
const target = routedTarget(agent.session)
|
||||
if (target === undefined) return next()
|
||||
const policy = resolveTargetPolicy(this.config, target)
|
||||
if (priorOverflowFailures >= policy.maxOverflowRetries) return next()
|
||||
const retries = this.overflowRetries.get(agent) ?? 0
|
||||
if (retries >= policy.maxOverflowRetries) return next()
|
||||
|
||||
const generation = agent.session.surface.replaceGeneration
|
||||
let result: CompactionResult | null
|
||||
@@ -181,27 +195,29 @@ export class BasicCompactService extends CompactService {
|
||||
// A model-free prune can land before later summary work fails. That
|
||||
// durable reduction is sufficient retry proof; do not discard it just
|
||||
// because the optional second phase threw. Cancellation still wins.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- signal can abort while recovery is awaited.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the signal can abort while recovery is awaited.
|
||||
if (!signal.aborted && agent.session.surface.replaceGeneration > generation) {
|
||||
ctx.logger.warn(
|
||||
`context-overflow compaction failed after durable surface progress: ${message}; `
|
||||
+ 'retrying from the replacement surface',
|
||||
)
|
||||
return { action: 'retry' }
|
||||
this.overflowRetries.set(agent, retries + 1)
|
||||
return { kind: 'retry' }
|
||||
}
|
||||
ctx.logger.warn(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- signal can abort while recovery is awaited.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the signal can abort while recovery is awaited.
|
||||
`context-overflow compaction failed: ${message}; ${signal.aborted
|
||||
? 'cancellation prevents retry'
|
||||
: 'preserving the original request error'}`,
|
||||
)
|
||||
return next()
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- signal can abort while compaction is awaited.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- the signal can abort while compaction is awaited.
|
||||
if (signal.aborted
|
||||
|| agent.session.surface.replaceGeneration <= generation) return next()
|
||||
if (result !== null) logResult(result, 'context overflow recovery')
|
||||
return { action: 'retry' }
|
||||
this.overflowRetries.set(agent, retries + 1)
|
||||
return { kind: 'retry' }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -228,12 +244,12 @@ export class BasicCompactService extends CompactService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact for replayed post-step pressure or one provider-confirmed context
|
||||
* Compact for replayed step-boundary pressure or one provider-confirmed context
|
||||
* overflow. Both triggers price the latest durable routed request envelope;
|
||||
* overflow bypasses the normal threshold and retained-tail policy so it can
|
||||
* force one useful balanced reduction.
|
||||
* @param agent - agent whose latest durable routed request is measured.
|
||||
* @param trigger - normal post-step pressure or context-overflow recovery.
|
||||
* @param trigger - normal step-boundary pressure or context-overflow recovery.
|
||||
* @param signal - live turn cancellation signal forwarded to summarization.
|
||||
* @returns the latest summary compaction result, or `null` when no summary ran.
|
||||
*/
|
||||
@@ -272,7 +288,7 @@ export class BasicCompactService extends CompactService {
|
||||
return this.compactRegion(range.start, range.end, agent, signal)
|
||||
}
|
||||
|
||||
const context = await this.ctx.llm.resolveModelContext(target.provider, target.model)
|
||||
const context = (await this.ctx.llm.resolveModelInfo(target.provider, target.model, signal)).context
|
||||
const targetKey = `${target.provider}/${target.model}`
|
||||
if (context === undefined) {
|
||||
throw new TargetPressureConfigError(
|
||||
|
||||
@@ -177,10 +177,10 @@ export async function compactSurfaceRegion(
|
||||
|
||||
/**
|
||||
* Reconstruct the last routed request's cacheable prefix for the shadowed
|
||||
* region: its system prompt and tool schemas, then the request-only message
|
||||
* prefix followed by the region's own derived messages in surface order. The
|
||||
* summarizer appends only the compaction instruction after this, so the call
|
||||
* is a genuine prefix of the conversation and reuses the provider's KV cache.
|
||||
* region: its system prompt and tool schemas, then the region's own derived
|
||||
* messages in surface order. The summarizer appends only the compaction
|
||||
* instruction after this, so the call is a genuine prefix of the conversation
|
||||
* and reuses the provider's KV cache.
|
||||
* @param session - session supplying the request header and per-node projection.
|
||||
* @param shadowedSeqs - the surface-node seqs, in order, being compacted.
|
||||
* @returns the replayed conversation prefix to condense.
|
||||
@@ -199,7 +199,7 @@ function buildSummarizationInput(
|
||||
return {
|
||||
...header?.system === undefined ? {} : { system: header.system },
|
||||
...header?.tools === undefined ? {} : { tools: header.tools },
|
||||
messages: [...header?.messagePrefix ?? [], ...regionMessages],
|
||||
messages: regionMessages,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ export interface SummarizationInput {
|
||||
readonly system?: string
|
||||
/** The conversation's tool schemas, reused for prefix-cache alignment; absent when the request carried none. */
|
||||
readonly tools?: readonly ToolSchema[]
|
||||
/** The request prefix followed by the shadowed region, in surface order, that precedes the compaction instruction. */
|
||||
/** The shadowed region, in surface order, that precedes the compaction instruction. */
|
||||
readonly messages: readonly Message[]
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface ModelCompactPolicyConfig extends CompactPolicyConfig {
|
||||
export interface BasicCompactConfig extends CompactPolicyConfig {
|
||||
/** Exact provider/model overrides; duplicate targets fail plugin load. */
|
||||
modelPolicies?: ModelCompactPolicyConfig[]
|
||||
/** Enable automatic post-step pressure and overflow-recovery listeners. Defaults to `true`. */
|
||||
/** Enable automatic step-boundary pressure and overflow-recovery listeners. Defaults to `true`. */
|
||||
auto?: boolean
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user