mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 00:03:15 +00:00
perf(history): cache numeric transcript timeline bounds
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
import { build } from 'esbuild'
|
||||
import { buildCounterbalancedSchedule } from './counterbalanced-benchmark-schedule.mjs'
|
||||
import { summarizeBenchmarkSamples } from './benchmark-sample-summary.mjs'
|
||||
|
||||
// git show <ref>:src/main/ai-vault/session-scanner-accumulator.ts | node config/scripts/session-timeline-benchmark.mjs
|
||||
const target = resolve('src/main/ai-vault/session-scanner-accumulator.ts')
|
||||
async function load(source) {
|
||||
const result = await build({
|
||||
stdin: {
|
||||
contents: `
|
||||
export {createAccumulator, cloneSessionAccumulator, updateTimeline, finalizeSession}
|
||||
from './session-scanner-accumulator';
|
||||
export {createClaudeSessionParseState, consumeClaudeSessionLine}
|
||||
from './session-scanner-primary-parsers';`,
|
||||
loader: 'ts',
|
||||
resolveDir: dirname(target)
|
||||
},
|
||||
bundle: true,
|
||||
write: false,
|
||||
platform: 'node',
|
||||
format: 'esm',
|
||||
plugins: [
|
||||
{
|
||||
name: 'timeline-baseline',
|
||||
setup(plugin) {
|
||||
plugin.onLoad({ filter: /session-scanner-accumulator\.ts$/ }, () => ({
|
||||
contents: source,
|
||||
loader: 'ts',
|
||||
resolveDir: dirname(target)
|
||||
}))
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
return import(
|
||||
`data:text/javascript;base64,${Buffer.from(result.outputFiles[0].text).toString('base64')}`
|
||||
)
|
||||
}
|
||||
const implementations = {
|
||||
before: await load(readFileSync(0, 'utf8')),
|
||||
after: await load(readFileSync(target, 'utf8'))
|
||||
}
|
||||
const file = { path: 'timeline.jsonl', mtimeMs: 0, modifiedAt: '2026-01-01T00:00:00.000Z' }
|
||||
const create = (implementation) =>
|
||||
implementation.createAccumulator({ agent: 'claude', sessionId: 'timeline', file })
|
||||
const observable = ({ createdAt, updatedAt, latestTimestampMs }) => ({
|
||||
createdAt,
|
||||
updatedAt,
|
||||
latestTimestampMs
|
||||
})
|
||||
|
||||
let seed = 42
|
||||
const random = (max) => {
|
||||
seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0
|
||||
return Math.floor((seed / 2 ** 32) * max)
|
||||
}
|
||||
const tokens = [
|
||||
null,
|
||||
undefined,
|
||||
'',
|
||||
'bad',
|
||||
0,
|
||||
-1,
|
||||
Infinity,
|
||||
Number.NaN,
|
||||
8_640_000_000_000_001,
|
||||
'1969-12-31T23:59:59.999Z',
|
||||
'-000001-01-01T00:00:00.000Z',
|
||||
'+010000-01-01T00:00:00.000Z',
|
||||
'2026-01-01T01:00:00+01:00',
|
||||
1_700_000_000.0009,
|
||||
1_700_000_000_000.9,
|
||||
1_700_000_000_000.1,
|
||||
1_700_000_000_000 - 0.1
|
||||
]
|
||||
let differentialUpdates = 0
|
||||
for (let trial = 0; trial < 3000; trial += 1) {
|
||||
let before = create(implementations.before)
|
||||
let after = create(implementations.after)
|
||||
for (let index = 0; index < 32; index += 1) {
|
||||
const input = random(2) ? tokens[random(tokens.length)] : 1_700_000_000_000 + random(1000) / 10
|
||||
const update = (implementation, state) => {
|
||||
try {
|
||||
implementation.updateTimeline(state, input)
|
||||
} catch (error) {
|
||||
return String(error)
|
||||
}
|
||||
return null
|
||||
}
|
||||
assert.equal(update(implementations.after, after), update(implementations.before, before))
|
||||
assert.deepEqual(observable(after), observable(before))
|
||||
if (index === 15) {
|
||||
before = implementations.before.cloneSessionAccumulator(before)
|
||||
after = implementations.after.cloneSessionAccumulator(after)
|
||||
}
|
||||
differentialUpdates += 1
|
||||
}
|
||||
assert.deepEqual(
|
||||
implementations.after.finalizeSession(after, 'linux'),
|
||||
implementations.before.finalizeSession(before, 'linux')
|
||||
)
|
||||
}
|
||||
|
||||
const results = []
|
||||
for (const records of [100, 10_000, 100_000]) {
|
||||
for (const workload of [
|
||||
'numeric-timeline',
|
||||
'iso-timeline',
|
||||
'out-of-order-timeline',
|
||||
'claude-record-fold'
|
||||
]) {
|
||||
const timestamps = Array.from({ length: records }, (_, index) => {
|
||||
const ms =
|
||||
1_700_000_000_000 + (workload === 'out-of-order-timeline' ? random(records) : index)
|
||||
return workload === 'numeric-timeline' ? ms : new Date(ms).toISOString()
|
||||
})
|
||||
const lines =
|
||||
workload === 'claude-record-fold'
|
||||
? timestamps.map((timestamp, index) =>
|
||||
JSON.stringify({
|
||||
type: index % 2 ? 'assistant' : 'user',
|
||||
sessionId: 'timeline',
|
||||
timestamp,
|
||||
message: {
|
||||
role: index % 2 ? 'assistant' : 'user',
|
||||
content: 'Example transcript message'
|
||||
}
|
||||
})
|
||||
)
|
||||
: []
|
||||
function run(arm) {
|
||||
const implementation = implementations[arm]
|
||||
const parser = implementation.createClaudeSessionParseState(file)
|
||||
const state = workload === 'claude-record-fold' ? parser.accumulator : create(implementation)
|
||||
const started = performance.now()
|
||||
if (workload === 'claude-record-fold') {
|
||||
for (const line of lines) {
|
||||
implementation.consumeClaudeSessionLine(parser, line)
|
||||
}
|
||||
} else {
|
||||
for (const timestamp of timestamps) {
|
||||
implementation.updateTimeline(state, timestamp)
|
||||
}
|
||||
}
|
||||
const ms = performance.now() - started
|
||||
return {
|
||||
ms,
|
||||
result: implementation.finalizeSession(state, 'linux'),
|
||||
timeline: observable(state)
|
||||
}
|
||||
}
|
||||
const expected = run('before')
|
||||
assert.deepEqual(run('after').result, expected.result)
|
||||
/** @type {{ before: number[], after: number[] }} */
|
||||
const samples = { before: [], after: [] }
|
||||
for (const pair of buildCounterbalancedSchedule(8, 'before', 'after')) {
|
||||
for (const arm of pair) {
|
||||
const actual = run(arm)
|
||||
samples[arm].push(actual.ms)
|
||||
assert.deepEqual(actual.result, expected.result)
|
||||
assert.deepEqual(actual.timeline, expected.timeline)
|
||||
}
|
||||
}
|
||||
results.push({
|
||||
records,
|
||||
workload,
|
||||
before: summarizeBenchmarkSamples(samples.before),
|
||||
after: summarizeBenchmarkSamples(samples.after)
|
||||
})
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{ node: process.version, platform: process.platform, differentialUpdates, results },
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
@@ -64,6 +64,7 @@ export function createAccumulator(args: {
|
||||
lastUserPrompt: null,
|
||||
queuedMessageCount: 0,
|
||||
subagentTranscriptCount: 0,
|
||||
earliestTimestampMs: 0,
|
||||
latestTimestampMs: 0
|
||||
}
|
||||
}
|
||||
@@ -191,10 +192,12 @@ export function updateTimeline(accumulator: SessionAccumulator, timestamp: unkno
|
||||
return
|
||||
}
|
||||
const iso = new Date(parsed).toISOString()
|
||||
if (!accumulator.createdAt || parsed < Date.parse(accumulator.createdAt)) {
|
||||
if (!accumulator.createdAt || parsed < accumulator.earliestTimestampMs) {
|
||||
accumulator.createdAt = iso
|
||||
accumulator.earliestTimestampMs = Math.trunc(parsed)
|
||||
}
|
||||
if (!accumulator.updatedAt || parsed >= Date.parse(accumulator.updatedAt)) {
|
||||
// ISO serialization truncates fractional milliseconds; latestTimestampMs retains them.
|
||||
if (!accumulator.updatedAt || parsed >= Math.trunc(accumulator.latestTimestampMs)) {
|
||||
accumulator.updatedAt = iso
|
||||
accumulator.latestTimestampMs = parsed
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
cloneSessionAccumulator,
|
||||
createAccumulator,
|
||||
finalizeSession,
|
||||
updateTimeline
|
||||
} from './session-scanner-accumulator'
|
||||
|
||||
function accumulator() {
|
||||
return createAccumulator({
|
||||
agent: 'claude',
|
||||
sessionId: 'timeline-test',
|
||||
file: { path: 'transcript.jsonl', mtimeMs: 0, modifiedAt: '2026-01-01T00:00:00.000Z' }
|
||||
})
|
||||
}
|
||||
|
||||
describe('session timeline bounds', () => {
|
||||
it('retains earliest and latest timestamps despite duplicates and out-of-order records', () => {
|
||||
const state = accumulator()
|
||||
for (const timestamp of [
|
||||
'2026-01-03T01:00:00+01:00',
|
||||
'2026-01-01T00:00:00Z',
|
||||
'2026-01-04T00:00:00Z',
|
||||
'2026-01-02T00:00:00Z',
|
||||
'2026-01-04T00:00:00Z'
|
||||
]) {
|
||||
updateTimeline(state, timestamp)
|
||||
}
|
||||
expect(finalizeSession(state, 'linux')).toMatchObject({
|
||||
createdAt: '2026-01-01T00:00:00.000Z',
|
||||
updatedAt: '2026-01-04T00:00:00.000Z'
|
||||
})
|
||||
expect(state.latestTimestampMs).toBe(Date.parse('2026-01-04T00:00:00Z'))
|
||||
})
|
||||
|
||||
it('compares fractional numeric timestamps against the rounded ISO bound', () => {
|
||||
const state = accumulator()
|
||||
const base = 1_700_000_000_000
|
||||
updateTimeline(state, base + 0.9)
|
||||
updateTimeline(state, base + 0.1)
|
||||
expect(state.latestTimestampMs).toBe(base + 0.1)
|
||||
expect(state.createdAt).toBe(new Date(base).toISOString())
|
||||
updateTimeline(state, base - 0.1)
|
||||
expect(state.createdAt).toBe(new Date(base - 1).toISOString())
|
||||
expect(state.latestTimestampMs).toBe(base + 0.1)
|
||||
})
|
||||
|
||||
it('preserves pre-epoch and extended-year ISO timestamps', () => {
|
||||
const state = accumulator()
|
||||
updateTimeline(state, '+010000-01-01T00:00:00.000Z')
|
||||
updateTimeline(state, '-000001-01-01T00:00:00.000Z')
|
||||
updateTimeline(state, '1969-12-31T23:59:59.999Z')
|
||||
expect(state.createdAt).toBe('-000001-01-01T00:00:00.000Z')
|
||||
expect(state.updatedAt).toBe('+010000-01-01T00:00:00.000Z')
|
||||
})
|
||||
|
||||
it('ignores invalid timestamps and retains the existing out-of-range error', () => {
|
||||
const state = accumulator()
|
||||
for (const timestamp of [null, undefined, '', 'bad-date', 0, -1, Number.NaN, Infinity]) {
|
||||
updateTimeline(state, timestamp)
|
||||
}
|
||||
expect(state.createdAt).toBeNull()
|
||||
expect(state.updatedAt).toBeNull()
|
||||
expect(() => updateTimeline(state, 8_640_000_000_000_001)).toThrow(RangeError)
|
||||
expect(state.createdAt).toBeNull()
|
||||
expect(state.updatedAt).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps cloned parse-state bounds independent', () => {
|
||||
const state = accumulator()
|
||||
updateTimeline(state, '2026-01-02T00:00:00Z')
|
||||
const clone = cloneSessionAccumulator(state)
|
||||
updateTimeline(clone, '2026-01-01T00:00:00Z')
|
||||
updateTimeline(clone, '2026-01-03T00:00:00Z')
|
||||
expect(state.createdAt).toBe('2026-01-02T00:00:00.000Z')
|
||||
expect(state.updatedAt).toBe('2026-01-02T00:00:00.000Z')
|
||||
expect(clone.createdAt).toBe('2026-01-01T00:00:00.000Z')
|
||||
expect(clone.updatedAt).toBe('2026-01-03T00:00:00.000Z')
|
||||
})
|
||||
|
||||
it('does not reparse accumulated bounds for every numeric record', () => {
|
||||
const state = accumulator()
|
||||
const spy = vi.spyOn(Date, 'parse')
|
||||
let parseCalls: number
|
||||
try {
|
||||
for (let index = 0; index < 1000; index += 1) {
|
||||
updateTimeline(state, 1_700_000_000_000 + index)
|
||||
}
|
||||
parseCalls = spy.mock.calls.length
|
||||
} finally {
|
||||
spy.mockRestore()
|
||||
}
|
||||
expect(state.latestTimestampMs).toBe(1_700_000_000_999)
|
||||
expect(parseCalls).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -144,6 +144,7 @@ export type SessionAccumulator = {
|
||||
// Recoverable signal for a zero-turn transcript (see AiVaultSession).
|
||||
queuedMessageCount: number
|
||||
subagentTranscriptCount: number
|
||||
earliestTimestampMs: number
|
||||
latestTimestampMs: number
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user