fix(web): let the context meter see a compaction
The composer ring, percentage, and `~used / capacity` header read
`contextPressure.pressureTokens`, which moves only when a request reports
usage. Compaction reports none — compact-basic summarizes through a direct
`ctx.llm.stream()` call and appends only its own `compact/*` records plus the
replacement `user/message` — so the meter was frozen across the one action
taken to change it. Driving a real `compactNow` through the agent loop:
BEFORE compact: ring=4% header=~4227/100000 rows=[18, 0, 4365]
AFTER compact: ring=4% header=~4227/100000 rows=[18, 0, 286]
The composition rows fell 93%; the ring did not move, and would not until an
entire further turn completed. The panel then contradicted itself by more than
an order of magnitude at exactly the moment a reader opens it.
`contextPressure` now also publishes `projectedTokens`: the provider sample
plus the heuristic repricing of everything the surface gained or lost since
that sample, clamped at zero, folded through the shared `surface-fold.ts`. The
sample is stamped before the same event joins the surface, so an
`assistant/message` anchors against the surface its own request carried. Only
the delta is estimated, so the figure stays provider-anchored — the estimator's
CJK and JSON-schema underpricing stays out of the occupancy number — while
reacting the moment content lands or a span is shadowed. Same run after:
BEFORE compact: ring=4% header=~4323/100000 (pressure=4227, projected=4323)
AFTER compact: ring=0% header=~ 244/100000 (pressure=4227, projected= 244)
`contextOccupancy` prefers the projected figure and falls back to the bare
sample, so a projection restored from a pre-field checkpoint degrades to the
old behavior rather than disappearing. `stateVersion` moves to 3.
This commit is contained in:
@@ -20,14 +20,13 @@ export interface TokenUsageProjection {
|
||||
/**
|
||||
* Approximate context occupancy for a status display.
|
||||
*
|
||||
* The two fields, when present, are deliberately NOT one atomic request
|
||||
* observation: `pressureTokens` is the newest provider-reported prompt size,
|
||||
* `contextWindow` the newest recorded route capacity. Switching models can
|
||||
* therefore pair a fresh capacity with the previous route's pressure until the
|
||||
* next request reports usage. This is an intentional trade — the value is a
|
||||
* user-facing reference, not a billing or gating input — and it matches how
|
||||
* the TUI status line has always computed occupancy. See the token-meter
|
||||
* README for the full rationale.
|
||||
* The fields, when present, are deliberately NOT one atomic request
|
||||
* observation: each is a last-wins record of a different moment. Switching
|
||||
* models can therefore pair a fresh capacity with the previous route's
|
||||
* pressure until the next request reports usage. This is an intentional trade
|
||||
* — the value is a user-facing reference, not a billing or gating input — and
|
||||
* it matches how the TUI status line has always computed occupancy. See the
|
||||
* token-meter README for the full rationale.
|
||||
*/
|
||||
export interface ContextPressureProjection {
|
||||
/**
|
||||
@@ -36,6 +35,15 @@ export interface ContextPressureProjection {
|
||||
* grow as the current turn streams. Absent until a provider reports usage.
|
||||
*/
|
||||
pressureTokens?: number
|
||||
/**
|
||||
* What the NEXT request's prompt would cost: {@link pressureTokens} plus the
|
||||
* heuristic repricing of everything the surface gained or lost since that
|
||||
* sample. Only the delta is estimated, so the figure stays anchored to the
|
||||
* provider while still reacting the moment a compaction shadows a span —
|
||||
* which `pressureTokens` alone cannot do, since compaction reports no usage
|
||||
* of its own. Absent until a provider reports usage.
|
||||
*/
|
||||
projectedTokens?: number
|
||||
/** Newest recorded route capacity; absent when no adapter advertised one. */
|
||||
contextWindow?: number
|
||||
}
|
||||
@@ -43,10 +51,11 @@ export interface ContextPressureProjection {
|
||||
/**
|
||||
* Heuristic composition of the next request's context: what the prompt is
|
||||
* made of, not what it costs. All three figures use the meter's fixed
|
||||
* density estimate (they will not sum exactly to the provider-reported
|
||||
* `pressureTokens`, which is billing-grade and one request behind), and the
|
||||
* message figure tracks the live surface, so it moves as content is appended
|
||||
* or compacted while the provider number holds still.
|
||||
* density estimate, so they will not sum to the provider-anchored
|
||||
* `projectedTokens`: the estimator systematically underprices CJK text and
|
||||
* JSON schemas, which is exactly the error the anchoring in
|
||||
* {@link ContextPressureProjection.projectedTokens} keeps out of the occupancy
|
||||
* figure. Present these as approximations of composition, never as a total.
|
||||
*/
|
||||
export interface ContextBreakdownProjection {
|
||||
/** Heuristic tokens of the newest request envelope's system prompt; 0 before any request. */
|
||||
|
||||
Reference in New Issue
Block a user