@dzhechkov/harness-core 0.3.146 → 0.3.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dz-manifest.json +42 -18
- package/README.md +1 -0
- package/dist/cost-ledger.d.ts +318 -0
- package/dist/cost-ledger.d.ts.map +1 -0
- package/dist/cost-ledger.js +871 -0
- package/dist/cost-ledger.js.map +1 -0
- package/dist/cost-scoring.d.ts +9 -0
- package/dist/cost-scoring.d.ts.map +1 -1
- package/dist/cost-scoring.js +18 -0
- package/dist/cost-scoring.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/usage.d.ts +32 -0
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +59 -10
- package/dist/usage.js.map +1 -1
- package/package.json +3 -3
- package/sbom.json +77 -17
- package/src/cost-ledger.ts +1105 -0
- package/src/cost-scoring.ts +17 -0
- package/src/index.ts +38 -0
- package/src/usage.ts +74 -12
package/src/cost-scoring.ts
CHANGED
|
@@ -78,6 +78,23 @@ const FALLBACK_PRICING = priced(3 / 1e6, 15 / 1e6);
|
|
|
78
78
|
* Resolve a model id to its pricing by longest-prefix match.
|
|
79
79
|
* `bedrock/anthropic.claude-3-opus...` → strips a leading `provider/` segment.
|
|
80
80
|
*/
|
|
81
|
+
/**
|
|
82
|
+
* Whether {@link pricingFor} will find a REAL entry for this model id, or silently fall back to
|
|
83
|
+
* {@link FALLBACK_PRICING} (sonnet-class).
|
|
84
|
+
*
|
|
85
|
+
* Exists so a cost surface can REPORT the fallback instead of hiding it: `claude-fable-*` has no
|
|
86
|
+
* entry in {@link MODEL_PRICES} and is the default model of every recorded feature-adr run, so its
|
|
87
|
+
* dollar figures are priced at a fallback rate (feature `cost-ledger`, ADR-003).
|
|
88
|
+
*/
|
|
89
|
+
export function hasKnownPricing(modelId: string): boolean {
|
|
90
|
+
if (typeof modelId !== 'string' || modelId.length === 0) return false;
|
|
91
|
+
const id = modelId.toLowerCase().replace(/^[a-z0-9-]+\//, '');
|
|
92
|
+
for (const key of Object.keys(MODEL_PRICES)) {
|
|
93
|
+
if (id.startsWith(key)) return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
81
98
|
export function pricingFor(modelId: string): ModelPricing {
|
|
82
99
|
// Strip a leading `provider/` segment (e.g. `bedrock/`, `us-east-1/`).
|
|
83
100
|
const id = modelId.toLowerCase().replace(/^[a-z0-9-]+\//, '');
|
package/src/index.ts
CHANGED
|
@@ -263,6 +263,7 @@ export type { RiskScore, RiskThresholds } from './risk-scoring.js';
|
|
|
263
263
|
export {
|
|
264
264
|
MODEL_PRICES,
|
|
265
265
|
pricingFor,
|
|
266
|
+
hasKnownPricing,
|
|
266
267
|
normalizeUsage,
|
|
267
268
|
usageCost,
|
|
268
269
|
invocationCost,
|
|
@@ -342,6 +343,43 @@ export {
|
|
|
342
343
|
readUsageLimits,
|
|
343
344
|
weeklyWindowFor,
|
|
344
345
|
} from './usage.js';
|
|
346
|
+
export { claudeProjectsRoot, rawTokenMixOf, weightedTokensOf } from './usage.js';
|
|
347
|
+
export type { RawTokenMix } from './usage.js';
|
|
348
|
+
// Per-stage cost ledger + reconciliation invariant (feature cost-ledger, ADR-001/002/003).
|
|
349
|
+
export {
|
|
350
|
+
COST_LEDGER_SCOPE,
|
|
351
|
+
COST_LEDGER_DEFECT_KINDS,
|
|
352
|
+
COST_LEDGER_VERDICTS,
|
|
353
|
+
DEFAULT_COST_LEDGER_EPSILON,
|
|
354
|
+
extractCostSamples,
|
|
355
|
+
parseWorkflowRunRecord,
|
|
356
|
+
buildCostLedger,
|
|
357
|
+
verifyCostLedgerReport,
|
|
358
|
+
stageCostAggregates,
|
|
359
|
+
renderCostLedger,
|
|
360
|
+
costLedgerJsonl,
|
|
361
|
+
listCostLedgerRuns,
|
|
362
|
+
deriveCostLedger,
|
|
363
|
+
deriveStageCostAggregates,
|
|
364
|
+
writeCostLedgerJsonl,
|
|
365
|
+
} from './cost-ledger.js';
|
|
366
|
+
export type {
|
|
367
|
+
CostLedgerDefect,
|
|
368
|
+
CostLedgerDefectKind,
|
|
369
|
+
CostLedgerIoOptions,
|
|
370
|
+
CostLedgerReconciliation,
|
|
371
|
+
CostLedgerReport,
|
|
372
|
+
CostLedgerRow,
|
|
373
|
+
CostLedgerRunRef,
|
|
374
|
+
CostLedgerSample,
|
|
375
|
+
CostLedgerVerdict,
|
|
376
|
+
BuildCostLedgerInput,
|
|
377
|
+
DeriveCostLedgerOptions,
|
|
378
|
+
StageCostAggregate,
|
|
379
|
+
StageSampleSet,
|
|
380
|
+
WorkflowRunRecord,
|
|
381
|
+
WorkflowStageEntry,
|
|
382
|
+
} from './cost-ledger.js';
|
|
345
383
|
export type {
|
|
346
384
|
ClaudeUsageModel,
|
|
347
385
|
UsageCalibrationChange,
|
package/src/usage.ts
CHANGED
|
@@ -45,6 +45,77 @@ const MTIME_SLACK_MS = HOUR_MS;
|
|
|
45
45
|
*/
|
|
46
46
|
export const TOKEN_WEIGHTS = { input: 1, cacheWrite: 1.25, cacheWrite1h: 2, cacheRead: 0.1, output: 5 } as const;
|
|
47
47
|
|
|
48
|
+
/** The four raw token buckets of one `message.usage` record, after clamping. */
|
|
49
|
+
export interface RawTokenMix {
|
|
50
|
+
readonly input: number;
|
|
51
|
+
/** Cache-CREATION tokens, already priced at their TTL rate inside {@link weightedTokensOf}. */
|
|
52
|
+
readonly cacheWrite: number;
|
|
53
|
+
readonly cacheRead: number;
|
|
54
|
+
readonly output: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function positiveFinite(v: unknown): number {
|
|
58
|
+
return typeof v === 'number' && isFinite(v) && v > 0 ? v : 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The raw token buckets of an Anthropic `message.usage` object, clamped to finite non-negatives.
|
|
63
|
+
* `cacheWrite` prefers the TTL breakdown (`cache_creation.ephemeral_*`) and falls back to the flat
|
|
64
|
+
* `cache_creation_input_tokens` — reading only the flat field scored a nested-only record as ZERO.
|
|
65
|
+
*/
|
|
66
|
+
export function rawTokenMixOf(usage: unknown): RawTokenMix {
|
|
67
|
+
if (typeof usage !== 'object' || usage === null) {
|
|
68
|
+
return { input: 0, cacheWrite: 0, cacheRead: 0, output: 0 };
|
|
69
|
+
}
|
|
70
|
+
const u = usage as Record<string, unknown>;
|
|
71
|
+
const cc = (typeof u['cache_creation'] === 'object' && u['cache_creation'] !== null
|
|
72
|
+
? (u['cache_creation'] as Record<string, unknown>)
|
|
73
|
+
: {}) as Record<string, unknown>;
|
|
74
|
+
const c5 = positiveFinite(cc['ephemeral_5m_input_tokens']);
|
|
75
|
+
const c1h = positiveFinite(cc['ephemeral_1h_input_tokens']);
|
|
76
|
+
const cacheWrite = c5 + c1h > 0 ? c5 + c1h : positiveFinite(u['cache_creation_input_tokens']);
|
|
77
|
+
return {
|
|
78
|
+
input: positiveFinite(u['input_tokens']),
|
|
79
|
+
cacheWrite,
|
|
80
|
+
cacheRead: positiveFinite(u['cache_read_input_tokens']),
|
|
81
|
+
output: positiveFinite(u['output_tokens']),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* THE estimator — cost-weighted "input-equivalent" tokens for one `message.usage` object.
|
|
87
|
+
*
|
|
88
|
+
* A flat token sum is 89-99.7% `cache_read` on this machine, which tracks CONVERSATION LENGTH
|
|
89
|
+
* rather than work done. {@link TOKEN_WEIGHTS} are the published per-token price ratios relative to
|
|
90
|
+
* base input, so the result tracks consumption instead of context size.
|
|
91
|
+
*
|
|
92
|
+
* Extracted verbatim from `computeUsage`'s per-sample arithmetic so that `dz usage` and the
|
|
93
|
+
* per-stage cost ledger measure the same quantity (feature `cost-ledger`, ADR-002 — the invariant
|
|
94
|
+
* only means something if both sides use ONE estimator). The return value is UNROUNDED; callers
|
|
95
|
+
* that need exact integer identities round once at their own extraction point.
|
|
96
|
+
*/
|
|
97
|
+
export function weightedTokensOf(usage: unknown): number {
|
|
98
|
+
if (typeof usage !== 'object' || usage === null) return 0;
|
|
99
|
+
const mix = rawTokenMixOf(usage);
|
|
100
|
+
const u = usage as Record<string, unknown>;
|
|
101
|
+
const cc =
|
|
102
|
+
typeof u['cache_creation'] === 'object' && u['cache_creation'] !== null
|
|
103
|
+
? (u['cache_creation'] as Record<string, unknown>)
|
|
104
|
+
: {};
|
|
105
|
+
const c5 = positiveFinite(cc['ephemeral_5m_input_tokens']);
|
|
106
|
+
const c1h = positiveFinite(cc['ephemeral_1h_input_tokens']);
|
|
107
|
+
const cacheWriteCost =
|
|
108
|
+
c5 + c1h > 0
|
|
109
|
+
? c5 * TOKEN_WEIGHTS.cacheWrite + c1h * TOKEN_WEIGHTS.cacheWrite1h
|
|
110
|
+
: mix.cacheWrite * TOKEN_WEIGHTS.cacheWrite;
|
|
111
|
+
return (
|
|
112
|
+
mix.input * TOKEN_WEIGHTS.input +
|
|
113
|
+
cacheWriteCost +
|
|
114
|
+
mix.cacheRead * TOKEN_WEIGHTS.cacheRead +
|
|
115
|
+
mix.output * TOKEN_WEIGHTS.output
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
48
119
|
export const CLAUDE_USAGE_MODELS = ['fable', 'opus', 'sonnet', 'haiku'] as const;
|
|
49
120
|
export type ClaudeUsageModel = (typeof CLAUDE_USAGE_MODELS)[number];
|
|
50
121
|
|
|
@@ -230,7 +301,7 @@ export function normalizeClaudeUsageModelKey(raw: unknown): ClaudeUsageModel | n
|
|
|
230
301
|
* The `~/.claude/projects` root (the account-wide transcript store). Overridable via
|
|
231
302
|
* `DZ_CLAUDE_PROJECTS_ROOT` — used by tests to point at a temp tree. Never throws.
|
|
232
303
|
*/
|
|
233
|
-
function claudeProjectsRoot(): string {
|
|
304
|
+
export function claudeProjectsRoot(): string {
|
|
234
305
|
const override = process.env['DZ_CLAUDE_PROJECTS_ROOT'];
|
|
235
306
|
if (typeof override === 'string' && override.length > 0) return override;
|
|
236
307
|
return join(homedir(), '.claude', 'projects');
|
|
@@ -414,17 +485,8 @@ function extractSamples(path: string, scanCutoff: number, into: Sample[], seen:
|
|
|
414
485
|
// tokens": a quantity that tracks consumption instead of context size.
|
|
415
486
|
// Prefer the TTL breakdown when present (5m 1.25x / 1h 2x); fall back to the flat field at the
|
|
416
487
|
// 5m rate. Reading only the flat field scored a nested-only record as ZERO.
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
const cacheWriteCost =
|
|
420
|
-
c5 + c1h > 0
|
|
421
|
-
? c5 * TOKEN_WEIGHTS.cacheWrite + c1h * TOKEN_WEIGHTS.cacheWrite1h
|
|
422
|
-
: n(usage.cache_creation_input_tokens) * TOKEN_WEIGHTS.cacheWrite;
|
|
423
|
-
const tokens =
|
|
424
|
-
n(usage.input_tokens) * TOKEN_WEIGHTS.input +
|
|
425
|
-
cacheWriteCost +
|
|
426
|
-
n(usage.cache_read_input_tokens) * TOKEN_WEIGHTS.cacheRead +
|
|
427
|
-
n(usage.output_tokens) * TOKEN_WEIGHTS.output;
|
|
488
|
+
// ONE estimator, shared with the per-stage cost ledger (feature `cost-ledger`, ADR-002).
|
|
489
|
+
const tokens = weightedTokensOf(usage);
|
|
428
490
|
if (tokens <= 0) continue;
|
|
429
491
|
// Dedup: streamed assistant messages repeat their usage object across chunks.
|
|
430
492
|
const id = typeof rec.message?.id === 'string' ? rec.message.id : '';
|