@namzu/sdk 21.1.0 → 22.0.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/CHANGELOG.md +90 -0
- package/dist/advisory/executor.d.ts +9 -4
- package/dist/advisory/executor.d.ts.map +1 -1
- package/dist/advisory/executor.js +16 -9
- package/dist/advisory/executor.js.map +1 -1
- package/dist/constants/limits.d.ts +13 -0
- package/dist/constants/limits.d.ts.map +1 -1
- package/dist/constants/limits.js +14 -2
- package/dist/constants/limits.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts +2 -2
- package/dist/manager/run/persistence.d.ts +37 -2
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +48 -7
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/pricing/catalogue.generated.d.ts +15 -0
- package/dist/pricing/catalogue.generated.d.ts.map +1 -0
- package/dist/pricing/catalogue.generated.js +293 -0
- package/dist/pricing/catalogue.generated.js.map +1 -0
- package/dist/pricing/index.d.ts +46 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +70 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/public-runtime.d.ts +2 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +6 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +3 -1
- package/dist/public-types.d.ts.map +1 -1
- package/dist/run/LimitChecker.d.ts +10 -0
- package/dist/run/LimitChecker.d.ts.map +1 -1
- package/dist/run/LimitChecker.js +14 -2
- package/dist/run/LimitChecker.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts +23 -2
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +24 -3
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/guard.d.ts.map +1 -1
- package/dist/runtime/query/guard.js +1 -0
- package/dist/runtime/query/guard.js.map +1 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +42 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +15 -2
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.js +9 -1
- package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +11 -2
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/store/run/conformance.d.ts.map +1 -1
- package/dist/store/run/conformance.js +8 -2
- package/dist/store/run/conformance.js.map +1 -1
- package/dist/types/common/index.d.ts +38 -2
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/run/stop-reason.d.ts +31 -1
- package/dist/types/run/stop-reason.d.ts.map +1 -1
- package/dist/utils/cost.d.ts +60 -2
- package/dist/utils/cost.d.ts.map +1 -1
- package/dist/utils/cost.js +126 -11
- package/dist/utils/cost.js.map +1 -1
- package/package.json +1 -1
- package/src/advisory/executor.ts +16 -9
- package/src/constants/limits.ts +14 -2
- package/src/manager/agent/lifecycle.ts +2 -2
- package/src/manager/run/persistence.ts +67 -7
- package/src/pricing/catalogue.generated.ts +308 -0
- package/src/pricing/index.ts +78 -0
- package/src/pricing/rates.source.json +206 -0
- package/src/public-runtime.ts +8 -0
- package/src/public-types.ts +3 -1
- package/src/run/LimitChecker.ts +24 -2
- package/src/runtime/query/checkpoint.ts +24 -3
- package/src/runtime/query/guard.ts +1 -0
- package/src/runtime/query/index.ts +42 -0
- package/src/runtime/query/iteration/index.ts +15 -2
- package/src/runtime/query/iteration/phases/advisory.ts +9 -1
- package/src/runtime/query/iteration/phases/compaction.ts +12 -2
- package/src/store/run/conformance.ts +8 -2
- package/src/types/common/index.ts +38 -2
- package/src/types/run/stop-reason.ts +30 -0
- package/src/utils/cost.ts +186 -16
package/dist/utils/cost.d.ts
CHANGED
|
@@ -1,11 +1,69 @@
|
|
|
1
1
|
import { ZERO_COST } from '../constants/limits.js';
|
|
2
2
|
import type { CostInfo, TokenUsage } from '../types/common/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* What a driver's cache tokens cost, and what they mean.
|
|
5
|
+
*
|
|
6
|
+
* `promptIncludesCacheReads` is a property of the DRIVER, not of the model, and
|
|
7
|
+
* it is why this is a nested object rather than two more rate fields. The
|
|
8
|
+
* drivers in this repository disagree about it: two report `promptTokens`
|
|
9
|
+
* excluding cache reads and charge them on top, one reports `promptTokens`
|
|
10
|
+
* already containing them. Applying a read rate without knowing which one you
|
|
11
|
+
* have is wrong by the entire cache volume, in a direction that changes with
|
|
12
|
+
* whoever served the turn — so the fact travels with the rates and is never
|
|
13
|
+
* inferred.
|
|
14
|
+
*/
|
|
15
|
+
export interface CacheRates {
|
|
16
|
+
/**
|
|
17
|
+
* `true`: `usage.cachedTokens` is a SUBSET of `usage.promptTokens`, so the
|
|
18
|
+
* billable input is the difference.
|
|
19
|
+
* `false`: they are additional to it.
|
|
20
|
+
*/
|
|
21
|
+
readonly promptIncludesCacheReads: boolean;
|
|
22
|
+
readonly readCostPer1M: number;
|
|
23
|
+
/**
|
|
24
|
+
* Absent when the driver never reports a cache write. Tokens that arrive
|
|
25
|
+
* anyway are counted as unpriced rather than charged at some neighbouring
|
|
26
|
+
* rate, so a driver that starts reporting them surfaces as a gap instead of
|
|
27
|
+
* a quietly wrong total.
|
|
28
|
+
*/
|
|
29
|
+
readonly writeCostPer1M?: number;
|
|
30
|
+
}
|
|
3
31
|
export interface ModelPricing {
|
|
4
32
|
inputCostPer1M: number;
|
|
5
33
|
outputCostPer1M: number;
|
|
34
|
+
/**
|
|
35
|
+
* Absent means the caller declared a two-rate card and gets a two-rate
|
|
36
|
+
* answer: prompt and completion tokens are priced, and cache tokens are
|
|
37
|
+
* left inside whichever of those the driver already counted them in. That
|
|
38
|
+
* is the host-supplied case and it is a declaration, not a guess on our
|
|
39
|
+
* part. The catalogue always supplies this.
|
|
40
|
+
*/
|
|
41
|
+
cache?: CacheRates;
|
|
6
42
|
}
|
|
7
43
|
export { ZERO_COST };
|
|
8
|
-
export declare function calculateCost(usage: TokenUsage, pricing: ModelPricing
|
|
9
|
-
export declare function accumulateCost(current: CostInfo, additionalUsage: TokenUsage, pricing: ModelPricing
|
|
44
|
+
export declare function calculateCost(usage: TokenUsage, pricing: ModelPricing): CostInfo;
|
|
45
|
+
export declare function accumulateCost(current: CostInfo, additionalUsage: TokenUsage, pricing: ModelPricing): CostInfo;
|
|
46
|
+
/**
|
|
47
|
+
* Record tokens that were consumed at a rate nobody has.
|
|
48
|
+
*
|
|
49
|
+
* The alternative was to add nothing and leave the total alone, which is how
|
|
50
|
+
* every run came to report `$0.00` for work that cost real money. Counting the
|
|
51
|
+
* tokens instead makes the gap a fact the caller can read and the budget guard
|
|
52
|
+
* can refuse on, rather than an absence that looks like an answer.
|
|
53
|
+
*
|
|
54
|
+
* The rate fields go, if they were there: a total that omits part of a run is
|
|
55
|
+
* not described by any single card.
|
|
56
|
+
*/
|
|
57
|
+
export declare function accumulateUnpricedCost(current: CostInfo, additionalUsage: TokenUsage): CostInfo;
|
|
10
58
|
export declare function formatCost(usd: number): string;
|
|
59
|
+
/**
|
|
60
|
+
* How a total should be shown, given what is and is not known about it.
|
|
61
|
+
*
|
|
62
|
+
* Exists so that no surface has to re-derive the free/unknown distinction from
|
|
63
|
+
* two fields and get it subtly wrong. `@namzu/cli` printed
|
|
64
|
+
* `'$0.0000 (this provider reported no price)'` for every run, because every
|
|
65
|
+
* run was unpriced; now the two cases really are different and the string has
|
|
66
|
+
* to follow.
|
|
67
|
+
*/
|
|
68
|
+
export declare function describeCost(cost: CostInfo): string;
|
|
11
69
|
//# sourceMappingURL=cost.d.ts.map
|
package/dist/utils/cost.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../src/utils/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAEpE,MAAM,WAAW,YAAY;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../../src/utils/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAEpE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,wBAAwB,EAAE,OAAO,CAAA;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAChC;AAED,MAAM,WAAW,YAAY;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,UAAU,CAAA;CAClB;AAED,OAAO,EAAE,SAAS,EAAE,CAAA;AAiDpB,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,QAAQ,CAShF;AA0DD,wBAAgB,cAAc,CAC7B,OAAO,EAAE,QAAQ,EACjB,eAAe,EAAE,UAAU,EAC3B,OAAO,EAAE,YAAY,GACnB,QAAQ,CASV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,GAAG,QAAQ,CAM/F;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAMnD"}
|
package/dist/utils/cost.js
CHANGED
|
@@ -1,23 +1,121 @@
|
|
|
1
1
|
import { ZERO_COST } from '../constants/limits.js';
|
|
2
2
|
export { ZERO_COST };
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
3
|
+
function priceUsage(usage, pricing) {
|
|
4
|
+
const perMillion = (tokens, rate) => (tokens / 1_000_000) * rate;
|
|
5
|
+
const cache = pricing.cache;
|
|
6
|
+
if (cache === undefined) {
|
|
7
|
+
return {
|
|
8
|
+
cost: perMillion(usage.promptTokens, pricing.inputCostPer1M) +
|
|
9
|
+
perMillion(usage.completionTokens, pricing.outputCostPer1M),
|
|
10
|
+
cacheDiscount: 0,
|
|
11
|
+
unpriced: 0,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
// Never negative: a driver that reports more cached tokens than prompt
|
|
15
|
+
// tokens is contradicting itself, and clamping keeps that from turning into
|
|
16
|
+
// a credit on the bill.
|
|
17
|
+
const billableInput = cache.promptIncludesCacheReads
|
|
18
|
+
? Math.max(0, usage.promptTokens - usage.cachedTokens)
|
|
19
|
+
: usage.promptTokens;
|
|
20
|
+
const writesRated = cache.writeCostPer1M !== undefined;
|
|
21
|
+
return {
|
|
22
|
+
cost: perMillion(billableInput, pricing.inputCostPer1M) +
|
|
23
|
+
perMillion(usage.completionTokens, pricing.outputCostPer1M) +
|
|
24
|
+
perMillion(usage.cachedTokens, cache.readCostPer1M) +
|
|
25
|
+
(writesRated ? perMillion(usage.cacheWriteTokens, cache.writeCostPer1M) : 0),
|
|
26
|
+
// Reported, not subtracted. The saving is already inside `cost` — the
|
|
27
|
+
// reads were charged at the read rate rather than the input rate — so
|
|
28
|
+
// taking it off again would double-count it. This field was previously
|
|
29
|
+
// declared, defaulted to zero at both call sites, and passed by nobody;
|
|
30
|
+
// it now carries the one quantity it was always named for.
|
|
31
|
+
cacheDiscount: perMillion(usage.cachedTokens, pricing.inputCostPer1M - cache.readCostPer1M),
|
|
32
|
+
unpriced: writesRated ? 0 : usage.cacheWriteTokens,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function calculateCost(usage, pricing) {
|
|
36
|
+
const priced = priceUsage(usage, pricing);
|
|
7
37
|
return {
|
|
8
38
|
inputCostPer1M: pricing.inputCostPer1M,
|
|
9
39
|
outputCostPer1M: pricing.outputCostPer1M,
|
|
10
|
-
totalCost:
|
|
11
|
-
cacheDiscount,
|
|
40
|
+
totalCost: priced.cost,
|
|
41
|
+
cacheDiscount: priced.cacheDiscount,
|
|
42
|
+
unpricedTokens: priced.unpriced,
|
|
12
43
|
};
|
|
13
44
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Whether `current` is a total nothing has been added to yet.
|
|
47
|
+
*
|
|
48
|
+
* All three conditions, not one. A run whose only turn so far was unpriced has
|
|
49
|
+
* a zero total and no rate fields too, and adopting the next turn's rate card
|
|
50
|
+
* as though it described the whole total would be exactly the wrong claim.
|
|
51
|
+
*
|
|
52
|
+
* This predicate is only sound while every writer of a `CostInfo` goes through
|
|
53
|
+
* this module or states the truth in its vocabulary. One did not:
|
|
54
|
+
* `projectEmergencyToCheckpoint` wrote `ZERO_COST` beside a real, non-zero
|
|
55
|
+
* `tokenUsage`, which is byte-identical to a fresh total — so a run resumed
|
|
56
|
+
* from an emergency dump would have adopted its next turn's rate card as
|
|
57
|
+
* covering spend that happened before the crash. That projection now records
|
|
58
|
+
* the pre-crash tokens as unpriced, which is both true and, usefully, not
|
|
59
|
+
* fresh-shaped.
|
|
60
|
+
*/
|
|
61
|
+
function isFresh(current) {
|
|
62
|
+
return (current.totalCost === 0 &&
|
|
63
|
+
current.unpricedTokens === 0 &&
|
|
64
|
+
current.inputCostPer1M === undefined &&
|
|
65
|
+
current.outputCostPer1M === undefined);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The rate fields that honestly describe `current + pricing`.
|
|
69
|
+
*
|
|
70
|
+
* Kept when one card still covers the whole total; dropped when it does not.
|
|
71
|
+
* Dropping rather than overwriting is the change: the previous version wrote
|
|
72
|
+
* the incoming card over whatever was there, so a run that swapped models
|
|
73
|
+
* reported the last card applied as though it had priced every token.
|
|
74
|
+
*
|
|
75
|
+
* Equal-but-distinct cards are treated as one, deliberately. Two models at the
|
|
76
|
+
* same published rate produce a total that a single rate card DOES describe,
|
|
77
|
+
* which is the only claim these two fields make — they name a rate, not a
|
|
78
|
+
* model, and `Run.steps[].servedBy` carries which model served each turn.
|
|
79
|
+
*/
|
|
80
|
+
function ratesFor(current, pricing) {
|
|
81
|
+
const incoming = {
|
|
17
82
|
inputCostPer1M: pricing.inputCostPer1M,
|
|
18
83
|
outputCostPer1M: pricing.outputCostPer1M,
|
|
19
|
-
|
|
20
|
-
|
|
84
|
+
};
|
|
85
|
+
if (isFresh(current))
|
|
86
|
+
return incoming;
|
|
87
|
+
if (current.inputCostPer1M === pricing.inputCostPer1M &&
|
|
88
|
+
current.outputCostPer1M === pricing.outputCostPer1M) {
|
|
89
|
+
return incoming;
|
|
90
|
+
}
|
|
91
|
+
return {};
|
|
92
|
+
}
|
|
93
|
+
export function accumulateCost(current, additionalUsage, pricing) {
|
|
94
|
+
const priced = priceUsage(additionalUsage, pricing);
|
|
95
|
+
const rates = priced.unpriced > 0 ? {} : ratesFor(current, pricing);
|
|
96
|
+
return {
|
|
97
|
+
...rates,
|
|
98
|
+
totalCost: current.totalCost + priced.cost,
|
|
99
|
+
cacheDiscount: current.cacheDiscount + priced.cacheDiscount,
|
|
100
|
+
unpricedTokens: current.unpricedTokens + priced.unpriced,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Record tokens that were consumed at a rate nobody has.
|
|
105
|
+
*
|
|
106
|
+
* The alternative was to add nothing and leave the total alone, which is how
|
|
107
|
+
* every run came to report `$0.00` for work that cost real money. Counting the
|
|
108
|
+
* tokens instead makes the gap a fact the caller can read and the budget guard
|
|
109
|
+
* can refuse on, rather than an absence that looks like an answer.
|
|
110
|
+
*
|
|
111
|
+
* The rate fields go, if they were there: a total that omits part of a run is
|
|
112
|
+
* not described by any single card.
|
|
113
|
+
*/
|
|
114
|
+
export function accumulateUnpricedCost(current, additionalUsage) {
|
|
115
|
+
return {
|
|
116
|
+
totalCost: current.totalCost,
|
|
117
|
+
cacheDiscount: current.cacheDiscount,
|
|
118
|
+
unpricedTokens: current.unpricedTokens + additionalUsage.totalTokens,
|
|
21
119
|
};
|
|
22
120
|
}
|
|
23
121
|
export function formatCost(usd) {
|
|
@@ -27,4 +125,21 @@ export function formatCost(usd) {
|
|
|
27
125
|
return `$${usd.toFixed(4)}`;
|
|
28
126
|
return `$${usd.toFixed(2)}`;
|
|
29
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* How a total should be shown, given what is and is not known about it.
|
|
130
|
+
*
|
|
131
|
+
* Exists so that no surface has to re-derive the free/unknown distinction from
|
|
132
|
+
* two fields and get it subtly wrong. `@namzu/cli` printed
|
|
133
|
+
* `'$0.0000 (this provider reported no price)'` for every run, because every
|
|
134
|
+
* run was unpriced; now the two cases really are different and the string has
|
|
135
|
+
* to follow.
|
|
136
|
+
*/
|
|
137
|
+
export function describeCost(cost) {
|
|
138
|
+
if (cost.unpricedTokens === 0)
|
|
139
|
+
return formatCost(cost.totalCost);
|
|
140
|
+
if (cost.totalCost === 0) {
|
|
141
|
+
return `unknown (${cost.unpricedTokens} tokens at no known rate)`;
|
|
142
|
+
}
|
|
143
|
+
return `at least ${formatCost(cost.totalCost)} (${cost.unpricedTokens} tokens at no known rate)`;
|
|
144
|
+
}
|
|
30
145
|
//# sourceMappingURL=cost.js.map
|
package/dist/utils/cost.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../../src/utils/cost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../../src/utils/cost.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/advisory/executor.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { CHARS_PER_TOKEN } from '../constants/limits.js'
|
|
1
|
+
import { CHARS_PER_TOKEN, ZERO_COST } from '../constants/limits.js'
|
|
2
2
|
import { assembleSystemPrompt } from '../persona/assembler.js'
|
|
3
|
+
import { resolveModelPricing } from '../pricing/index.js'
|
|
3
4
|
import { collect } from '../provider/collect.js'
|
|
4
5
|
|
|
5
6
|
import type { AdvisorDefinition, AdvisoryBudget } from '../types/advisory/config.js'
|
|
@@ -7,7 +8,7 @@ import type { AdvisoryRequest, AdvisoryResult } from '../types/advisory/result.j
|
|
|
7
8
|
import type { CostInfo, TokenUsage } from '../types/common/index.js'
|
|
8
9
|
import { type Message, createSystemMessage, createUserMessage } from '../types/message/index.js'
|
|
9
10
|
import type { LLMToolSchema } from '../types/tool/index.js'
|
|
10
|
-
import { calculateCost } from '../utils/cost.js'
|
|
11
|
+
import { accumulateUnpricedCost, calculateCost } from '../utils/cost.js'
|
|
11
12
|
import { type Logger, getRootLogger } from '../utils/logger.js'
|
|
12
13
|
import { ADVISORY_RESPONSE_CONTRACT, parseAdvisoryResponse } from './parse.js'
|
|
13
14
|
|
|
@@ -221,16 +222,22 @@ export class AdvisoryExecutor {
|
|
|
221
222
|
}
|
|
222
223
|
|
|
223
224
|
/**
|
|
224
|
-
* Cost for one call, from the advisor's own pricing.
|
|
225
|
+
* Cost for one call, from the advisor's own pricing, then the catalogue.
|
|
225
226
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
227
|
+
* When neither has a rate this reports the tokens as UNPRICED rather than
|
|
228
|
+
* as a cost of zero. The previous version returned a zero rate card and a
|
|
229
|
+
* zero total, defended on the grounds that a cost CAP over unpriced
|
|
230
|
+
* advisors is refused at construction so nothing enforces against it — true
|
|
231
|
+
* of the cap, and beside the point for the reader. `AdvisoryResult.cost` is
|
|
232
|
+
* reported to the host, and `$0.00` for a call that cost real money is the
|
|
233
|
+
* exact defect this change exists to remove; it does not become acceptable
|
|
234
|
+
* because the number happens to be unenforced.
|
|
229
235
|
*/
|
|
230
236
|
private computeCost(advisor: AdvisorDefinition, usage: TokenUsage): CostInfo {
|
|
231
|
-
|
|
232
|
-
|
|
237
|
+
const pricing = advisor.pricing ?? resolveModelPricing(advisor.provider.id, advisor.model)
|
|
238
|
+
if (!pricing) {
|
|
239
|
+
return accumulateUnpricedCost(ZERO_COST, usage)
|
|
233
240
|
}
|
|
234
|
-
return calculateCost(usage,
|
|
241
|
+
return calculateCost(usage, pricing)
|
|
235
242
|
}
|
|
236
243
|
}
|
package/src/constants/limits.ts
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
import type { CostInfo, TokenUsage } from '../types/common/index.js'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The starting value for a run: nothing accumulated, nothing unpriced.
|
|
5
|
+
*
|
|
6
|
+
* Carries NO rate fields, and that is load-bearing rather than tidy. It is what
|
|
7
|
+
* lets {@link import('../utils/cost.js').accumulateCost} tell "fresh" from
|
|
8
|
+
* "already spans two rate cards" without a separate marker: a fresh total has
|
|
9
|
+
* no rates AND no unpriced tokens AND a zero total, and no accumulated one can
|
|
10
|
+
* have all three.
|
|
11
|
+
*
|
|
12
|
+
* It also stops reading as a rate card of zero. `inputCostPer1M: 0` on a run
|
|
13
|
+
* nobody has priced says the model is free, which was exactly the confusion
|
|
14
|
+
* `unpricedTokens` was added to end.
|
|
15
|
+
*/
|
|
3
16
|
export const ZERO_COST: CostInfo = {
|
|
4
|
-
inputCostPer1M: 0,
|
|
5
|
-
outputCostPer1M: 0,
|
|
6
17
|
totalCost: 0,
|
|
7
18
|
cacheDiscount: 0,
|
|
19
|
+
unpricedTokens: 0,
|
|
8
20
|
}
|
|
9
21
|
|
|
10
22
|
export const CHARS_PER_TOKEN = 4
|
|
@@ -48,8 +48,8 @@ import type { ThreadManager } from '../thread/lifecycle.js'
|
|
|
48
48
|
*
|
|
49
49
|
* Phase 9 Known Delta #5: fields are now unconditional required. The legacy
|
|
50
50
|
* "run without deps" compat branch was removed; every `AgentManager` consumer
|
|
51
|
-
* (SDK internals
|
|
52
|
-
* full set before instantiating. Convention #0 (no workarounds): the
|
|
51
|
+
* (SDK internals and `@namzu/cli`; the list here once also named two packages
|
|
52
|
+
* that do not exist) MUST wire the full set before instantiating. Convention #0 (no workarounds): the
|
|
53
53
|
* partially-wired mode was a migration-window bridge; 0.2.0 closes it.
|
|
54
54
|
*
|
|
55
55
|
* `workspaceRegistry` is required but may be empty — spawns without a
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AUTO_CONTINUATION_USER_MESSAGE } from '../../constants/continuation.js'
|
|
2
2
|
import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
|
|
3
|
+
import { resolveModelPricing } from '../../pricing/index.js'
|
|
3
4
|
import { DiskCheckpointStore } from '../../store/run/checkpoint-disk.js'
|
|
4
5
|
import { RunDiskStore } from '../../store/run/disk.js'
|
|
5
6
|
import { type CostInfo, type TokenUsage, accumulateTokenUsage } from '../../types/common/index.js'
|
|
@@ -11,10 +12,28 @@ import type { EmergencySaveData } from '../../types/run/emergency.js'
|
|
|
11
12
|
import type { Run, RunPersistenceConfig, StepResult, StopReason } from '../../types/run/index.js'
|
|
12
13
|
import type { RunStore } from '../../types/run/store.js'
|
|
13
14
|
import type { ProjectId, ThreadId } from '../../types/session/ids.js'
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
type ModelPricing,
|
|
17
|
+
ZERO_COST,
|
|
18
|
+
accumulateCost,
|
|
19
|
+
accumulateUnpricedCost,
|
|
20
|
+
} from '../../utils/cost.js'
|
|
15
21
|
import { generateEmergencySaveId } from '../../utils/id.js'
|
|
16
22
|
import type { Logger } from '../../utils/logger.js'
|
|
17
23
|
|
|
24
|
+
/**
|
|
25
|
+
* The pair a rate lookup needs: which driver, running which model.
|
|
26
|
+
*
|
|
27
|
+
* `model` may be absent because a driver can be pointed at whatever the host
|
|
28
|
+
* configured and some report nothing back. Absent resolves to no rate, which
|
|
29
|
+
* is the honest answer, EXCEPT for a driver that bills nothing whatever it
|
|
30
|
+
* runs — see `resolveModelPricing`.
|
|
31
|
+
*/
|
|
32
|
+
export interface PricingSubject {
|
|
33
|
+
readonly providerId: string
|
|
34
|
+
readonly model: string | undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
18
37
|
export class RunPersistence {
|
|
19
38
|
private run: Run
|
|
20
39
|
private runStore: RunStore
|
|
@@ -266,12 +285,38 @@ export class RunPersistence {
|
|
|
266
285
|
this.run.messages.push(message)
|
|
267
286
|
}
|
|
268
287
|
|
|
269
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Who served the tokens being accumulated, so a rate can be found for them.
|
|
290
|
+
*
|
|
291
|
+
* Both fields are required. The previous signature took usage alone and
|
|
292
|
+
* priced it from one table the run was constructed with, which is why the
|
|
293
|
+
* only way to get a non-zero cost was for a host to pass that table — and
|
|
294
|
+
* no shipped surface did. Naming the subject at every call site is what
|
|
295
|
+
* lets the catalogue answer, and it makes a site that cannot say who served
|
|
296
|
+
* a compile error rather than a silent misattribution.
|
|
297
|
+
*/
|
|
298
|
+
private resolvePricing(servedBy: PricingSubject): ModelPricing | undefined {
|
|
299
|
+
// A host-supplied table wins. It is a deliberate declaration about this
|
|
300
|
+
// run, and `query()` already refuses to accept one alongside a provider
|
|
301
|
+
// chain of more than one member, so it cannot be silently spread across
|
|
302
|
+
// two rate cards here.
|
|
303
|
+
if (this.pricing) return this.pricing
|
|
304
|
+
return resolveModelPricing(servedBy.providerId, servedBy.model)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
accumulateUsage(usage: TokenUsage, servedBy: PricingSubject): void {
|
|
270
308
|
this.run.tokenUsage = accumulateTokenUsage(this.run.tokenUsage, usage)
|
|
271
309
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
310
|
+
const pricing = this.resolvePricing(servedBy)
|
|
311
|
+
// No rate is not the same as a rate of zero, and the difference is the
|
|
312
|
+
// whole point of this change. Tokens nobody can price are counted as
|
|
313
|
+
// such, so `costInfo.unpricedTokens` distinguishes "this cost nothing"
|
|
314
|
+
// from "nobody knows what this cost" — and so the budget guard can
|
|
315
|
+
// refuse rather than pass a limit it cannot measure.
|
|
316
|
+
this.run.costInfo =
|
|
317
|
+
pricing === undefined
|
|
318
|
+
? accumulateUnpricedCost(this.run.costInfo, usage)
|
|
319
|
+
: accumulateCost(this.run.costInfo, usage, pricing)
|
|
275
320
|
}
|
|
276
321
|
|
|
277
322
|
/**
|
|
@@ -287,8 +332,8 @@ export class RunPersistence {
|
|
|
287
332
|
* plain {@link accumulateUsage} instead: their prompts are not the
|
|
288
333
|
* run's context, and letting them write here would corrupt the signal.
|
|
289
334
|
*/
|
|
290
|
-
recordTurnUsage(usage: TokenUsage): void {
|
|
291
|
-
this.accumulateUsage(usage)
|
|
335
|
+
recordTurnUsage(usage: TokenUsage, servedBy: PricingSubject): void {
|
|
336
|
+
this.accumulateUsage(usage, servedBy)
|
|
292
337
|
this._lastPromptTokens = usage.promptTokens
|
|
293
338
|
// How much of the history that number covers. The loop pushes the
|
|
294
339
|
// assistant message and its tool results AFTER this call, so without
|
|
@@ -393,6 +438,21 @@ export class RunPersistence {
|
|
|
393
438
|
this.run.metadata.servingProvider = providerId
|
|
394
439
|
}
|
|
395
440
|
|
|
441
|
+
/**
|
|
442
|
+
* Who is serving right now, for a side-channel call that has no provenance
|
|
443
|
+
* of its own.
|
|
444
|
+
*
|
|
445
|
+
* The main loop does not use this — it reads `servedBy` off the turn, which
|
|
446
|
+
* is exact. Compaction and the closing summary go through the same wrapped
|
|
447
|
+
* provider without recording who answered, and this is the best available
|
|
448
|
+
* answer for them: the fallback decorator reports the head member's id
|
|
449
|
+
* whoever is actually serving, so asking the provider would hand back the
|
|
450
|
+
* declaration rather than the fact.
|
|
451
|
+
*/
|
|
452
|
+
get servingProviderId(): string {
|
|
453
|
+
return this.run.metadata.servingProvider ?? this.run.metadata.provider
|
|
454
|
+
}
|
|
455
|
+
|
|
396
456
|
clearLastPromptTokens(): void {
|
|
397
457
|
this._lastPromptTokens = undefined
|
|
398
458
|
this._lastPromptMessageCount = undefined
|