refactor(token-meter): make context occupancy durable projection state
Replace the transient `session/model-request` mux frame with ordinary durable session state. Occupancy now rides two last-wins projection fields instead of a non-replayable frame that needed removal tombstones and cross-stream fencing. The frame was the only non-replayable class on the mux stream. Because host and mux are independent SSE streams with no cross-stream order, a request emitted before a removal could arrive after `host/session-removed`, and a legitimate request for a new lifecycle reusing the same id could be fenced by a late removal. Fixing that needed a lifecycle generation on every frame; the frame itself was the problem. Removed: the `session/model-request` frame and schema, the `agent/model-request` core event, the ApiProxy measurement point, the client-side telemetry map and removal tombstone, and the synthetic `cancelled` open error used to signal reconnect through the error channel. Added: `request/context`, a log-only session event recording the registration-bound capacity of the route a request resolved to, appended beside `request/header` from the lookup that already prepared the call and skipped when the route is unchanged. Capacity stays out of `EpochHeader` because it is adapter metadata about a route, not an input the request was built from, so it must not join request reconstruction or header equality. The `contextPressure` projection pairs the newest provider-reported prompt size with the newest recorded capacity. The two are deliberately not one atomic request observation: switching models can pair a fresh capacity with the prior route's pressure until the next request reports usage. The figure is a user-facing reference, and this matches how the TUI status line has always computed occupancy.
This commit is contained in:
@@ -41,228 +41,6 @@ describe('instances', () => {
|
||||
expect(manager.get(S2).getSnapshot().pending).toEqual([])
|
||||
})
|
||||
|
||||
it('retains the latest transient request snapshot until lazy instantiation', () => {
|
||||
const api = new FakeApiClient()
|
||||
const manager = new SessionManager(api)
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'request-1' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'alpha',
|
||||
contextTokens: 12_000,
|
||||
contextWindow: 128_000,
|
||||
},
|
||||
})
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'request-2' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 2,
|
||||
provider: 'test',
|
||||
model: 'beta',
|
||||
contextTokens: 32_000,
|
||||
contextWindow: 256_000,
|
||||
},
|
||||
})
|
||||
|
||||
expect(manager.get(S1).getSnapshot().modelRequest).toEqual({
|
||||
turn: 1,
|
||||
step: 2,
|
||||
provider: 'test',
|
||||
model: 'beta',
|
||||
contextTokens: 32_000,
|
||||
contextWindow: 256_000,
|
||||
})
|
||||
})
|
||||
|
||||
it('retains whole-frame replacement before lazy instantiation', () => {
|
||||
const api = new FakeApiClient()
|
||||
const manager = new SessionManager(api)
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'request-with-capacity' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'alpha',
|
||||
contextWindow: 128_000,
|
||||
},
|
||||
})
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'request-without-capacity' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 2,
|
||||
provider: 'test',
|
||||
model: 'unknown-capacity',
|
||||
},
|
||||
})
|
||||
|
||||
expect(manager.get(S1).getSnapshot().modelRequest).toEqual({
|
||||
turn: 1,
|
||||
step: 2,
|
||||
provider: 'test',
|
||||
model: 'unknown-capacity',
|
||||
})
|
||||
})
|
||||
|
||||
it('clears retained request telemetry on subscribed and removal', () => {
|
||||
const api = new FakeApiClient()
|
||||
const manager = new SessionManager(api)
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'request-before-subscribe' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'alpha',
|
||||
contextWindow: 128_000,
|
||||
},
|
||||
})
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'subscribed' as never,
|
||||
payload: { type: 'session/subscribed', sessionId: S1, lastSeq: 0 },
|
||||
})
|
||||
const session = manager.get(S1)
|
||||
expect(session.getSnapshot().modelRequest).toBeNull()
|
||||
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'request-after-subscribe' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 2,
|
||||
provider: 'test',
|
||||
model: 'beta',
|
||||
contextWindow: 256_000,
|
||||
},
|
||||
})
|
||||
expect(session.getSnapshot().modelRequest?.contextWindow).toBe(256_000)
|
||||
manager.handleHostEnvelope({
|
||||
rpcId: 'removed' as never,
|
||||
payload: { type: 'host/session-removed', sessionId: S1 },
|
||||
})
|
||||
expect(session.getSnapshot().modelRequest).toBeNull()
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'late-resident-request' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 2,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'late',
|
||||
contextWindow: 512_000,
|
||||
},
|
||||
})
|
||||
expect(session.getSnapshot().modelRequest).toBeNull()
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'resumed-subscription' as never,
|
||||
payload: { type: 'session/subscribed', sessionId: S1, lastSeq: 0 },
|
||||
})
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'resumed-request' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'resumed',
|
||||
contextWindow: 256_000,
|
||||
},
|
||||
})
|
||||
expect(session.getSnapshot().modelRequest).toMatchObject({
|
||||
model: 'resumed',
|
||||
contextWindow: 256_000,
|
||||
})
|
||||
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'request-before-lazy-removal' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S2,
|
||||
turn: 1,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'gamma',
|
||||
contextWindow: 64_000,
|
||||
},
|
||||
})
|
||||
manager.handleHostEnvelope({
|
||||
rpcId: 'lazy-removed' as never,
|
||||
payload: { type: 'host/session-removed', sessionId: S2 },
|
||||
})
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'late-lazy-request' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S2,
|
||||
turn: 2,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'late-lazy',
|
||||
contextWindow: 512_000,
|
||||
},
|
||||
})
|
||||
expect(manager.get(S2).getSnapshot().modelRequest).toBeNull()
|
||||
})
|
||||
|
||||
it('clears resident and lazy request telemetry on disconnect', () => {
|
||||
const api = new FakeApiClient()
|
||||
const manager = new SessionManager(api)
|
||||
const session = manager.get(S1)
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'resident-request' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S1,
|
||||
turn: 1,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'resident',
|
||||
contextTokens: 35,
|
||||
contextWindow: 128_000,
|
||||
},
|
||||
})
|
||||
manager.handleMuxEnvelope({
|
||||
rpcId: 'lazy-request' as never,
|
||||
payload: {
|
||||
type: 'session/model-request',
|
||||
sessionId: S2,
|
||||
turn: 1,
|
||||
step: 1,
|
||||
provider: 'test',
|
||||
model: 'lazy',
|
||||
contextTokens: 70,
|
||||
contextWindow: 256_000,
|
||||
},
|
||||
})
|
||||
expect(session.getSnapshot().modelRequest).toMatchObject({
|
||||
model: 'resident',
|
||||
contextTokens: 35,
|
||||
contextWindow: 128_000,
|
||||
})
|
||||
|
||||
manager.handleDisconnected()
|
||||
|
||||
expect(session.getSnapshot().modelRequest).toBeNull()
|
||||
expect(manager.get(S2).getSnapshot().modelRequest).toBeNull()
|
||||
})
|
||||
|
||||
it('caps the pending buffer at 32 keeping the newest, and drops it on session-removed', () => {
|
||||
const api = new FakeApiClient()
|
||||
const manager = new SessionManager(api)
|
||||
|
||||
Reference in New Issue
Block a user