mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 18:17:36 +00:00
155 lines
5.7 KiB
TypeScript
155 lines
5.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import type {
|
|
AgentSessionContextReport,
|
|
AgentSessionContextUsage,
|
|
AgentSessionContextWindow
|
|
} from './agent-session-context-usage'
|
|
import type { AgentJournalItemBody, AgentJournalRenderItem } from './agent-session-journal-types'
|
|
import {
|
|
latestStructuredAgentContextFacts,
|
|
selectStructuredAgentContextUsage
|
|
} from './structured-agent-session-context-usage'
|
|
|
|
function item(
|
|
itemId: string,
|
|
sequence: number,
|
|
body: AgentJournalItemBody
|
|
): AgentJournalRenderItem {
|
|
return { itemId, revision: 1, sequence, observedAt: sequence * 1_000, body }
|
|
}
|
|
|
|
const REPORT: AgentSessionContextReport = {
|
|
model: 'claude-fable-5-1',
|
|
usedTokens: 29_400,
|
|
windowTokens: 200_000,
|
|
percentage: 15,
|
|
autoCompactAtTokens: 167_000,
|
|
categories: [{ name: 'Messages', tokens: 10_200 }],
|
|
capturedAt: 5_000
|
|
}
|
|
|
|
const WINDOW: AgentSessionContextWindow = { tokens: 1_000_000, capturedAt: 2_000 }
|
|
|
|
function estimate(usedTokens: number): AgentSessionContextUsage['used'] {
|
|
return {
|
|
kind: 'estimate',
|
|
usage: {
|
|
inputTokens: usedTokens,
|
|
cacheCreationInputTokens: 0,
|
|
cacheReadInputTokens: 0,
|
|
outputTokens: 4
|
|
},
|
|
capturedAt: 1
|
|
}
|
|
}
|
|
|
|
function turn(sequence: number, contextUsage: AgentSessionContextUsage): AgentJournalRenderItem {
|
|
return item(`turn-${sequence}`, sequence, {
|
|
kind: 'turn',
|
|
turnId: `turn-${sequence}`,
|
|
state: 'completed',
|
|
contextUsage
|
|
})
|
|
}
|
|
|
|
describe('selectStructuredAgentContextUsage', () => {
|
|
it('orders by journal position, not by the clock on the fact', () => {
|
|
const late = { ...REPORT, capturedAt: 99_000 }
|
|
expect(
|
|
selectStructuredAgentContextUsage([
|
|
turn(1, { used: { kind: 'report', ...late }, window: WINDOW }),
|
|
turn(2, { used: estimate(54_617) })
|
|
])
|
|
).toMatchObject({ usedTokens: 54_617, windowTokens: 1_000_000, estimated: true })
|
|
})
|
|
|
|
it('divides by the newest window, not the one written beside the estimate', () => {
|
|
expect(
|
|
selectStructuredAgentContextUsage([
|
|
turn(1, { used: estimate(150_000), window: WINDOW }),
|
|
turn(2, { window: { tokens: 200_000, capturedAt: 3_000 } })
|
|
])
|
|
).toMatchObject({ usedTokens: 150_000, windowTokens: 200_000, percentage: 75 })
|
|
})
|
|
|
|
it('keeps the last turn with a count while a new turn has none yet', () => {
|
|
expect(
|
|
selectStructuredAgentContextUsage([
|
|
turn(1, { used: estimate(18_600), window: WINDOW }),
|
|
item('turn-2', 2, { kind: 'turn', turnId: 'turn-2', state: 'running' })
|
|
])
|
|
).toMatchObject({ usedTokens: 18_600 })
|
|
})
|
|
|
|
it('states nothing before the CLI has reported a window', () => {
|
|
expect(selectStructuredAgentContextUsage([turn(1, { used: estimate(18_600) })])).toBeNull()
|
|
})
|
|
|
|
it('hides the pre-compaction size until the next response or report restates it', () => {
|
|
const before = turn(1, { used: estimate(150_000), window: WINDOW })
|
|
const compacted = turn(2, { used: { kind: 'unknown', capturedAt: 3_000 } })
|
|
expect(selectStructuredAgentContextUsage([before, compacted])).toBeNull()
|
|
expect(
|
|
selectStructuredAgentContextUsage([before, compacted, turn(3, { used: estimate(12_000) })])
|
|
).toMatchObject({ usedTokens: 12_000, estimated: true })
|
|
expect(
|
|
selectStructuredAgentContextUsage([before, turn(2, { used: { kind: 'report', ...REPORT } })])
|
|
).toMatchObject({ usedTokens: 29_400, estimated: false })
|
|
})
|
|
|
|
it('states nothing for a used kind a newer host writes', () => {
|
|
const future = { kind: 'forecast', capturedAt: 4_000 }
|
|
expect(
|
|
selectStructuredAgentContextUsage([
|
|
turn(1, { used: estimate(18_600), window: WINDOW }),
|
|
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: a row from a newer host, which no local type admits.
|
|
turn(2, { used: future as unknown as AgentSessionContextUsage['used'] })
|
|
])
|
|
).toBeNull()
|
|
})
|
|
|
|
it('fills a part the loaded rows lack from the host whole-journal answer', () => {
|
|
const wholeJournal = { used: estimate(90_000), window: WINDOW }
|
|
expect(selectStructuredAgentContextUsage([], wholeJournal)).toMatchObject({
|
|
usedTokens: 90_000,
|
|
windowTokens: 1_000_000
|
|
})
|
|
// The window was written on a turn row older than the loaded page.
|
|
expect(
|
|
selectStructuredAgentContextUsage([turn(40, { used: estimate(120_000) })], wholeJournal)
|
|
).toMatchObject({ usedTokens: 120_000, windowTokens: 1_000_000, estimated: true })
|
|
})
|
|
|
|
it('prefers each part the loaded rows carry over the host answer', () => {
|
|
const wholeJournal = { used: estimate(90_000), window: { tokens: 200_000, capturedAt: 1 } }
|
|
expect(
|
|
selectStructuredAgentContextUsage(
|
|
[turn(40, { used: estimate(120_000), window: WINDOW })],
|
|
wholeJournal
|
|
)
|
|
).toMatchObject({ usedTokens: 120_000, windowTokens: 1_000_000 })
|
|
// An unknown size in the loaded rows is newer than any size the host holds for older rows.
|
|
expect(
|
|
selectStructuredAgentContextUsage(
|
|
[turn(40, { used: { kind: 'unknown', capturedAt: 3_000 } })],
|
|
wholeJournal
|
|
)
|
|
).toBeNull()
|
|
})
|
|
})
|
|
|
|
describe('latestStructuredAgentContextFacts', () => {
|
|
it('answers the same for rows in any order, as the host holds them', () => {
|
|
const rows = [
|
|
turn(1, { used: estimate(18_600), window: WINDOW }),
|
|
turn(2, { used: { kind: 'report', ...REPORT } }),
|
|
turn(3, { used: estimate(40_000) }),
|
|
item('u', 4, { kind: 'message', role: 'user', blocks: [{ type: 'text', text: 'hi' }] })
|
|
]
|
|
const facts = latestStructuredAgentContextFacts(rows)
|
|
expect(facts).toEqual({ used: estimate(40_000), window: WINDOW })
|
|
expect(latestStructuredAgentContextFacts(rows.toReversed())).toEqual(facts)
|
|
expect(latestStructuredAgentContextFacts([])).toEqual({})
|
|
})
|
|
})
|