@amalgm/chat 0.1.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/PURPOSE.md +107 -0
- package/README.md +41 -0
- package/dist/contract/chat-input.d.ts +144 -0
- package/dist/contract/chat-input.d.ts.map +1 -0
- package/dist/contract/chat-input.js +261 -0
- package/dist/contract/chat-input.js.map +1 -0
- package/dist/contract/create.d.ts +191 -0
- package/dist/contract/create.d.ts.map +1 -0
- package/dist/contract/create.js +249 -0
- package/dist/contract/create.js.map +1 -0
- package/dist/contract/freeze.d.ts +85 -0
- package/dist/contract/freeze.d.ts.map +1 -0
- package/dist/contract/freeze.js +151 -0
- package/dist/contract/freeze.js.map +1 -0
- package/dist/contract/harnesses.d.ts +21 -0
- package/dist/contract/harnesses.d.ts.map +1 -0
- package/dist/contract/harnesses.js +39 -0
- package/dist/contract/harnesses.js.map +1 -0
- package/dist/contract/index.d.ts +17 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +17 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/input.d.ts +45 -0
- package/dist/contract/input.d.ts.map +1 -0
- package/dist/contract/input.js +78 -0
- package/dist/contract/input.js.map +1 -0
- package/dist/contract/models.d.ts +57 -0
- package/dist/contract/models.d.ts.map +1 -0
- package/dist/contract/models.js +380 -0
- package/dist/contract/models.js.map +1 -0
- package/dist/contract/payload-merge.d.ts +76 -0
- package/dist/contract/payload-merge.d.ts.map +1 -0
- package/dist/contract/payload-merge.js +215 -0
- package/dist/contract/payload-merge.js.map +1 -0
- package/dist/events/constructors.d.ts +53 -0
- package/dist/events/constructors.d.ts.map +1 -0
- package/dist/events/constructors.js +217 -0
- package/dist/events/constructors.js.map +1 -0
- package/dist/events/index.d.ts +12 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +12 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/schema.d.ts +455 -0
- package/dist/events/schema.d.ts.map +1 -0
- package/dist/events/schema.js +228 -0
- package/dist/events/schema.js.map +1 -0
- package/dist/events/tool-display.d.ts +22 -0
- package/dist/events/tool-display.d.ts.map +1 -0
- package/dist/events/tool-display.js +60 -0
- package/dist/events/tool-display.js.map +1 -0
- package/dist/events/tool-shape.d.ts +48 -0
- package/dist/events/tool-shape.d.ts.map +1 -0
- package/dist/events/tool-shape.js +225 -0
- package/dist/events/tool-shape.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/normalizers/claude.d.ts +628 -0
- package/dist/normalizers/claude.d.ts.map +1 -0
- package/dist/normalizers/claude.js +603 -0
- package/dist/normalizers/claude.js.map +1 -0
- package/dist/normalizers/codex.d.ts +352 -0
- package/dist/normalizers/codex.d.ts.map +1 -0
- package/dist/normalizers/codex.js +485 -0
- package/dist/normalizers/codex.js.map +1 -0
- package/dist/normalizers/cursor.d.ts +34 -0
- package/dist/normalizers/cursor.d.ts.map +1 -0
- package/dist/normalizers/cursor.js +197 -0
- package/dist/normalizers/cursor.js.map +1 -0
- package/dist/normalizers/index.d.ts +13 -0
- package/dist/normalizers/index.d.ts.map +1 -0
- package/dist/normalizers/index.js +13 -0
- package/dist/normalizers/index.js.map +1 -0
- package/dist/normalizers/opencode.d.ts +81 -0
- package/dist/normalizers/opencode.d.ts.map +1 -0
- package/dist/normalizers/opencode.js +640 -0
- package/dist/normalizers/opencode.js.map +1 -0
- package/dist/normalizers/pi.d.ts +28 -0
- package/dist/normalizers/pi.d.ts.map +1 -0
- package/dist/normalizers/pi.js +160 -0
- package/dist/normalizers/pi.js.map +1 -0
- package/dist/ports.d.ts +14 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +7 -0
- package/dist/ports.js.map +1 -0
- package/dist/transcript/index.d.ts +14 -0
- package/dist/transcript/index.d.ts.map +1 -0
- package/dist/transcript/index.js +12 -0
- package/dist/transcript/index.js.map +1 -0
- package/dist/transcript/message-reducer.d.ts +167 -0
- package/dist/transcript/message-reducer.d.ts.map +1 -0
- package/dist/transcript/message-reducer.js +400 -0
- package/dist/transcript/message-reducer.js.map +1 -0
- package/dist/transcript/part-accumulator.d.ts +24 -0
- package/dist/transcript/part-accumulator.d.ts.map +1 -0
- package/dist/transcript/part-accumulator.js +30 -0
- package/dist/transcript/part-accumulator.js.map +1 -0
- package/dist/transcript/tool-display.d.ts +14 -0
- package/dist/transcript/tool-display.d.ts.map +1 -0
- package/dist/transcript/tool-display.js +125 -0
- package/dist/transcript/tool-display.js.map +1 -0
- package/dist/transcript/tool-shape.d.ts +34 -0
- package/dist/transcript/tool-shape.d.ts.map +1 -0
- package/dist/transcript/tool-shape.js +216 -0
- package/dist/transcript/tool-shape.js.map +1 -0
- package/dist/turns/index.d.ts +11 -0
- package/dist/turns/index.d.ts.map +1 -0
- package/dist/turns/index.js +11 -0
- package/dist/turns/index.js.map +1 -0
- package/dist/turns/queries.d.ts +36 -0
- package/dist/turns/queries.d.ts.map +1 -0
- package/dist/turns/queries.js +37 -0
- package/dist/turns/queries.js.map +1 -0
- package/dist/turns/replay.d.ts +105 -0
- package/dist/turns/replay.d.ts.map +1 -0
- package/dist/turns/replay.js +163 -0
- package/dist/turns/replay.js.map +1 -0
- package/dist/turns/status.d.ts +49 -0
- package/dist/turns/status.d.ts.map +1 -0
- package/dist/turns/status.js +56 -0
- package/dist/turns/status.js.map +1 -0
- package/dist/turns/turn-store.d.ts +109 -0
- package/dist/turns/turn-store.d.ts.map +1 -0
- package/dist/turns/turn-store.js +169 -0
- package/dist/turns/turn-store.js.map +1 -0
- package/dist/usage/finalize.d.ts +57 -0
- package/dist/usage/finalize.d.ts.map +1 -0
- package/dist/usage/finalize.js +139 -0
- package/dist/usage/finalize.js.map +1 -0
- package/dist/usage/index.d.ts +11 -0
- package/dist/usage/index.d.ts.map +1 -0
- package/dist/usage/index.js +11 -0
- package/dist/usage/index.js.map +1 -0
- package/dist/usage/model-catalog.d.ts +50 -0
- package/dist/usage/model-catalog.d.ts.map +1 -0
- package/dist/usage/model-catalog.js +344 -0
- package/dist/usage/model-catalog.js.map +1 -0
- package/dist/usage/normalize.d.ts +103 -0
- package/dist/usage/normalize.d.ts.map +1 -0
- package/dist/usage/normalize.js +71 -0
- package/dist/usage/normalize.js.map +1 -0
- package/dist/usage/ports.d.ts +141 -0
- package/dist/usage/ports.d.ts.map +1 -0
- package/dist/usage/ports.js +113 -0
- package/dist/usage/ports.js.map +1 -0
- package/dist/usage/tokens.d.ts +79 -0
- package/dist/usage/tokens.d.ts.map +1 -0
- package/dist/usage/tokens.js +98 -0
- package/dist/usage/tokens.js.map +1 -0
- package/dist/warm/client-cache.d.ts +47 -0
- package/dist/warm/client-cache.d.ts.map +1 -0
- package/dist/warm/client-cache.js +48 -0
- package/dist/warm/client-cache.js.map +1 -0
- package/dist/warm/envelope-cache.d.ts +79 -0
- package/dist/warm/envelope-cache.d.ts.map +1 -0
- package/dist/warm/envelope-cache.js +85 -0
- package/dist/warm/envelope-cache.js.map +1 -0
- package/dist/warm/index.d.ts +16 -0
- package/dist/warm/index.d.ts.map +1 -0
- package/dist/warm/index.js +11 -0
- package/dist/warm/index.js.map +1 -0
- package/dist/warm/payload.d.ts +137 -0
- package/dist/warm/payload.d.ts.map +1 -0
- package/dist/warm/payload.js +112 -0
- package/dist/warm/payload.js.map +1 -0
- package/dist/warm/revision.d.ts +70 -0
- package/dist/warm/revision.d.ts.map +1 -0
- package/dist/warm/revision.js +63 -0
- package/dist/warm/revision.js.map +1 -0
- package/dist/warm/store-policy.d.ts +57 -0
- package/dist/warm/store-policy.d.ts.map +1 -0
- package/dist/warm/store-policy.js +89 -0
- package/dist/warm/store-policy.js.map +1 -0
- package/dist/wire/frames.d.ts +208 -0
- package/dist/wire/frames.d.ts.map +1 -0
- package/dist/wire/frames.js +245 -0
- package/dist/wire/frames.js.map +1 -0
- package/dist/wire/index.d.ts +7 -0
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/wire/index.js +7 -0
- package/dist/wire/index.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage normalization — engine `chat-core/usage.js:65-116,204-206`, ported
|
|
3
|
+
* byte-exact. Every provider-flavored usage payload (Claude SDK snake_case,
|
|
4
|
+
* OpenAI camelCase, codex cumulative meters, opencode step records) funnels
|
|
5
|
+
* through `normalizeUsage` into one canonical `NormalizedUsage` shape.
|
|
6
|
+
*/
|
|
7
|
+
import { type TokenList } from './tokens.js';
|
|
8
|
+
/**
|
|
9
|
+
* Engine `positiveOrNull()`: the numeric value when finite and > 0, else
|
|
10
|
+
* null.
|
|
11
|
+
*/
|
|
12
|
+
export declare function positiveOrNull(value: unknown): number | null;
|
|
13
|
+
/**
|
|
14
|
+
* Loose input accepted by `normalizeUsage` — the union of every key
|
|
15
|
+
* spelling the engine recognizes across providers.
|
|
16
|
+
*/
|
|
17
|
+
export interface UsageInput {
|
|
18
|
+
tokenList?: unknown;
|
|
19
|
+
cumulativeTokenList?: unknown;
|
|
20
|
+
previousCumulativeTokenList?: unknown;
|
|
21
|
+
/** Non-cached input tokens — unless `inputTokensAreTotal` is set. */
|
|
22
|
+
inputTokens?: unknown;
|
|
23
|
+
input_tokens?: unknown;
|
|
24
|
+
nonCachedInputTokens?: unknown;
|
|
25
|
+
/** Total input including cache buckets; decomposed into non-cached input. */
|
|
26
|
+
totalInputTokens?: unknown;
|
|
27
|
+
promptTokens?: unknown;
|
|
28
|
+
/** When truthy, `inputTokens` is treated as a total-input figure. */
|
|
29
|
+
inputTokensAreTotal?: unknown;
|
|
30
|
+
outputTokens?: unknown;
|
|
31
|
+
completionTokens?: unknown;
|
|
32
|
+
output_tokens?: unknown;
|
|
33
|
+
cacheReadTokens?: unknown;
|
|
34
|
+
cachedInputTokens?: unknown;
|
|
35
|
+
cache_read_input_tokens?: unknown;
|
|
36
|
+
cacheWriteTokens?: unknown;
|
|
37
|
+
cacheCreationInputTokens?: unknown;
|
|
38
|
+
cache_creation_input_tokens?: unknown;
|
|
39
|
+
thoughtTokens?: unknown;
|
|
40
|
+
reasoningTokens?: unknown;
|
|
41
|
+
inputContextSize?: unknown;
|
|
42
|
+
input_context_size?: unknown;
|
|
43
|
+
contextUsedTokens?: unknown;
|
|
44
|
+
usedTokens?: unknown;
|
|
45
|
+
lastUsedTokens?: unknown;
|
|
46
|
+
contextLimit?: unknown;
|
|
47
|
+
maxTokens?: unknown;
|
|
48
|
+
modelContextWindow?: unknown;
|
|
49
|
+
totalProcessedTokens?: unknown;
|
|
50
|
+
costUsd?: unknown;
|
|
51
|
+
costUSD?: unknown;
|
|
52
|
+
cost?: unknown;
|
|
53
|
+
marketCostUsd?: unknown;
|
|
54
|
+
operation?: unknown;
|
|
55
|
+
usageOperation?: unknown;
|
|
56
|
+
billable?: unknown;
|
|
57
|
+
source?: unknown;
|
|
58
|
+
exact?: unknown;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* The canonical usage record. Laws:
|
|
62
|
+
* - `costUsd`/`marketCostUsd` are null when unknown — never zero. Zero
|
|
63
|
+
* means "measured as free"; null means "not measured".
|
|
64
|
+
* - `inputTokens` always means non-cached input; total-input figures are
|
|
65
|
+
* decomposed by subtracting the cache buckets (clamped at 0).
|
|
66
|
+
* - `inputContextSize` is the next-turn context footprint (defaults to the
|
|
67
|
+
* tokenList total).
|
|
68
|
+
* - `source` is a free-form provenance string (engine default `'runtime'`);
|
|
69
|
+
* well-known values include `'claude_agent_sdk_model_usage'`,
|
|
70
|
+
* `'codex_app_server_turn_delta'`, `'codex_app_server_step_sum'`,
|
|
71
|
+
* `'pi_rpc_step_sum'`, `'turn_sum'`, `'native_chat_turn'`.
|
|
72
|
+
*/
|
|
73
|
+
export interface NormalizedUsage {
|
|
74
|
+
tokenList: TokenList;
|
|
75
|
+
cumulativeTokenList: TokenList | null;
|
|
76
|
+
previousCumulativeTokenList: TokenList | null;
|
|
77
|
+
inputTokens: number;
|
|
78
|
+
outputTokens: number;
|
|
79
|
+
cacheReadTokens: number;
|
|
80
|
+
cacheWriteTokens: number;
|
|
81
|
+
thoughtTokens: number;
|
|
82
|
+
inputContextSize: number;
|
|
83
|
+
contextUsedTokens: number | null;
|
|
84
|
+
contextLimit: number | null;
|
|
85
|
+
usedTokens: number | null;
|
|
86
|
+
lastUsedTokens: number | null;
|
|
87
|
+
maxTokens: number | null;
|
|
88
|
+
totalProcessedTokens: number | null;
|
|
89
|
+
costUsd: number | null;
|
|
90
|
+
marketCostUsd: number | null;
|
|
91
|
+
operation: string;
|
|
92
|
+
billable: boolean;
|
|
93
|
+
source: string;
|
|
94
|
+
exact: boolean;
|
|
95
|
+
}
|
|
96
|
+
/** Engine `normalizeUsage()`, byte-exact including every fallback chain. */
|
|
97
|
+
export declare function normalizeUsage(input?: UsageInput): NormalizedUsage;
|
|
98
|
+
/**
|
|
99
|
+
* Engine `providerFromModel()`: the provider prefix of a slash-qualified
|
|
100
|
+
* model id (`'anthropic/claude-sonnet-4.6'` → `'anthropic'`), else null.
|
|
101
|
+
*/
|
|
102
|
+
export declare function providerFromModel(modelId: unknown): string | null;
|
|
103
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/usage/normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage normalization — engine `chat-core/usage.js:65-116,204-206`, ported
|
|
3
|
+
* byte-exact. Every provider-flavored usage payload (Claude SDK snake_case,
|
|
4
|
+
* OpenAI camelCase, codex cumulative meters, opencode step records) funnels
|
|
5
|
+
* through `normalizeUsage` into one canonical `NormalizedUsage` shape.
|
|
6
|
+
*/
|
|
7
|
+
import { n, nonNegativeOrNull, normalizeTokenList } from './tokens.js';
|
|
8
|
+
/**
|
|
9
|
+
* Engine `positiveOrNull()`: the numeric value when finite and > 0, else
|
|
10
|
+
* null.
|
|
11
|
+
*/
|
|
12
|
+
export function positiveOrNull(value) {
|
|
13
|
+
const parsed = Number(value);
|
|
14
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
15
|
+
}
|
|
16
|
+
/** Engine `normalizeUsage()`, byte-exact including every fallback chain. */
|
|
17
|
+
export function normalizeUsage(input = {}) {
|
|
18
|
+
const cacheRead = n(input.cacheReadTokens ?? input.cachedInputTokens ?? input.cache_read_input_tokens);
|
|
19
|
+
const cacheWrite = n(input.cacheWriteTokens ?? input.cacheCreationInputTokens ?? input.cache_creation_input_tokens);
|
|
20
|
+
const output = n(input.outputTokens ?? input.completionTokens ?? input.output_tokens);
|
|
21
|
+
const thought = n(input.thoughtTokens ?? input.reasoningTokens);
|
|
22
|
+
const totalInput = input.totalInputTokens ?? input.promptTokens ?? (input.inputTokensAreTotal ? input.inputTokens : null);
|
|
23
|
+
const nonCachedInput = totalInput == null
|
|
24
|
+
? n(input.inputTokens ?? input.input_tokens ?? input.nonCachedInputTokens)
|
|
25
|
+
: Math.max(0, n(totalInput) - cacheRead - cacheWrite);
|
|
26
|
+
const tokenList = normalizeTokenList({
|
|
27
|
+
tokenList: input.tokenList,
|
|
28
|
+
inputTokens: nonCachedInput,
|
|
29
|
+
outputTokens: output,
|
|
30
|
+
cacheReadTokens: cacheRead,
|
|
31
|
+
cacheWriteTokens: cacheWrite,
|
|
32
|
+
thoughtTokens: thought,
|
|
33
|
+
});
|
|
34
|
+
const inputContextSize = nonNegativeOrNull(input.inputContextSize ?? input.input_context_size)
|
|
35
|
+
?? tokenList.total;
|
|
36
|
+
return {
|
|
37
|
+
tokenList,
|
|
38
|
+
cumulativeTokenList: input.cumulativeTokenList && typeof input.cumulativeTokenList === 'object'
|
|
39
|
+
? normalizeTokenList({ tokenList: input.cumulativeTokenList })
|
|
40
|
+
: null,
|
|
41
|
+
previousCumulativeTokenList: input.previousCumulativeTokenList && typeof input.previousCumulativeTokenList === 'object'
|
|
42
|
+
? normalizeTokenList({ tokenList: input.previousCumulativeTokenList })
|
|
43
|
+
: null,
|
|
44
|
+
inputTokens: nonCachedInput,
|
|
45
|
+
outputTokens: output,
|
|
46
|
+
cacheReadTokens: cacheRead,
|
|
47
|
+
cacheWriteTokens: cacheWrite,
|
|
48
|
+
thoughtTokens: thought,
|
|
49
|
+
inputContextSize,
|
|
50
|
+
contextUsedTokens: positiveOrNull(input.contextUsedTokens ?? input.usedTokens ?? inputContextSize),
|
|
51
|
+
contextLimit: positiveOrNull(input.contextLimit ?? input.maxTokens ?? input.modelContextWindow),
|
|
52
|
+
usedTokens: positiveOrNull(input.usedTokens ?? input.contextUsedTokens ?? inputContextSize),
|
|
53
|
+
lastUsedTokens: positiveOrNull(input.lastUsedTokens ?? input.contextUsedTokens ?? input.usedTokens ?? inputContextSize),
|
|
54
|
+
maxTokens: positiveOrNull(input.maxTokens ?? input.contextLimit ?? input.modelContextWindow),
|
|
55
|
+
totalProcessedTokens: positiveOrNull(input.totalProcessedTokens),
|
|
56
|
+
costUsd: (input.costUsd ?? input.costUSD ?? input.cost ?? null),
|
|
57
|
+
marketCostUsd: (input.marketCostUsd ?? null),
|
|
58
|
+
operation: String(input.operation || input.usageOperation || 'chat_step'),
|
|
59
|
+
billable: input.billable !== false,
|
|
60
|
+
source: (input.source || 'runtime'),
|
|
61
|
+
exact: input.exact === true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Engine `providerFromModel()`: the provider prefix of a slash-qualified
|
|
66
|
+
* model id (`'anthropic/claude-sonnet-4.6'` → `'anthropic'`), else null.
|
|
67
|
+
*/
|
|
68
|
+
export function providerFromModel(modelId) {
|
|
69
|
+
return typeof modelId === 'string' && modelId.includes('/') ? modelId.split('/')[0] ?? null : null;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/usage/normalize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage host boundary — the ownership DECISIONS from engine
|
|
3
|
+
* `chat-core/usage.js` (`beginProxyTurn` :145-148,:159-173 and
|
|
4
|
+
* `logTurnUsage` :325-351) ported as pure functions, plus the port
|
|
5
|
+
* interfaces the host implements for the side effects (the proxy
|
|
6
|
+
* turn-start POST and the usage_logs db write) and the exact
|
|
7
|
+
* `usage_logs` row shape from engine `chat-server/db.js
|
|
8
|
+
* buildUsageLogRow` (:507).
|
|
9
|
+
*
|
|
10
|
+
* Ownership law: when `authMethod === 'amalgm'` the platform proxy meters
|
|
11
|
+
* and persists usage itself — the runtime must NOT double-log. Every other
|
|
12
|
+
* auth method is user-owned: the runtime finalizes the turn locally and
|
|
13
|
+
* writes one usage row, pricing it from the local catalog when the
|
|
14
|
+
* provider gave no cost.
|
|
15
|
+
*/
|
|
16
|
+
import { type TurnUsageRecord, type UsageContract } from './finalize.js';
|
|
17
|
+
import type { NormalizedUsage } from './normalize.js';
|
|
18
|
+
export type { UsageContract } from './finalize.js';
|
|
19
|
+
/** Body POSTed to the proxy's `/internal/turn-start` (engine :159-173). */
|
|
20
|
+
export interface BeginProxyTurnBody {
|
|
21
|
+
sessionId: string | undefined;
|
|
22
|
+
messageId: string | undefined;
|
|
23
|
+
agentId: string | undefined;
|
|
24
|
+
harnessId: string | undefined;
|
|
25
|
+
authMethod: string | undefined;
|
|
26
|
+
usageOwner: string;
|
|
27
|
+
modelId: string | undefined;
|
|
28
|
+
internalModelId: string | undefined;
|
|
29
|
+
cliModel: string | undefined;
|
|
30
|
+
contextWindow: number | null;
|
|
31
|
+
maxTokens: number | null;
|
|
32
|
+
cwd: string | undefined;
|
|
33
|
+
mcpServers: unknown;
|
|
34
|
+
}
|
|
35
|
+
export type BeginProxyTurnDecision = {
|
|
36
|
+
skipped: true;
|
|
37
|
+
reason: 'not_amalgm';
|
|
38
|
+
} | {
|
|
39
|
+
skipped: false;
|
|
40
|
+
body: BeginProxyTurnBody;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The pure fork of engine `beginProxyTurn()`: non-amalgm auth skips the
|
|
44
|
+
* proxy entirely (`'not_amalgm'`); amalgm auth yields the exact
|
|
45
|
+
* turn-start body for the host's `BillingPort.beginTurn`. Token refresh,
|
|
46
|
+
* the POST, the 401 retry, and error mapping are host concerns.
|
|
47
|
+
*/
|
|
48
|
+
export declare function decideBeginProxyTurn(contract: UsageContract): BeginProxyTurnDecision;
|
|
49
|
+
/**
|
|
50
|
+
* Host port for the proxy turn-start POST. The host owns base URL, token
|
|
51
|
+
* freshness (refresh + 401 retry-once), and mapping non-OK responses to
|
|
52
|
+
* coded errors (`AMALGM_PROXY_NOT_CONFIGURED`, `AMALGM_TURN_START_FAILED`,
|
|
53
|
+
* `balanceUsd` passthrough).
|
|
54
|
+
*/
|
|
55
|
+
export interface BillingPort {
|
|
56
|
+
beginTurn(body: BeginProxyTurnBody): Promise<unknown>;
|
|
57
|
+
}
|
|
58
|
+
/** Camel-case payload the engine hands to `db.logUsage` (:339-349). */
|
|
59
|
+
export interface UsageLogPayload {
|
|
60
|
+
userId: string | undefined;
|
|
61
|
+
sessionId: string | undefined;
|
|
62
|
+
messageId: string | undefined;
|
|
63
|
+
agentId: string | undefined;
|
|
64
|
+
harnessId: string | undefined;
|
|
65
|
+
authMethod: string | undefined;
|
|
66
|
+
modelId: string | undefined;
|
|
67
|
+
usage: NormalizedUsage;
|
|
68
|
+
marketCostUsd: number | null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The exact `usage_logs` row from engine `chat-server/db.js
|
|
72
|
+
* buildUsageLogRow` (:507). Note the boundary law: `NormalizedUsage`
|
|
73
|
+
* keeps unknown cost as null, but the persisted row coerces
|
|
74
|
+
* `cost_usd`/`market_cost_usd` to 0 (engine `?? 0`).
|
|
75
|
+
*/
|
|
76
|
+
export interface UsageLogRow {
|
|
77
|
+
user_id: string | undefined;
|
|
78
|
+
session_id: string | null;
|
|
79
|
+
message_id: string | null;
|
|
80
|
+
agent_id: string | null;
|
|
81
|
+
harness_id: string | null;
|
|
82
|
+
auth_method: string | null;
|
|
83
|
+
provider: string | null;
|
|
84
|
+
model: string | null;
|
|
85
|
+
input_tokens: number;
|
|
86
|
+
output_tokens: number;
|
|
87
|
+
cache_read_tokens: number;
|
|
88
|
+
cache_write_tokens: number;
|
|
89
|
+
cost_usd: number;
|
|
90
|
+
market_cost_usd: number;
|
|
91
|
+
container_id: string | null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Engine `buildUsageLogRow()`, byte-exact — except the engine's
|
|
95
|
+
* `process.env.AMALGM_CONTAINER_ID` read becomes the documented
|
|
96
|
+
* `options.containerId` parameter (falsy → null, matching the engine's
|
|
97
|
+
* `|| null`).
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildUsageLogRow({ userId, sessionId, messageId, agentId, harnessId, authMethod, modelId, usage, marketCostUsd }: UsageLogPayload, options?: {
|
|
100
|
+
containerId?: string | null | undefined;
|
|
101
|
+
}): UsageLogRow;
|
|
102
|
+
/** Host port for the usage_logs write (engine `db.logUsage`). */
|
|
103
|
+
export interface UsageLogPort {
|
|
104
|
+
logUsage(row: UsageLogRow): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
export type TurnUsageLogDecision = {
|
|
107
|
+
skipped: true;
|
|
108
|
+
source: 'proxy_owned_provider_usage';
|
|
109
|
+
reason: 'proxy_persists_usage_immediately';
|
|
110
|
+
} | {
|
|
111
|
+
skipped: true;
|
|
112
|
+
reason: 'no_turn_usage';
|
|
113
|
+
} | {
|
|
114
|
+
skipped: false;
|
|
115
|
+
source: 'local_turn_exact';
|
|
116
|
+
count: 1;
|
|
117
|
+
log: UsageLogPayload;
|
|
118
|
+
};
|
|
119
|
+
export interface DecideTurnUsageLogArgs {
|
|
120
|
+
contract: UsageContract;
|
|
121
|
+
messageId?: string | undefined;
|
|
122
|
+
usage?: NormalizedUsage | null | undefined;
|
|
123
|
+
usageRecords?: readonly TurnUsageRecord[] | null | undefined;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* The pure decision of engine `logTurnUsage()` (:325-351):
|
|
127
|
+
*
|
|
128
|
+
* - amalgm auth → skip `{ skipped: true, source:
|
|
129
|
+
* 'proxy_owned_provider_usage', reason: 'proxy_persists_usage_immediately' }`
|
|
130
|
+
* (the proxy already persisted usage; logging here would double-bill).
|
|
131
|
+
* - no finalizable turn usage → skip `{ skipped: true, reason:
|
|
132
|
+
* 'no_turn_usage' }`.
|
|
133
|
+
* - otherwise → `{ skipped: false, source: 'local_turn_exact', count: 1 }`
|
|
134
|
+
* with the exact `db.logUsage` payload; `marketCostUsd` falls back to the
|
|
135
|
+
* local catalog (`calculateCost`) when the finalized usage carries none.
|
|
136
|
+
*
|
|
137
|
+
* The host performs the write:
|
|
138
|
+
* `usageLogPort.logUsage(buildUsageLogRow(decision.log, { containerId }))`.
|
|
139
|
+
*/
|
|
140
|
+
export declare function decideTurnUsageLog({ contract, messageId, usage, usageRecords }: DecideTurnUsageLogArgs): TurnUsageLogDecision;
|
|
141
|
+
//# sourceMappingURL=ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../src/usage/ports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage host boundary — the ownership DECISIONS from engine
|
|
3
|
+
* `chat-core/usage.js` (`beginProxyTurn` :145-148,:159-173 and
|
|
4
|
+
* `logTurnUsage` :325-351) ported as pure functions, plus the port
|
|
5
|
+
* interfaces the host implements for the side effects (the proxy
|
|
6
|
+
* turn-start POST and the usage_logs db write) and the exact
|
|
7
|
+
* `usage_logs` row shape from engine `chat-server/db.js
|
|
8
|
+
* buildUsageLogRow` (:507).
|
|
9
|
+
*
|
|
10
|
+
* Ownership law: when `authMethod === 'amalgm'` the platform proxy meters
|
|
11
|
+
* and persists usage itself — the runtime must NOT double-log. Every other
|
|
12
|
+
* auth method is user-owned: the runtime finalizes the turn locally and
|
|
13
|
+
* writes one usage row, pricing it from the local catalog when the
|
|
14
|
+
* provider gave no cost.
|
|
15
|
+
*/
|
|
16
|
+
import { finalizeTurnUsage } from './finalize.js';
|
|
17
|
+
import { calculateCost } from './model-catalog.js';
|
|
18
|
+
/**
|
|
19
|
+
* The pure fork of engine `beginProxyTurn()`: non-amalgm auth skips the
|
|
20
|
+
* proxy entirely (`'not_amalgm'`); amalgm auth yields the exact
|
|
21
|
+
* turn-start body for the host's `BillingPort.beginTurn`. Token refresh,
|
|
22
|
+
* the POST, the 401 retry, and error mapping are host concerns.
|
|
23
|
+
*/
|
|
24
|
+
export function decideBeginProxyTurn(contract) {
|
|
25
|
+
if (contract.authMethod !== 'amalgm') {
|
|
26
|
+
return { skipped: true, reason: 'not_amalgm' };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
skipped: false,
|
|
30
|
+
body: {
|
|
31
|
+
sessionId: contract.sessionId,
|
|
32
|
+
messageId: contract.assistantMessageId,
|
|
33
|
+
agentId: contract.agentId,
|
|
34
|
+
harnessId: contract.harness,
|
|
35
|
+
authMethod: contract.authMethod,
|
|
36
|
+
usageOwner: contract.usageOwner || 'platform_proxy',
|
|
37
|
+
modelId: contract.usageModelId || contract.modelId,
|
|
38
|
+
internalModelId: contract.modelId,
|
|
39
|
+
cliModel: contract.cliModel,
|
|
40
|
+
contextWindow: contract.contextWindow ?? null,
|
|
41
|
+
maxTokens: contract.maxTokens ?? null,
|
|
42
|
+
cwd: contract.cwd,
|
|
43
|
+
mcpServers: contract.mcpServers,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Engine `buildUsageLogRow()`, byte-exact — except the engine's
|
|
49
|
+
* `process.env.AMALGM_CONTAINER_ID` read becomes the documented
|
|
50
|
+
* `options.containerId` parameter (falsy → null, matching the engine's
|
|
51
|
+
* `|| null`).
|
|
52
|
+
*/
|
|
53
|
+
export function buildUsageLogRow({ userId, sessionId, messageId, agentId, harnessId, authMethod, modelId, usage, marketCostUsd }, options = {}) {
|
|
54
|
+
const provider = modelId?.split('/')[0] || null;
|
|
55
|
+
return {
|
|
56
|
+
user_id: userId,
|
|
57
|
+
session_id: sessionId || null,
|
|
58
|
+
message_id: messageId || null,
|
|
59
|
+
agent_id: agentId || null,
|
|
60
|
+
harness_id: harnessId || null,
|
|
61
|
+
auth_method: authMethod || null,
|
|
62
|
+
provider,
|
|
63
|
+
model: modelId || null,
|
|
64
|
+
input_tokens: usage.inputTokens || 0,
|
|
65
|
+
output_tokens: usage.outputTokens || 0,
|
|
66
|
+
cache_read_tokens: usage.cacheReadTokens || 0,
|
|
67
|
+
cache_write_tokens: usage.cacheWriteTokens || 0,
|
|
68
|
+
cost_usd: usage.costUsd ?? 0,
|
|
69
|
+
market_cost_usd: marketCostUsd ?? usage.costUsd ?? 0,
|
|
70
|
+
container_id: options.containerId || null,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The pure decision of engine `logTurnUsage()` (:325-351):
|
|
75
|
+
*
|
|
76
|
+
* - amalgm auth → skip `{ skipped: true, source:
|
|
77
|
+
* 'proxy_owned_provider_usage', reason: 'proxy_persists_usage_immediately' }`
|
|
78
|
+
* (the proxy already persisted usage; logging here would double-bill).
|
|
79
|
+
* - no finalizable turn usage → skip `{ skipped: true, reason:
|
|
80
|
+
* 'no_turn_usage' }`.
|
|
81
|
+
* - otherwise → `{ skipped: false, source: 'local_turn_exact', count: 1 }`
|
|
82
|
+
* with the exact `db.logUsage` payload; `marketCostUsd` falls back to the
|
|
83
|
+
* local catalog (`calculateCost`) when the finalized usage carries none.
|
|
84
|
+
*
|
|
85
|
+
* The host performs the write:
|
|
86
|
+
* `usageLogPort.logUsage(buildUsageLogRow(decision.log, { containerId }))`.
|
|
87
|
+
*/
|
|
88
|
+
export function decideTurnUsageLog({ contract, messageId, usage, usageRecords }) {
|
|
89
|
+
if (contract.authMethod === 'amalgm') {
|
|
90
|
+
return { skipped: true, source: 'proxy_owned_provider_usage', reason: 'proxy_persists_usage_immediately' };
|
|
91
|
+
}
|
|
92
|
+
const turnUsage = finalizeTurnUsage({ contract, usage, usageRecords });
|
|
93
|
+
if (!turnUsage)
|
|
94
|
+
return { skipped: true, reason: 'no_turn_usage' };
|
|
95
|
+
const marketCostUsd = turnUsage.marketCostUsd ?? calculateCost(contract.usageModelId || contract.modelId, turnUsage.inputTokens, turnUsage.outputTokens, turnUsage.cacheReadTokens, turnUsage.cacheWriteTokens);
|
|
96
|
+
return {
|
|
97
|
+
skipped: false,
|
|
98
|
+
source: 'local_turn_exact',
|
|
99
|
+
count: 1,
|
|
100
|
+
log: {
|
|
101
|
+
userId: contract.userId,
|
|
102
|
+
sessionId: contract.sessionId,
|
|
103
|
+
messageId,
|
|
104
|
+
agentId: contract.agentId,
|
|
105
|
+
harnessId: contract.harness,
|
|
106
|
+
authMethod: contract.authMethod,
|
|
107
|
+
modelId: contract.usageModelId || contract.modelId,
|
|
108
|
+
usage: turnUsage,
|
|
109
|
+
marketCostUsd,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=ports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/usage/ports.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token counting primitives — engine `chat-core/usage.js:11-63,118-127`,
|
|
3
|
+
* ported byte-exact. Owned by the usage module; the wire module imports
|
|
4
|
+
* `totalUsageTokens` for PromptUsage projection.
|
|
5
|
+
*/
|
|
6
|
+
/** Engine `n()`: coerce to a finite number, else 0. */
|
|
7
|
+
export declare function n(value: unknown): number;
|
|
8
|
+
/**
|
|
9
|
+
* Engine `nonNegativeOrNull()`: the numeric value when finite and >= 0,
|
|
10
|
+
* else null. Note `Number(null)` is 0, so null coerces to 0 here — callers
|
|
11
|
+
* relying on "absent" must pass `undefined`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function nonNegativeOrNull(value: unknown): number | null;
|
|
14
|
+
/** The six-bucket token list every normalized usage record carries. */
|
|
15
|
+
export interface TokenList {
|
|
16
|
+
input: number;
|
|
17
|
+
output: number;
|
|
18
|
+
cacheRead: number;
|
|
19
|
+
cacheWrite: number;
|
|
20
|
+
thought: number;
|
|
21
|
+
total: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Loose input accepted by `normalizeTokenList`: either a nested
|
|
25
|
+
* `tokenList` (short or long key names) or flat `*Tokens` fields.
|
|
26
|
+
*/
|
|
27
|
+
export interface TokenListInput {
|
|
28
|
+
tokenList?: unknown;
|
|
29
|
+
inputTokens?: unknown;
|
|
30
|
+
outputTokens?: unknown;
|
|
31
|
+
cacheReadTokens?: unknown;
|
|
32
|
+
cacheWriteTokens?: unknown;
|
|
33
|
+
thoughtTokens?: unknown;
|
|
34
|
+
totalTokens?: unknown;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Engine `normalizeTokenList()`: coerce any of the accepted key spellings
|
|
38
|
+
* into the canonical six-bucket list. `total` prefers an explicit
|
|
39
|
+
* non-negative total (nested or flat), else the computed sum of the four
|
|
40
|
+
* context buckets — `thought` is informational and never counted into the
|
|
41
|
+
* computed total.
|
|
42
|
+
*/
|
|
43
|
+
export declare function normalizeTokenList(input?: TokenListInput): TokenList;
|
|
44
|
+
/**
|
|
45
|
+
* Engine `hasTokenListBuckets()`: true when any bucket coerces to a finite
|
|
46
|
+
* number. Mirrors the engine's `Number()` coercion exactly — `null`
|
|
47
|
+
* coerces to 0 (finite → true), `undefined` to NaN (→ false).
|
|
48
|
+
*/
|
|
49
|
+
export declare function hasTokenListBuckets(tokenList?: Partial<Record<keyof TokenList, unknown>>): boolean;
|
|
50
|
+
/** Engine `zeroTokenList()`: a fresh all-zero list. */
|
|
51
|
+
export declare function zeroTokenList(): TokenList;
|
|
52
|
+
/**
|
|
53
|
+
* Engine `addTokenLists()`: bucket-wise sum, re-normalized (so `total` is
|
|
54
|
+
* recomputed from the four context buckets, never carried over).
|
|
55
|
+
*/
|
|
56
|
+
export declare function addTokenLists(a?: Partial<TokenList>, b?: Partial<TokenList>): TokenList;
|
|
57
|
+
/**
|
|
58
|
+
* Engine `subtractTokenLists()`: bucket-wise `after - before`, clamped at
|
|
59
|
+
* 0 per bucket — a cumulative meter that reset (e.g. across compaction)
|
|
60
|
+
* can never produce a negative delta.
|
|
61
|
+
*/
|
|
62
|
+
export declare function subtractTokenLists(after?: Partial<TokenList>, before?: Partial<TokenList>): TokenList;
|
|
63
|
+
export interface UsageLike {
|
|
64
|
+
tokenList?: {
|
|
65
|
+
total?: unknown;
|
|
66
|
+
} | undefined;
|
|
67
|
+
inputContextSize?: unknown;
|
|
68
|
+
inputTokens?: unknown;
|
|
69
|
+
outputTokens?: unknown;
|
|
70
|
+
cacheReadTokens?: unknown;
|
|
71
|
+
cacheWriteTokens?: unknown;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Total tokens for a usage record: tokenList total, else context size, else
|
|
75
|
+
* the sum of the four buckets. Precedence uses `||`, exactly as the engine —
|
|
76
|
+
* a zero total falls through to the next source.
|
|
77
|
+
*/
|
|
78
|
+
export declare function totalUsageTokens(usage?: UsageLike): number;
|
|
79
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/usage/tokens.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,uDAAuD;AACvD,wBAAgB,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGxC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG/D;AAED,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,GAAE,cAAmB,GAAG,SAAS,CAoBxE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,GAAE,OAAO,CAAC,MAAM,CAAC,MAAM,SAAS,EAAE,OAAO,CAAC,CAAM,GAAG,OAAO,CAGtG;AAED,uDAAuD;AACvD,wBAAgB,aAAa,IAAI,SAAS,CAEzC;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAE,OAAO,CAAC,SAAS,CAAM,EAAE,CAAC,GAAE,OAAO,CAAC,SAAS,CAAM,GAAG,SAAS,CAQ/F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,GAAE,OAAO,CAAC,SAAS,CAAM,EAAE,MAAM,GAAE,OAAO,CAAC,SAAS,CAAM,GAAG,SAAS,CAQ7G;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,SAAS,CAAC;IAC5C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,SAAc,GAAG,MAAM,CAM9D"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token counting primitives — engine `chat-core/usage.js:11-63,118-127`,
|
|
3
|
+
* ported byte-exact. Owned by the usage module; the wire module imports
|
|
4
|
+
* `totalUsageTokens` for PromptUsage projection.
|
|
5
|
+
*/
|
|
6
|
+
/** Engine `n()`: coerce to a finite number, else 0. */
|
|
7
|
+
export function n(value) {
|
|
8
|
+
const parsed = Number(value || 0);
|
|
9
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Engine `nonNegativeOrNull()`: the numeric value when finite and >= 0,
|
|
13
|
+
* else null. Note `Number(null)` is 0, so null coerces to 0 here — callers
|
|
14
|
+
* relying on "absent" must pass `undefined`.
|
|
15
|
+
*/
|
|
16
|
+
export function nonNegativeOrNull(value) {
|
|
17
|
+
const parsed = Number(value);
|
|
18
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Engine `normalizeTokenList()`: coerce any of the accepted key spellings
|
|
22
|
+
* into the canonical six-bucket list. `total` prefers an explicit
|
|
23
|
+
* non-negative total (nested or flat), else the computed sum of the four
|
|
24
|
+
* context buckets — `thought` is informational and never counted into the
|
|
25
|
+
* computed total.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeTokenList(input = {}) {
|
|
28
|
+
const tokenList = (input.tokenList && typeof input.tokenList === 'object'
|
|
29
|
+
? input.tokenList
|
|
30
|
+
: {});
|
|
31
|
+
const inputTokens = n(tokenList['input'] ?? tokenList['inputTokens'] ?? input.inputTokens);
|
|
32
|
+
const outputTokens = n(tokenList['output'] ?? tokenList['outputTokens'] ?? input.outputTokens);
|
|
33
|
+
const cacheReadTokens = n(tokenList['cacheRead'] ?? tokenList['cacheReadTokens'] ?? input.cacheReadTokens);
|
|
34
|
+
const cacheWriteTokens = n(tokenList['cacheWrite'] ?? tokenList['cacheWriteTokens'] ?? input.cacheWriteTokens);
|
|
35
|
+
const thoughtTokens = n(tokenList['thought'] ?? tokenList['thoughtTokens'] ?? input.thoughtTokens);
|
|
36
|
+
const computedTotal = inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens;
|
|
37
|
+
const total = nonNegativeOrNull(tokenList['total'] ?? tokenList['totalTokens'] ?? input.totalTokens)
|
|
38
|
+
?? computedTotal;
|
|
39
|
+
return {
|
|
40
|
+
input: inputTokens,
|
|
41
|
+
output: outputTokens,
|
|
42
|
+
cacheRead: cacheReadTokens,
|
|
43
|
+
cacheWrite: cacheWriteTokens,
|
|
44
|
+
thought: thoughtTokens,
|
|
45
|
+
total,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Engine `hasTokenListBuckets()`: true when any bucket coerces to a finite
|
|
50
|
+
* number. Mirrors the engine's `Number()` coercion exactly — `null`
|
|
51
|
+
* coerces to 0 (finite → true), `undefined` to NaN (→ false).
|
|
52
|
+
*/
|
|
53
|
+
export function hasTokenListBuckets(tokenList = {}) {
|
|
54
|
+
return ['input', 'output', 'cacheRead', 'cacheWrite', 'thought', 'total']
|
|
55
|
+
.some((key) => Number.isFinite(Number(tokenList[key])));
|
|
56
|
+
}
|
|
57
|
+
/** Engine `zeroTokenList()`: a fresh all-zero list. */
|
|
58
|
+
export function zeroTokenList() {
|
|
59
|
+
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, thought: 0, total: 0 };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Engine `addTokenLists()`: bucket-wise sum, re-normalized (so `total` is
|
|
63
|
+
* recomputed from the four context buckets, never carried over).
|
|
64
|
+
*/
|
|
65
|
+
export function addTokenLists(a = {}, b = {}) {
|
|
66
|
+
return normalizeTokenList({
|
|
67
|
+
inputTokens: n(a.input) + n(b.input),
|
|
68
|
+
outputTokens: n(a.output) + n(b.output),
|
|
69
|
+
cacheReadTokens: n(a.cacheRead) + n(b.cacheRead),
|
|
70
|
+
cacheWriteTokens: n(a.cacheWrite) + n(b.cacheWrite),
|
|
71
|
+
thoughtTokens: n(a.thought) + n(b.thought),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Engine `subtractTokenLists()`: bucket-wise `after - before`, clamped at
|
|
76
|
+
* 0 per bucket — a cumulative meter that reset (e.g. across compaction)
|
|
77
|
+
* can never produce a negative delta.
|
|
78
|
+
*/
|
|
79
|
+
export function subtractTokenLists(after = {}, before = {}) {
|
|
80
|
+
return normalizeTokenList({
|
|
81
|
+
inputTokens: Math.max(0, n(after.input) - n(before.input)),
|
|
82
|
+
outputTokens: Math.max(0, n(after.output) - n(before.output)),
|
|
83
|
+
cacheReadTokens: Math.max(0, n(after.cacheRead) - n(before.cacheRead)),
|
|
84
|
+
cacheWriteTokens: Math.max(0, n(after.cacheWrite) - n(before.cacheWrite)),
|
|
85
|
+
thoughtTokens: Math.max(0, n(after.thought) - n(before.thought)),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Total tokens for a usage record: tokenList total, else context size, else
|
|
90
|
+
* the sum of the four buckets. Precedence uses `||`, exactly as the engine —
|
|
91
|
+
* a zero total falls through to the next source.
|
|
92
|
+
*/
|
|
93
|
+
export function totalUsageTokens(usage = {}) {
|
|
94
|
+
return (n(usage.tokenList?.total) ||
|
|
95
|
+
n(usage.inputContextSize) ||
|
|
96
|
+
n(usage.inputTokens) + n(usage.outputTokens) + n(usage.cacheReadTokens) + n(usage.cacheWriteTokens));
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../src/usage/tokens.ts"],"names":[],"mappings":"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"}
|