mirror of
https://github.com/stablyai/orca.git
synced 2026-11-04 19:17:28 +00:00
* feat(native-chat): show context usage in Codex chats Codex turn-row writes become journal revisions so the reading survives a turn end. * test(codex): run the context-usage journal suite under Node --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
73 lines
3.1 KiB
TypeScript
73 lines
3.1 KiB
TypeScript
// What a Codex thread reports about its context window, and where that lands in the journal.
|
|
|
|
import type { AgentSessionContextUsage } from '../../shared/agent-session-context-usage'
|
|
import { parseAgentJournalItemKey } from '../../shared/agent-session-journal-item-key'
|
|
import { writeAgentJournalTurnRow } from '../native-chat/agent-session-timeline/agent-journal-turn-row-revision'
|
|
import type { StructuredAgentSessionEventSink } from '../native-chat/agent-session-wire/structured-agent-session-event-sink'
|
|
import { readRecord } from './codex-item-field-readers'
|
|
import type { CodexJournalActiveTurns } from './codex-structured-journal-translation-turn-state'
|
|
import { readCodexTurnId } from './codex-structured-thread-facts'
|
|
import type { CodexRowAttribution } from './codex-subagent-linkage'
|
|
|
|
function positiveCount(value: unknown): number | null {
|
|
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : null
|
|
}
|
|
|
|
/** Codex's own reading of its context on a `thread/tokenUsage/updated` frame, so a report with no
|
|
* breakdown; null when it measures nothing. `last.totalTokens` is the size, and the only count
|
|
* Codex sets when it restates one after a compaction; `total` is the thread's running spend. */
|
|
export function readCodexContextUsage(
|
|
params: unknown,
|
|
capturedAt: number
|
|
): AgentSessionContextUsage | null {
|
|
const usage = readRecord(readRecord(params).tokenUsage)
|
|
// Every request reads at least the instructions, so zero is no measurement.
|
|
const usedTokens = positiveCount(readRecord(usage.last).totalTokens)
|
|
if (usedTokens === null) {
|
|
return null
|
|
}
|
|
const windowTokens = positiveCount(usage.modelContextWindow)
|
|
if (windowTokens === null) {
|
|
// The reading the journal already holds may be measured against another model's window.
|
|
return { used: { kind: 'unknown', capturedAt } }
|
|
}
|
|
return {
|
|
used: {
|
|
kind: 'report',
|
|
// The frame names no model.
|
|
model: 'unknown',
|
|
usedTokens,
|
|
windowTokens,
|
|
percentage: Math.round((usedTokens / windowTokens) * 100),
|
|
categories: [],
|
|
capturedAt
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Writes the session's own frame to the open turn's row, else the newest, where the frame Codex
|
|
* replays on resume belongs. */
|
|
export function recordCodexContextUsage(input: {
|
|
sink: StructuredAgentSessionEventSink
|
|
activeTurns: Pick<CodexJournalActiveTurns, 'current' | 'has'>
|
|
attributionFor: CodexRowAttribution
|
|
threadId: string
|
|
params: unknown
|
|
capturedAt: number
|
|
}): void {
|
|
const contextUsage = readCodexContextUsage(input.params, input.capturedAt)
|
|
if (!contextUsage) {
|
|
return
|
|
}
|
|
const turnId = readCodexTurnId(input.params) ?? input.activeTurns.current(input.threadId)
|
|
const openTurnId = turnId && input.activeTurns.has(input.threadId, turnId) ? turnId : null
|
|
const scope = openTurnId ? input.attributionFor(input.threadId, openTurnId).turnScope : null
|
|
const identity = scope?.kind === 'turn' ? parseAgentJournalItemKey(scope.turnItemId) : null
|
|
writeAgentJournalTurnRow(
|
|
input.sink,
|
|
identity ? { identity } : { newest: true },
|
|
{ contextUsage },
|
|
{ publish: true }
|
|
)
|
|
}
|