Files
pxpipe/tests/baseline.test.ts
T
Steven Chong 47b5e412b3 feat: cache-stable history-collapse imaging for GPT (OpenAI) + Anthropic
Collapse old conversation history into rendered PNG sections so the model
reads a compact image instead of re-billed text, while preserving prompt
caching and tool-call behavior. Measures real vs compressed token/cost.

Core:
- GPT history collapse (openai-history.ts): append-only, o200k token-length
  sectioning. Sections seal only at a tool-closed boundary (open call-id set
  empty), so a function_call and its function_call_output never split across
  the collapse cut. Fixes the OpenAI 400 "No tool call found for function
  call output with call_id ..." that hit long Responses-API sessions.
- Anthropic cache contract (history.ts): append-only per-chunk rendering;
  cache_control markers are preserved/moved, never added; chunk boundaries
  align with caller marker seams for byte-stable prefix caching.
- GPT image budget (openai.ts): detail:'original' for gpt-5.x, flagship
  vision-multiplier fix, patch cap; schema-strip preserves real descriptions.
- Savings accounting (openai-savings.ts): cached_tokens + vision-token basis.

Model scope (applicability.ts):
- Default imaged scope = claude-fable-5 + gpt-5.6.
- gpt-5.5 and claude-opus-4-8 stay opt-in: same pipeline, but they degrade
  reading dense imaged history (gist drift), so silently imaging them by
  default is wrong. Promotion is gated on an OCR/recall threshold.

Dashboard: GPT + Anthropic rendering, per-family model toggles, persisted
metrics, thumbnail-expired session UI, reflow/newline handling.

Tests: cache-alignment (GPT + Anthropic), history sectioning + tool-boundary
invariants, savings, dashboard, sessions/restart-restore. 452 passing.
2026-06-20 21:11:24 -04:00

69 lines
3.0 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
computeBaselineInputEff,
computeActualInputEff,
CACHE_CREATE_RATE,
CACHE_READ_RATE,
} from '../src/core/baseline.js';
/**
* The text counterfactual must be warmth-aware. The old warmth-free version
* always priced the cacheable prefix at the cheap read rate, which fabricated a
* "free read" on cold/TTL-expiry turns — text would actually re-create the
* prefix there, same as the imaged path. That phantom read showed up as a
* dashboard loss on growth/cold turns even when imaging genuinely won.
*/
describe('computeBaselineInputEff (warmth-aware)', () => {
const inp = 1000;
const cc = 0;
const cr = 0;
it('credits nothing (returns actual) when the probe could not split the prefix', () => {
const actual = computeActualInputEff(inp, cc, cr);
expect(computeBaselineInputEff(5000, 0, inp, cc, cr, true, 0)).toBe(actual);
expect(computeBaselineInputEff(5000, -1, inp, cc, cr, false, 0)).toBe(actual);
});
it('returns 0 for a non-positive baseline', () => {
expect(computeBaselineInputEff(0, 100, inp, cc, cr)).toBe(0);
expect(computeBaselineInputEff(-10, 100, inp, cc, cr)).toBe(0);
});
it('cold turn re-creates the whole cacheable prefix at the create rate', () => {
// baseline=5000, cacheable=4000, coldTail=1000. No warm cache for text.
const got = computeBaselineInputEff(5000, 4000, inp, cc, cr, false, 0);
expect(got).toBe(4000 * CACHE_CREATE_RATE + 1000 * 1.0);
});
it('defaults to the cold (warmth-free) path when warm is omitted', () => {
const explicit = computeBaselineInputEff(5000, 4000, inp, cc, cr, false, 0);
const defaulted = computeBaselineInputEff(5000, 4000, inp, cc, cr);
expect(defaulted).toBe(explicit);
});
it('warm turn reads the prefix it already had cached at the read rate', () => {
// Same prefix size as last turn: fully reused, nothing grown.
const got = computeBaselineInputEff(5000, 4000, inp, cc, cr, true, 4000);
expect(got).toBe(4000 * CACHE_READ_RATE + 1000 * 1.0);
});
it('warm growth turn reads the reused prefix and creates only the growth', () => {
// prev cached 3000, prefix grew to 4000: reused=3000, grown=1000.
const got = computeBaselineInputEff(5000, 4000, inp, cc, cr, true, 3000);
expect(got).toBe(3000 * CACHE_READ_RATE + 1000 * CACHE_CREATE_RATE + 1000 * 1.0);
});
it('caps reused at the current cacheable when the prefix shrank', () => {
// prev cached 9000 but prefix is now 4000: reused=4000, grown=0.
const got = computeBaselineInputEff(5000, 4000, inp, cc, cr, true, 9000);
expect(got).toBe(4000 * CACHE_READ_RATE + 1000 * 1.0);
});
it('never prices a cold turn cheaper than a warm turn for the same prefix', () => {
// The regression guard: cold must cost MORE than warm (no phantom free read).
const cold = computeBaselineInputEff(5000, 4000, inp, cc, cr, false, 0);
const warm = computeBaselineInputEff(5000, 4000, inp, cc, cr, true, 4000);
expect(cold).toBeGreaterThan(warm);
});
});