taskflow-core 0.1.8 → 0.2.0
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/README.md +80 -25
- package/dist/deterministic.d.ts +8 -0
- package/dist/deterministic.d.ts.map +1 -1
- package/dist/deterministic.js +23 -1
- package/dist/deterministic.js.map +1 -1
- package/dist/exec/driver.d.ts +47 -0
- package/dist/exec/driver.d.ts.map +1 -0
- package/dist/exec/driver.js +397 -0
- package/dist/exec/driver.js.map +1 -0
- package/dist/exec/events.d.ts +76 -0
- package/dist/exec/events.d.ts.map +1 -0
- package/dist/exec/events.js +108 -0
- package/dist/exec/events.js.map +1 -0
- package/dist/exec/fold.d.ts +45 -0
- package/dist/exec/fold.d.ts.map +1 -0
- package/dist/exec/fold.js +122 -0
- package/dist/exec/fold.js.map +1 -0
- package/dist/exec/index.d.ts +17 -0
- package/dist/exec/index.d.ts.map +1 -0
- package/dist/exec/index.js +17 -0
- package/dist/exec/index.js.map +1 -0
- package/dist/exec/kernel-policy.d.ts +35 -0
- package/dist/exec/kernel-policy.d.ts.map +1 -0
- package/dist/exec/kernel-policy.js +184 -0
- package/dist/exec/kernel-policy.js.map +1 -0
- package/dist/exec/step-kinds.d.ts +41 -0
- package/dist/exec/step-kinds.d.ts.map +1 -0
- package/dist/exec/step-kinds.js +574 -0
- package/dist/exec/step-kinds.js.map +1 -0
- package/dist/exec/step.d.ts +93 -0
- package/dist/exec/step.d.ts.map +1 -0
- package/dist/exec/step.js +436 -0
- package/dist/exec/step.js.map +1 -0
- package/dist/flowir/canonical-hash.d.ts +101 -0
- package/dist/flowir/canonical-hash.d.ts.map +1 -0
- package/dist/flowir/canonical-hash.js +219 -0
- package/dist/flowir/canonical-hash.js.map +1 -0
- package/dist/flowir/compile.d.ts +49 -0
- package/dist/flowir/compile.d.ts.map +1 -0
- package/dist/flowir/compile.js +219 -0
- package/dist/flowir/compile.js.map +1 -0
- package/dist/flowir/cond.d.ts +76 -0
- package/dist/flowir/cond.d.ts.map +1 -0
- package/dist/flowir/cond.js +214 -0
- package/dist/flowir/cond.js.map +1 -0
- package/dist/flowir/index.d.ts +20 -23
- package/dist/flowir/index.d.ts.map +1 -1
- package/dist/flowir/index.js +47 -35
- package/dist/flowir/index.js.map +1 -1
- package/dist/flowir/meta.d.ts +4 -4
- package/dist/flowir/meta.d.ts.map +1 -1
- package/dist/flowir/phasefp.d.ts.map +1 -1
- package/dist/flowir/phasefp.js +13 -1
- package/dist/flowir/phasefp.js.map +1 -1
- package/dist/flowir/schema.d.ts +260 -0
- package/dist/flowir/schema.d.ts.map +1 -0
- package/dist/flowir/schema.js +234 -0
- package/dist/flowir/schema.js.map +1 -0
- package/dist/flowir/translate.d.ts.map +1 -1
- package/dist/flowir/translate.js +3 -0
- package/dist/flowir/translate.js.map +1 -1
- package/dist/host/runner-types.d.ts +3 -0
- package/dist/host/runner-types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/rates.d.ts +121 -0
- package/dist/rates.d.ts.map +1 -0
- package/dist/rates.js +187 -0
- package/dist/rates.js.map +1 -0
- package/dist/replay.d.ts +43 -50
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +629 -19
- package/dist/replay.js.map +1 -1
- package/dist/runner-core.d.ts +24 -0
- package/dist/runner-core.d.ts.map +1 -1
- package/dist/runner-core.js +225 -32
- package/dist/runner-core.js.map +1 -1
- package/dist/runtime/phases/approval.d.ts +23 -0
- package/dist/runtime/phases/approval.d.ts.map +1 -0
- package/dist/runtime/phases/approval.js +38 -0
- package/dist/runtime/phases/approval.js.map +1 -0
- package/dist/runtime/phases/expand.d.ts +29 -0
- package/dist/runtime/phases/expand.d.ts.map +1 -0
- package/dist/runtime/phases/expand.js +126 -0
- package/dist/runtime/phases/expand.js.map +1 -0
- package/dist/runtime/phases/parallel.d.ts +26 -0
- package/dist/runtime/phases/parallel.d.ts.map +1 -0
- package/dist/runtime/phases/parallel.js +15 -0
- package/dist/runtime/phases/parallel.js.map +1 -0
- package/dist/runtime/phases/race.d.ts +31 -0
- package/dist/runtime/phases/race.d.ts.map +1 -0
- package/dist/runtime/phases/race.js +220 -0
- package/dist/runtime/phases/race.js.map +1 -0
- package/dist/runtime/phases/script.d.ts +38 -0
- package/dist/runtime/phases/script.d.ts.map +1 -0
- package/dist/runtime/phases/script.js +139 -0
- package/dist/runtime/phases/script.js.map +1 -0
- package/dist/runtime.d.ts +29 -16
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +554 -292
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +36 -4
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +86 -10
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +6 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js.map +1 -1
- package/dist/trace.d.ts +4 -1
- package/dist/trace.d.ts.map +1 -1
- package/dist/trace.js +33 -26
- package/dist/trace.js.map +1 -1
- package/package.json +5 -4
package/dist/rates.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model rate registry — pure token-to-USD cost estimation.
|
|
3
|
+
*
|
|
4
|
+
* ## CODEX COST GAP
|
|
5
|
+
*
|
|
6
|
+
* The `codex-runner` (packages/codex-taskflow) emits NO cost field in its usage
|
|
7
|
+
* response, so `usage.cost` stays 0 on Codex even when tokens are accurately
|
|
8
|
+
* reported. This makes `budget.maxUSD` silently non-functional for Codex-hosted
|
|
9
|
+
* flows. This module fills the gap: batch 2 of the runner layer (or a post-hoc
|
|
10
|
+
* cost fill) can call `estimateCost()` to compute USD from the known model +
|
|
11
|
+
* token counts, making Codex cost-aware without changing the runner protocol.
|
|
12
|
+
*
|
|
13
|
+
* ## Link to verify.ts
|
|
14
|
+
*
|
|
15
|
+
* `verify.ts` defines `ESTIMATED_COST_PER_PHASE = 0.001` as a crude lower-bound
|
|
16
|
+
* budget guard. `FLAT_FALLBACK_PER_TOKEN` serves the same role for unknown models
|
|
17
|
+
* — a rough-order-of-magnitude per-token default (~$1/1M tokens, consistent with
|
|
18
|
+
* $0.001/1000-tokens at verify's granularity).
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import type { UsageStats } from "./usage.ts";
|
|
23
|
+
/**
|
|
24
|
+
* Per-model pricing in USD per 1,000,000 tokens.
|
|
25
|
+
*
|
|
26
|
+
* Rates mirror the provider's published API pricing (input, output, cached
|
|
27
|
+
* input read, cached input write). Missing fields default to zero.
|
|
28
|
+
*/
|
|
29
|
+
export interface ModelRate {
|
|
30
|
+
/** USD per 1,000,000 input (prompt) tokens. */
|
|
31
|
+
inputPer1M: number;
|
|
32
|
+
/** USD per 1,000,000 output (completion) tokens. */
|
|
33
|
+
outputPer1M: number;
|
|
34
|
+
/** USD per 1,000,000 cached input tokens read (if applicable). */
|
|
35
|
+
cacheReadPer1M?: number;
|
|
36
|
+
/** USD per 1,000,000 cached input tokens written (if applicable). */
|
|
37
|
+
cacheWritePer1M?: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Built-in rate table keyed by **model-family prefix** (case-sensitive lookup
|
|
41
|
+
* key — matching in `resolveRate` is case-insensitive).
|
|
42
|
+
*
|
|
43
|
+
* This is a small, illustrative, SPARSE table. Hosts can inject a richer table
|
|
44
|
+
* at runtime via the optional `table` parameter to `resolveRate`/`estimateCost`.
|
|
45
|
+
*
|
|
46
|
+
* Prices are approximate and may drift from provider publishing. For production
|
|
47
|
+
* billing, always use the provider's official cost API.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* // Example override from a host adapter:
|
|
51
|
+
* const myRates = { ...DEFAULT_RATES, "my-model": { inputPer1M: 1, outputPer1M: 4 } };
|
|
52
|
+
* const cost = estimateCost(usage, "my-model", myRates);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare const DEFAULT_RATES: Record<string, ModelRate>;
|
|
56
|
+
/**
|
|
57
|
+
* Flat per-token USD fallback used when no model rate can be resolved.
|
|
58
|
+
*
|
|
59
|
+
* Currently set to $1 per 1,000,000 tokens (~$0.001 per 1K tokens), consistent
|
|
60
|
+
* with `verify.ts`'s `ESTIMATED_COST_PER_PHASE = 0.001` rough-order-of-magnitude
|
|
61
|
+
* estimate. This avoids silent zero-cost for unknown models while staying in the
|
|
62
|
+
* right ballpark for most hosted LLM APIs.
|
|
63
|
+
*/
|
|
64
|
+
export declare const FLAT_FALLBACK_PER_TOKEN = 0.000001;
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a model identifier to a `ModelRate` via case-insensitive prefix match
|
|
67
|
+
* against the provided table (defaults to `DEFAULT_RATES`).
|
|
68
|
+
*
|
|
69
|
+
* Matching strategy:
|
|
70
|
+
* 1. Exact case-insensitive key match.
|
|
71
|
+
* 2. Longest prefix match (case-insensitive), e.g. "gpt-4o-2024-08-06" matches
|
|
72
|
+
* the "gpt-4o" key.
|
|
73
|
+
* 3. Falls back to single-word prefix: "claude-sonnet-4-20241022" matches
|
|
74
|
+
* "claude-sonnet-4" (best prefix).
|
|
75
|
+
*
|
|
76
|
+
* Returns `undefined` if no match is found. Never throws.
|
|
77
|
+
*
|
|
78
|
+
* @param model - The model identifier to look up (e.g. "gpt-4o-2024-08-06")
|
|
79
|
+
* @param table - Optional rate table (defaults to DEFAULT_RATES)
|
|
80
|
+
* @returns The matching ModelRate, or undefined on miss
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveRate(model: string, table?: Record<string, ModelRate>): ModelRate | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Compute estimated USD cost from token usage.
|
|
85
|
+
*
|
|
86
|
+
* ## Cache accounting contract
|
|
87
|
+
*
|
|
88
|
+
* Hosts disagree on whether `usage.input` already includes cached tokens:
|
|
89
|
+
* - **Codex**: `input_tokens` includes `cached_input_tokens` (overlap).
|
|
90
|
+
* - **Claude / OpenCode (typical)**: `input` is non-cached; `cacheRead` is
|
|
91
|
+
* disjoint and additional.
|
|
92
|
+
*
|
|
93
|
+
* Heuristic (never double-bill, never under-bill the common cases):
|
|
94
|
+
* - If `0 < cacheRead <= input`, treat cache as a **subset** of input
|
|
95
|
+
* (Codex-style): bill `(input - cacheRead)` at the input rate and
|
|
96
|
+
* `cacheRead` at the cache-read rate.
|
|
97
|
+
* - Otherwise treat input and cacheRead as **disjoint**: bill full `input`
|
|
98
|
+
* at the input rate plus `cacheRead` at the cache-read rate.
|
|
99
|
+
*
|
|
100
|
+
* Cache-write tokens are always billed separately when a write rate exists
|
|
101
|
+
* (they are never part of `input`).
|
|
102
|
+
*
|
|
103
|
+
* Formula (known rate):
|
|
104
|
+
* nonCachedInput = (cacheRead > 0 && cacheRead <= input)
|
|
105
|
+
* ? input - cacheRead : input
|
|
106
|
+
* cost = (nonCachedInput * inputRate + output * outputRate +
|
|
107
|
+
* cacheRead * cacheReadRate + cacheWrite * cacheWriteRate) / 1_000_000
|
|
108
|
+
*
|
|
109
|
+
* If the model cannot be resolved, falls back to
|
|
110
|
+
* `FLAT_FALLBACK_PER_TOKEN * (input + output)`. Cache tokens are *not* counted
|
|
111
|
+
* in the fallback (avoids double-count when input already includes them).
|
|
112
|
+
*
|
|
113
|
+
* Pure function — no I/O, no side effects. Never throws.
|
|
114
|
+
*
|
|
115
|
+
* @param usage - Token counts (input, output, cacheRead, cacheWrite)
|
|
116
|
+
* @param model - Model identifier used to resolve the rate
|
|
117
|
+
* @param table - Optional rate table override (passed through to resolveRate)
|
|
118
|
+
* @returns Estimated cost in USD
|
|
119
|
+
*/
|
|
120
|
+
export declare function estimateCost(usage: UsageStats, model: string, table?: Record<string, ModelRate>): number;
|
|
121
|
+
//# sourceMappingURL=rates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rates.d.ts","sourceRoot":"","sources":["../src/rates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACzB,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAMD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAkC1C,CAAC;AAMX;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAMhD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAC1B,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAC/B,SAAS,GAAG,SAAS,CAsBvB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAC/B,MAAM,CAwBR"}
|
package/dist/rates.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model rate registry — pure token-to-USD cost estimation.
|
|
3
|
+
*
|
|
4
|
+
* ## CODEX COST GAP
|
|
5
|
+
*
|
|
6
|
+
* The `codex-runner` (packages/codex-taskflow) emits NO cost field in its usage
|
|
7
|
+
* response, so `usage.cost` stays 0 on Codex even when tokens are accurately
|
|
8
|
+
* reported. This makes `budget.maxUSD` silently non-functional for Codex-hosted
|
|
9
|
+
* flows. This module fills the gap: batch 2 of the runner layer (or a post-hoc
|
|
10
|
+
* cost fill) can call `estimateCost()` to compute USD from the known model +
|
|
11
|
+
* token counts, making Codex cost-aware without changing the runner protocol.
|
|
12
|
+
*
|
|
13
|
+
* ## Link to verify.ts
|
|
14
|
+
*
|
|
15
|
+
* `verify.ts` defines `ESTIMATED_COST_PER_PHASE = 0.001` as a crude lower-bound
|
|
16
|
+
* budget guard. `FLAT_FALLBACK_PER_TOKEN` serves the same role for unknown models
|
|
17
|
+
* — a rough-order-of-magnitude per-token default (~$1/1M tokens, consistent with
|
|
18
|
+
* $0.001/1000-tokens at verify's granularity).
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Default rate table
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/**
|
|
26
|
+
* Built-in rate table keyed by **model-family prefix** (case-sensitive lookup
|
|
27
|
+
* key — matching in `resolveRate` is case-insensitive).
|
|
28
|
+
*
|
|
29
|
+
* This is a small, illustrative, SPARSE table. Hosts can inject a richer table
|
|
30
|
+
* at runtime via the optional `table` parameter to `resolveRate`/`estimateCost`.
|
|
31
|
+
*
|
|
32
|
+
* Prices are approximate and may drift from provider publishing. For production
|
|
33
|
+
* billing, always use the provider's official cost API.
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* // Example override from a host adapter:
|
|
37
|
+
* const myRates = { ...DEFAULT_RATES, "my-model": { inputPer1M: 1, outputPer1M: 4 } };
|
|
38
|
+
* const cost = estimateCost(usage, "my-model", myRates);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export const DEFAULT_RATES = {
|
|
42
|
+
// ── OpenAI ──────────────────────────────────────────────────────────
|
|
43
|
+
/** gpt-4o family (including gpt-4o-*, gpt-4o-mini) */
|
|
44
|
+
"gpt-4o": { inputPer1M: 2.50, outputPer1M: 10.00, cacheReadPer1M: 1.25 },
|
|
45
|
+
/** gpt-4o-mini (cheap, fast) */
|
|
46
|
+
"gpt-4o-mini": { inputPer1M: 0.15, outputPer1M: 0.60, cacheReadPer1M: 0.075 },
|
|
47
|
+
/** o3-mini reasoning model */
|
|
48
|
+
"o3-mini": { inputPer1M: 1.10, outputPer1M: 4.40, cacheReadPer1M: 0.55 },
|
|
49
|
+
/** o1 reasoning model */
|
|
50
|
+
o1: { inputPer1M: 15.00, outputPer1M: 60.00, cacheReadPer1M: 7.50 },
|
|
51
|
+
// ── Anthropic ───────────────────────────────────────────────────────
|
|
52
|
+
/** claude-sonnet-4 (and sonnet-4-*) */
|
|
53
|
+
"claude-sonnet-4": { inputPer1M: 3.00, outputPer1M: 15.00, cacheReadPer1M: 0.30, cacheWritePer1M: 3.75 },
|
|
54
|
+
/** claude-haiku-3.5 (and haiku-3.5-*) */
|
|
55
|
+
"claude-haiku-3.5": { inputPer1M: 0.80, outputPer1M: 4.00, cacheReadPer1M: 0.08, cacheWritePer1M: 1.00 },
|
|
56
|
+
/** claude-opus-4 (and opus-4-*) */
|
|
57
|
+
"claude-opus-4": { inputPer1M: 15.00, outputPer1M: 75.00, cacheReadPer1M: 1.50, cacheWritePer1M: 18.75 },
|
|
58
|
+
// ── DeepSeek ────────────────────────────────────────────────────────
|
|
59
|
+
/** deepseek-chat (V3) */
|
|
60
|
+
"deepseek-chat": { inputPer1M: 0.27, outputPer1M: 1.10, cacheReadPer1M: 0.07 },
|
|
61
|
+
/** deepseek-reasoner (R1) */
|
|
62
|
+
"deepseek-reasoner": { inputPer1M: 0.55, outputPer1M: 2.19, cacheReadPer1M: 0.14 },
|
|
63
|
+
// ── Google Gemini ───────────────────────────────────────────────────
|
|
64
|
+
/** gemini-2.5-flash-* */
|
|
65
|
+
"gemini-2.5-flash": { inputPer1M: 0.15, outputPer1M: 0.60, cacheReadPer1M: 0.075 },
|
|
66
|
+
/** gemini-2.5-pro-* */
|
|
67
|
+
"gemini-2.5-pro": { inputPer1M: 1.25, outputPer1M: 10.00, cacheReadPer1M: 0.3125 },
|
|
68
|
+
// ── Meta ────────────────────────────────────────────────────────────
|
|
69
|
+
/** llama-3.x (generic — actual pricing depends on host) */
|
|
70
|
+
"llama-3": { inputPer1M: 0.25, outputPer1M: 0.25 },
|
|
71
|
+
};
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Fallback
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
/**
|
|
76
|
+
* Flat per-token USD fallback used when no model rate can be resolved.
|
|
77
|
+
*
|
|
78
|
+
* Currently set to $1 per 1,000,000 tokens (~$0.001 per 1K tokens), consistent
|
|
79
|
+
* with `verify.ts`'s `ESTIMATED_COST_PER_PHASE = 0.001` rough-order-of-magnitude
|
|
80
|
+
* estimate. This avoids silent zero-cost for unknown models while staying in the
|
|
81
|
+
* right ballpark for most hosted LLM APIs.
|
|
82
|
+
*/
|
|
83
|
+
export const FLAT_FALLBACK_PER_TOKEN = 0.000001; // $1/1M tokens
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Resolution
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Resolve a model identifier to a `ModelRate` via case-insensitive prefix match
|
|
89
|
+
* against the provided table (defaults to `DEFAULT_RATES`).
|
|
90
|
+
*
|
|
91
|
+
* Matching strategy:
|
|
92
|
+
* 1. Exact case-insensitive key match.
|
|
93
|
+
* 2. Longest prefix match (case-insensitive), e.g. "gpt-4o-2024-08-06" matches
|
|
94
|
+
* the "gpt-4o" key.
|
|
95
|
+
* 3. Falls back to single-word prefix: "claude-sonnet-4-20241022" matches
|
|
96
|
+
* "claude-sonnet-4" (best prefix).
|
|
97
|
+
*
|
|
98
|
+
* Returns `undefined` if no match is found. Never throws.
|
|
99
|
+
*
|
|
100
|
+
* @param model - The model identifier to look up (e.g. "gpt-4o-2024-08-06")
|
|
101
|
+
* @param table - Optional rate table (defaults to DEFAULT_RATES)
|
|
102
|
+
* @returns The matching ModelRate, or undefined on miss
|
|
103
|
+
*/
|
|
104
|
+
export function resolveRate(model, table) {
|
|
105
|
+
if (!model)
|
|
106
|
+
return undefined;
|
|
107
|
+
const tbl = table ?? DEFAULT_RATES;
|
|
108
|
+
const lowerModel = model.toLowerCase();
|
|
109
|
+
// 1. Exact case-insensitive match
|
|
110
|
+
const exact = Object.keys(tbl).find((k) => k.toLowerCase() === lowerModel);
|
|
111
|
+
if (exact)
|
|
112
|
+
return tbl[exact];
|
|
113
|
+
// 2. Longest prefix match — iterate to find the key that is a prefix of the
|
|
114
|
+
// model (case-insensitive) with the greatest length.
|
|
115
|
+
let best;
|
|
116
|
+
for (const key of Object.keys(tbl)) {
|
|
117
|
+
const lowerKey = key.toLowerCase();
|
|
118
|
+
if (lowerModel.startsWith(lowerKey) && lowerKey.length > (best?.length ?? 0)) {
|
|
119
|
+
best = { key, length: lowerKey.length };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (best)
|
|
123
|
+
return tbl[best.key];
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Cost estimation
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
/**
|
|
130
|
+
* Compute estimated USD cost from token usage.
|
|
131
|
+
*
|
|
132
|
+
* ## Cache accounting contract
|
|
133
|
+
*
|
|
134
|
+
* Hosts disagree on whether `usage.input` already includes cached tokens:
|
|
135
|
+
* - **Codex**: `input_tokens` includes `cached_input_tokens` (overlap).
|
|
136
|
+
* - **Claude / OpenCode (typical)**: `input` is non-cached; `cacheRead` is
|
|
137
|
+
* disjoint and additional.
|
|
138
|
+
*
|
|
139
|
+
* Heuristic (never double-bill, never under-bill the common cases):
|
|
140
|
+
* - If `0 < cacheRead <= input`, treat cache as a **subset** of input
|
|
141
|
+
* (Codex-style): bill `(input - cacheRead)` at the input rate and
|
|
142
|
+
* `cacheRead` at the cache-read rate.
|
|
143
|
+
* - Otherwise treat input and cacheRead as **disjoint**: bill full `input`
|
|
144
|
+
* at the input rate plus `cacheRead` at the cache-read rate.
|
|
145
|
+
*
|
|
146
|
+
* Cache-write tokens are always billed separately when a write rate exists
|
|
147
|
+
* (they are never part of `input`).
|
|
148
|
+
*
|
|
149
|
+
* Formula (known rate):
|
|
150
|
+
* nonCachedInput = (cacheRead > 0 && cacheRead <= input)
|
|
151
|
+
* ? input - cacheRead : input
|
|
152
|
+
* cost = (nonCachedInput * inputRate + output * outputRate +
|
|
153
|
+
* cacheRead * cacheReadRate + cacheWrite * cacheWriteRate) / 1_000_000
|
|
154
|
+
*
|
|
155
|
+
* If the model cannot be resolved, falls back to
|
|
156
|
+
* `FLAT_FALLBACK_PER_TOKEN * (input + output)`. Cache tokens are *not* counted
|
|
157
|
+
* in the fallback (avoids double-count when input already includes them).
|
|
158
|
+
*
|
|
159
|
+
* Pure function — no I/O, no side effects. Never throws.
|
|
160
|
+
*
|
|
161
|
+
* @param usage - Token counts (input, output, cacheRead, cacheWrite)
|
|
162
|
+
* @param model - Model identifier used to resolve the rate
|
|
163
|
+
* @param table - Optional rate table override (passed through to resolveRate)
|
|
164
|
+
* @returns Estimated cost in USD
|
|
165
|
+
*/
|
|
166
|
+
export function estimateCost(usage, model, table) {
|
|
167
|
+
const rate = resolveRate(model, table);
|
|
168
|
+
if (!rate) {
|
|
169
|
+
return FLAT_FALLBACK_PER_TOKEN * (usage.input + usage.output);
|
|
170
|
+
}
|
|
171
|
+
// Overlap heuristic: when cacheRead is a positive subset of input, peel it
|
|
172
|
+
// out so it is not billed at the full input rate (Codex-style hosts).
|
|
173
|
+
const nonCachedInput = usage.cacheRead > 0 && usage.cacheRead <= usage.input
|
|
174
|
+
? usage.input - usage.cacheRead
|
|
175
|
+
: usage.input;
|
|
176
|
+
const inputCost = (nonCachedInput / 1_000_000) * rate.inputPer1M;
|
|
177
|
+
const outputCost = (usage.output / 1_000_000) * rate.outputPer1M;
|
|
178
|
+
let cacheCost = 0;
|
|
179
|
+
if (rate.cacheReadPer1M !== undefined && usage.cacheRead > 0) {
|
|
180
|
+
cacheCost += (usage.cacheRead / 1_000_000) * rate.cacheReadPer1M;
|
|
181
|
+
}
|
|
182
|
+
if (rate.cacheWritePer1M !== undefined && usage.cacheWrite > 0) {
|
|
183
|
+
cacheCost += (usage.cacheWrite / 1_000_000) * rate.cacheWritePer1M;
|
|
184
|
+
}
|
|
185
|
+
return inputCost + outputCost + cacheCost;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=rates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rates.js","sourceRoot":"","sources":["../src/rates.ts"],"names":[],"mappings":"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"}
|
package/dist/replay.d.ts
CHANGED
|
@@ -1,75 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Deterministic replay —
|
|
2
|
+
* Deterministic replay — re-fold a recorded event log under alternate **decision
|
|
3
|
+
* knobs** without calling the model (RFC §7, S3).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `runTask`**. Zero tokens, offline. Because the DAG and its verdicts are data,
|
|
8
|
-
* you can re-adjudicate them against recorded evidence.
|
|
5
|
+
* **Import graph guard (structural):** this module imports only pure packages
|
|
6
|
+
* (`exec/events`, `exec/fold`, `deterministic`, `scorers`). It must NEVER import
|
|
7
|
+
* `runtime.ts` or `exec/driver` / `exec/step` (those drag process-spawning).
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
* function and the `replay` action land in 0.2.0. The `ReplayDecision` type is
|
|
12
|
-
* defined now so the trace schema and the diff-report contract are fixed before
|
|
13
|
-
* any events are emitted — avoiding a breaking migration later.
|
|
14
|
-
*
|
|
15
|
-
* The 0.2.0 `replayRun()` will import ONLY from pure modules:
|
|
16
|
-
* `trace.ts` (read), `schema.ts` (topo sort/desugar),
|
|
17
|
-
* `interpolate.ts` (condition re-eval), `deterministic.ts` (parseGateVerdict,
|
|
18
|
-
* overBudget), `scorers.ts` (pure scorer re-run).
|
|
19
|
-
* It must NOT import `runtime.ts` (which drags in the process-spawning runner)
|
|
20
|
-
* — the "replay never spends a token" invariant is enforced structurally by the
|
|
21
|
-
* import graph.
|
|
9
|
+
* 0.1.7 shipped types only. 0.2.0 implements {@link replayRun}.
|
|
22
10
|
*/
|
|
11
|
+
import type { Event } from "./exec/events.ts";
|
|
12
|
+
import { type FoldedRun } from "./exec/fold.ts";
|
|
13
|
+
import { type UsageStats } from "./usage.ts";
|
|
23
14
|
/**
|
|
24
15
|
* Per-phase outcome of a replay. Distinct from `RecomputeDecision` (which
|
|
25
|
-
* describes a *live* recompute): only `"reused"` overlaps.
|
|
26
|
-
* `RecomputeDecision.outcome` with replay-specific values would be a semantic
|
|
27
|
-
* lie — replay and recompute are different operations (recorded vs. live).
|
|
16
|
+
* describes a *live* recompute): only `"reused"` overlaps.
|
|
28
17
|
*/
|
|
29
18
|
export interface ReplayDecision {
|
|
30
19
|
readonly phaseId: string;
|
|
31
|
-
readonly outcome:
|
|
32
|
-
/** Recorded output valid → reused verbatim (zero tokens). */
|
|
33
|
-
"reused"
|
|
34
|
-
/** A gate verdict flipped under the new threshold/scorers. */
|
|
35
|
-
| "verdict-flipped"
|
|
36
|
-
/** The phase would now BLOCK the flow (e.g. gate under a stricter threshold). */
|
|
37
|
-
| "would-block"
|
|
38
|
-
/** The phase would be skipped by a tighter budget cap. */
|
|
39
|
-
| "would-exceed-budget"
|
|
40
|
-
/** Replay cannot decide — a fresh model call is required. Conservative. */
|
|
41
|
-
| "needs-live-rerun"
|
|
42
|
-
/** A `when` guard now skips a phase that previously ran (or vice-versa). */
|
|
43
|
-
| "would-skip"
|
|
44
|
-
/** A score threshold changed (verdict may or may not have flipped). */
|
|
45
|
-
| "threshold-changed"
|
|
46
|
-
/** The phase failed in the recorded run; replay does not retry. */
|
|
47
|
-
| "failed";
|
|
20
|
+
readonly outcome: "reused" | "verdict-flipped" | "would-block" | "would-exceed-budget" | "needs-live-rerun" | "would-skip" | "threshold-changed" | "failed";
|
|
48
21
|
readonly reason: string;
|
|
49
|
-
/** The phase's outcome in the recorded run. */
|
|
50
22
|
readonly priorOutcome?: string;
|
|
51
|
-
/** The phase's outcome under the replayed knobs. */
|
|
52
23
|
readonly replayedOutcome?: string;
|
|
53
|
-
/** Upstream phases whose change caused this outcome, when applicable. */
|
|
54
24
|
readonly causedBy?: readonly string[];
|
|
55
25
|
}
|
|
56
26
|
/**
|
|
57
27
|
* Knobs a replay may override on a *copy* of the recorded definition (never the
|
|
58
|
-
* stored run).
|
|
59
|
-
* would require re-generation (a changed task prompt) routes to
|
|
60
|
-
* `needs-live-rerun`.
|
|
28
|
+
* stored run).
|
|
61
29
|
*/
|
|
62
30
|
export interface ReplayOverrides {
|
|
63
|
-
/** `budget.maxUSD` / `budget.maxTokens` — re-tally recorded usage. */
|
|
64
31
|
budgetMaxUSD?: number;
|
|
65
32
|
budgetMaxTokens?: number;
|
|
66
|
-
/** `phases.<id>.score.threshold` — re-
|
|
33
|
+
/** `phases.<id>.score.threshold` — re-judge recorded gate-score events. */
|
|
67
34
|
thresholds?: Record<string, number>;
|
|
68
|
-
/** `phases.<id>.model` —
|
|
35
|
+
/** `phases.<id>.model` — cost delta only (needs rates; otherwise needs-live-rerun). */
|
|
69
36
|
models?: Record<string, string>;
|
|
70
|
-
/** `args.*` —
|
|
37
|
+
/** `args.*` — text-changing args → needs-live-rerun for affected phases. */
|
|
71
38
|
args?: Record<string, unknown>;
|
|
72
39
|
}
|
|
73
|
-
/**
|
|
74
|
-
export
|
|
40
|
+
/** Result of {@link replayRun}. */
|
|
41
|
+
export interface ReplayReport {
|
|
42
|
+
readonly decisions: ReplayDecision[];
|
|
43
|
+
/** Fold under recorded knobs (baseline). */
|
|
44
|
+
readonly baseline: FoldedRun;
|
|
45
|
+
/** Fold after applying overrides (may equal baseline when no knobs change). */
|
|
46
|
+
readonly replayed: FoldedRun;
|
|
47
|
+
/** True if any phase needs a live model call. */
|
|
48
|
+
readonly needsLiveRerun: boolean;
|
|
49
|
+
/** Aggregate recorded usage (for budget re-check). */
|
|
50
|
+
readonly totalUsage: UsageStats;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Re-evaluate a recorded event log under optional decision overrides.
|
|
54
|
+
*
|
|
55
|
+
* - **No overrides** → every completed phase is `"reused"` (consistency oracle).
|
|
56
|
+
* - **thresholds[phaseId]** → re-compare recorded `gate-score.combined` to the
|
|
57
|
+
* new threshold; emit `verdict-flipped` / `would-block` / `threshold-changed`.
|
|
58
|
+
* - **budgetMax*** → re-tally recorded usage; phases that would not have run
|
|
59
|
+
* under a tighter cap → `would-exceed-budget`.
|
|
60
|
+
* - **models / args** → currently `needs-live-rerun` for any phase (cannot
|
|
61
|
+
* re-judge quality offline without more instrumentation).
|
|
62
|
+
*
|
|
63
|
+
* Never calls a model. Never throws.
|
|
64
|
+
*/
|
|
65
|
+
export declare function replayRun(events: readonly Event[], overrides?: ReplayOverrides): ReplayReport;
|
|
66
|
+
/** @deprecated kept for 0.1.7 callers that checked the sentinel string */
|
|
67
|
+
export declare const REPLAY_NOT_YET_IMPLEMENTED = "replayRun() is implemented in 0.2.0; this sentinel remains for back-compat string checks.";
|
|
75
68
|
//# sourceMappingURL=replay.d.ts.map
|
package/dist/replay.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAiB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EACb,QAAQ,GACR,iBAAiB,GACjB,aAAa,GACb,qBAAqB,GACrB,kBAAkB,GAClB,YAAY,GACZ,mBAAmB,GACnB,QAAQ,CAAC;IACZ,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,SAAS,EAAE,cAAc,EAAE,CAAC;IACrC,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,iDAAiD;IACjD,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CAChC;AA6FD;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,EAAE,SAAS,GAAE,eAAoB,GAAG,YAAY,CAigBjG;AAED,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,8FACqD,CAAC"}
|