@company-semantics/contracts 94.0.0 → 96.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/package.json +1 -1
- package/src/affordances/README.md +24 -1
- package/src/affordances/__tests__/README.md +6 -0
- package/src/affordances/__tests__/affordances.test.ts +6 -1
- package/src/affordances/__tests__/catalog-wave.test.ts +2 -2
- package/src/affordances/__tests__/workflow-affordances.test.ts +98 -0
- package/src/affordances/affordance.ts +15 -0
- package/src/affordances/args.ts +13 -0
- package/src/affordances/ids.ts +14 -0
- package/src/affordances/index.ts +1 -0
- package/src/api/generated-spec-hash.ts +2 -2
- package/src/api/generated.ts +430 -8
- package/src/billing/README.md +55 -3
- package/src/billing/__tests__/price-list-tiers.test.ts +129 -0
- package/src/billing/__tests__/processing-modes.test.ts +19 -0
- package/src/billing/__tests__/schemas.test.ts +44 -21
- package/src/billing/index.ts +16 -0
- package/src/billing/schemas.ts +145 -24
- package/src/billing/types.ts +23 -1
- package/src/billing/vocabulary.ts +37 -0
- package/src/generated/openapi-routes.ts +3 -0
- package/src/root-exports/delivery.ts +35 -2
- package/src/workflows/README.md +27 -2
- package/src/workflows/__tests__/README.md +18 -2
- package/src/workflows/__tests__/execution-affordances.test.ts +122 -0
- package/src/workflows/__tests__/projection-fields.test.ts +148 -0
- package/src/workflows/__tests__/types.test.ts +19 -1
- package/src/workflows/execution-affordances.ts +107 -0
- package/src/workflows/index.ts +26 -2
- package/src/workflows/projection-fields.ts +65 -0
- package/src/workflows/types.ts +27 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
PriceListModelSchema,
|
|
4
|
+
PriceListSchema,
|
|
5
|
+
PriceBookVersionListSchema,
|
|
6
|
+
} from "../schemas";
|
|
7
|
+
import { PROCESSING_MODES, PROMPT_TIER_MEASURES } from "../vocabulary";
|
|
8
|
+
|
|
9
|
+
const rates = (input: string) => ({
|
|
10
|
+
inputPerMMicros: input,
|
|
11
|
+
outputPerMMicros: "500000",
|
|
12
|
+
cacheReadPerMMicros: "10000",
|
|
13
|
+
cacheWritePerMMicros: "125000",
|
|
14
|
+
cacheWrite1hPerMMicros: "200000",
|
|
15
|
+
reasoningPerMMicros: "500000",
|
|
16
|
+
});
|
|
17
|
+
const haiku55 = {
|
|
18
|
+
provider: "anthropic",
|
|
19
|
+
modelId: "claude-haiku-5-5",
|
|
20
|
+
aliases: [],
|
|
21
|
+
sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing",
|
|
22
|
+
retrievedOn: "2026-10-09",
|
|
23
|
+
promptTierMeasure: "input_incl_cache",
|
|
24
|
+
promptTokensCeiling: null,
|
|
25
|
+
tiers: [
|
|
26
|
+
{
|
|
27
|
+
processingMode: "standard",
|
|
28
|
+
promptTokensAbove: 0,
|
|
29
|
+
reference: rates("100000"),
|
|
30
|
+
hosted: rates("120000"),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
processingMode: "standard",
|
|
34
|
+
promptTokensAbove: 100000,
|
|
35
|
+
reference: rates("500000"),
|
|
36
|
+
hosted: rates("600000"),
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
describe("price list tiers", () => {
|
|
42
|
+
it("accepts a two-tier model that names its measure", () => {
|
|
43
|
+
expect(PriceListModelSchema.safeParse(haiku55).success).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
it("refuses two tiers without a promptTierMeasure", () => {
|
|
46
|
+
expect(
|
|
47
|
+
PriceListModelSchema.safeParse({ ...haiku55, promptTierMeasure: null })
|
|
48
|
+
.success,
|
|
49
|
+
).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it("refuses a model with no standard base tier", () => {
|
|
52
|
+
expect(
|
|
53
|
+
PriceListModelSchema.safeParse({ ...haiku55, tiers: [haiku55.tiers[1]] })
|
|
54
|
+
.success,
|
|
55
|
+
).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
it("accepts a null cache read as not separately priced", () => {
|
|
58
|
+
const t = {
|
|
59
|
+
...haiku55.tiers[0],
|
|
60
|
+
reference: { ...rates("500000"), cacheReadPerMMicros: null },
|
|
61
|
+
};
|
|
62
|
+
expect(
|
|
63
|
+
PriceListModelSchema.safeParse({
|
|
64
|
+
...haiku55,
|
|
65
|
+
promptTierMeasure: null,
|
|
66
|
+
tiers: [t],
|
|
67
|
+
}).success,
|
|
68
|
+
).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
it("accepts a pre-catalog card with no source", () => {
|
|
71
|
+
expect(
|
|
72
|
+
PriceListModelSchema.safeParse({
|
|
73
|
+
...haiku55,
|
|
74
|
+
sourceUrl: null,
|
|
75
|
+
retrievedOn: null,
|
|
76
|
+
}).success,
|
|
77
|
+
).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
it("refuses an alias that is also listed as its own model", () => {
|
|
80
|
+
const dated = { ...haiku55, modelId: "claude-haiku-5-5-20261001" };
|
|
81
|
+
expect(
|
|
82
|
+
PriceListSchema.safeParse({
|
|
83
|
+
seatPriceMicros: null,
|
|
84
|
+
hostedSurchargeBps: 2000,
|
|
85
|
+
byoFeeBps: 500,
|
|
86
|
+
priceBookVersion: "pb-2026-10-v3",
|
|
87
|
+
priceBookEffectiveAt: "2026-10-10T00:00:00Z",
|
|
88
|
+
models: [{ ...haiku55, aliases: ["claude-haiku-5-5-20261001"] }, dated],
|
|
89
|
+
}).success,
|
|
90
|
+
).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
it("names the closed processing modes and tier measures", () => {
|
|
93
|
+
expect(PROCESSING_MODES).toEqual([
|
|
94
|
+
"standard",
|
|
95
|
+
"batch",
|
|
96
|
+
"flex",
|
|
97
|
+
"fast",
|
|
98
|
+
"ultrafast",
|
|
99
|
+
]);
|
|
100
|
+
expect(PROMPT_TIER_MEASURES).toEqual([
|
|
101
|
+
"input_incl_cache",
|
|
102
|
+
"input_excl_cache",
|
|
103
|
+
]);
|
|
104
|
+
});
|
|
105
|
+
it("requires the effective-at on the list and the version history shape", () => {
|
|
106
|
+
expect(
|
|
107
|
+
PriceListSchema.safeParse({
|
|
108
|
+
seatPriceMicros: null,
|
|
109
|
+
hostedSurchargeBps: 2000,
|
|
110
|
+
byoFeeBps: 500,
|
|
111
|
+
priceBookVersion: "pb-2026-10-v3",
|
|
112
|
+
models: [haiku55],
|
|
113
|
+
}).success,
|
|
114
|
+
).toBe(false);
|
|
115
|
+
expect(
|
|
116
|
+
PriceBookVersionListSchema.safeParse({
|
|
117
|
+
versions: [
|
|
118
|
+
{
|
|
119
|
+
versionLabel: "pb-2026-10-v3",
|
|
120
|
+
effectiveAt: "2026-10-10T00:00:00Z",
|
|
121
|
+
source: "catalog",
|
|
122
|
+
notes: null,
|
|
123
|
+
active: true,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
}).success,
|
|
127
|
+
).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
PROCESSING_MODES,
|
|
4
|
+
ProcessingModeSchema,
|
|
5
|
+
PROMPT_TIER_MEASURES,
|
|
6
|
+
PromptTierMeasureSchema,
|
|
7
|
+
} from "../index";
|
|
8
|
+
|
|
9
|
+
describe("processing modes and prompt tier measures", () => {
|
|
10
|
+
it("exports the processing modes through the billing index", () => {
|
|
11
|
+
expect(PROCESSING_MODES).toContain("fast");
|
|
12
|
+
expect(PROMPT_TIER_MEASURES).toContain("input_excl_cache");
|
|
13
|
+
// "priority" was renamed to "fast"; the closed vocabulary refuses it.
|
|
14
|
+
expect(ProcessingModeSchema.safeParse("priority").success).toBe(false);
|
|
15
|
+
expect(PromptTierMeasureSchema.safeParse("input_incl_cache").success).toBe(
|
|
16
|
+
true,
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -179,27 +179,50 @@ describe("the billing response schemas", () => {
|
|
|
179
179
|
});
|
|
180
180
|
|
|
181
181
|
it("rejects a price list whose per-million rate is a number", () => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
182
|
+
const rates = {
|
|
183
|
+
inputPerMMicros: "15000000",
|
|
184
|
+
outputPerMMicros: "75000000",
|
|
185
|
+
cacheReadPerMMicros: "1500000",
|
|
186
|
+
cacheWritePerMMicros: "18750000",
|
|
187
|
+
cacheWrite1hPerMMicros: "30000000",
|
|
188
|
+
reasoningPerMMicros: "75000000",
|
|
189
|
+
};
|
|
190
|
+
const priceList = (inputPerMMicros: string | number) => ({
|
|
191
|
+
seatPriceMicros: null,
|
|
192
|
+
hostedSurchargeBps: 2000,
|
|
193
|
+
byoFeeBps: 500,
|
|
194
|
+
priceBookVersion: "2026-09-01",
|
|
195
|
+
priceBookEffectiveAt: "2026-09-01T00:00:00Z",
|
|
196
|
+
models: [
|
|
197
|
+
{
|
|
198
|
+
provider: "anthropic",
|
|
199
|
+
modelId: "claude-opus-5",
|
|
200
|
+
aliases: [],
|
|
201
|
+
sourceUrl:
|
|
202
|
+
"https://platform.claude.com/docs/en/about-claude/pricing",
|
|
203
|
+
retrievedOn: "2026-09-01",
|
|
204
|
+
promptTierMeasure: null,
|
|
205
|
+
promptTokensCeiling: null,
|
|
206
|
+
tiers: [
|
|
207
|
+
{
|
|
208
|
+
processingMode: "standard",
|
|
209
|
+
promptTokensAbove: 0,
|
|
210
|
+
reference: { ...rates, inputPerMMicros },
|
|
211
|
+
hosted: { ...rates, inputPerMMicros: "18000000" },
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// The same list with the rate as a string parses, so the only defect is
|
|
219
|
+
// the JSON number.
|
|
220
|
+
expect(PriceListSchema.safeParse(priceList("15000000")).success).toBe(
|
|
221
|
+
true,
|
|
222
|
+
);
|
|
223
|
+
expect(PriceListSchema.safeParse(priceList(15000000)).success).toBe(
|
|
224
|
+
false,
|
|
225
|
+
);
|
|
203
226
|
});
|
|
204
227
|
});
|
|
205
228
|
|
package/src/billing/index.ts
CHANGED
|
@@ -30,6 +30,8 @@ export type {
|
|
|
30
30
|
TokenDimension,
|
|
31
31
|
PrincipalKind,
|
|
32
32
|
BillingProvider,
|
|
33
|
+
ProcessingMode,
|
|
34
|
+
PromptTierMeasure,
|
|
33
35
|
SeatBillability,
|
|
34
36
|
} from "./vocabulary";
|
|
35
37
|
|
|
@@ -60,6 +62,10 @@ export {
|
|
|
60
62
|
PrincipalKindSchema,
|
|
61
63
|
BILLING_PROVIDERS,
|
|
62
64
|
BillingProviderSchema,
|
|
65
|
+
PROCESSING_MODES,
|
|
66
|
+
ProcessingModeSchema,
|
|
67
|
+
PROMPT_TIER_MEASURES,
|
|
68
|
+
PromptTierMeasureSchema,
|
|
63
69
|
SEAT_BILLABILITY,
|
|
64
70
|
TokenDimensionsSchema,
|
|
65
71
|
} from "./vocabulary";
|
|
@@ -102,8 +108,13 @@ export {
|
|
|
102
108
|
ModelAllowlistSchema,
|
|
103
109
|
OrgBillingOverviewSchema,
|
|
104
110
|
PortalSessionResponseSchema,
|
|
111
|
+
PriceBookVersionListSchema,
|
|
112
|
+
PriceBookVersionSummarySchema,
|
|
105
113
|
PriceListModelSchema,
|
|
114
|
+
PriceListQuerySchema,
|
|
115
|
+
PriceListRatesSchema,
|
|
106
116
|
PriceListSchema,
|
|
117
|
+
PriceListTierSchema,
|
|
107
118
|
ProviderKeyListSchema,
|
|
108
119
|
ProviderKeyStatusSchema,
|
|
109
120
|
SpendingControlsSchema,
|
|
@@ -144,8 +155,13 @@ export type {
|
|
|
144
155
|
ModelAllowlist,
|
|
145
156
|
OrgBillingOverview,
|
|
146
157
|
PortalSessionResponse,
|
|
158
|
+
PriceBookVersionList,
|
|
159
|
+
PriceBookVersionSummary,
|
|
147
160
|
PriceList,
|
|
148
161
|
PriceListModel,
|
|
162
|
+
PriceListQuery,
|
|
163
|
+
PriceListRates,
|
|
164
|
+
PriceListTier,
|
|
149
165
|
ProviderKeyList,
|
|
150
166
|
ProviderKeyStatus,
|
|
151
167
|
SpendingControls,
|
package/src/billing/schemas.ts
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
* - EVERY percentage is {@link BpsSchema} — an integer number of basis points.
|
|
20
20
|
* `effectiveMarkupBps: 20.5` is not a fine-grained markup, it is a bug.
|
|
21
21
|
* - EVERY timestamp is {@link IsoDateTime}, the package's single definition
|
|
22
|
-
* site for a `format: date-time` field (ADR-CONTRACTS-156)
|
|
22
|
+
* site for a `format: date-time` field (ADR-CONTRACTS-156); the price book's
|
|
23
|
+
* effective-at timestamps use its offset-tolerant sibling.
|
|
24
|
+
* - A price-list rate that is `null` means the provider publishes no separate
|
|
25
|
+
* rate for it — never `"0"`. A processing mode is an explicit rate set, never
|
|
26
|
+
* a multiplier (ADR slug price-list-tiers).
|
|
23
27
|
* - EVERY token count is a non-negative integer, reported per dimension via
|
|
24
28
|
* {@link TokenDimensionsSchema} and never collapsed into one total
|
|
25
29
|
* (INV-BILL-RAW-TOKENS-ALWAYS-VISIBLE).
|
|
@@ -35,7 +39,7 @@
|
|
|
35
39
|
*/
|
|
36
40
|
import { z } from "zod";
|
|
37
41
|
|
|
38
|
-
import { IsoDateTime } from "../api/primitives";
|
|
42
|
+
import { IsoDateTime, IsoDateTimeWithOffset } from "../api/primitives";
|
|
39
43
|
import {
|
|
40
44
|
AI_PAUSE_REASONS,
|
|
41
45
|
BILLING_PLATFORM_STATUSES,
|
|
@@ -45,6 +49,9 @@ import {
|
|
|
45
49
|
FUNDING_MODES,
|
|
46
50
|
LEDGER_ENTRY_TYPES,
|
|
47
51
|
MoneyMicrosSchema,
|
|
52
|
+
PROCESSING_MODES,
|
|
53
|
+
ProcessingModeSchema,
|
|
54
|
+
PromptTierMeasureSchema,
|
|
48
55
|
TOP_UP_KINDS,
|
|
49
56
|
TOP_UP_STATUSES,
|
|
50
57
|
TokenDimensionsSchema,
|
|
@@ -412,37 +419,151 @@ export const ProviderKeyListSchema = z.object({
|
|
|
412
419
|
/* -------------------------------------------------------------------------- */
|
|
413
420
|
|
|
414
421
|
/**
|
|
415
|
-
* One
|
|
416
|
-
*
|
|
417
|
-
*
|
|
418
|
-
*
|
|
422
|
+
* One rate set, micro-USD PER MILLION tokens. `null` means the provider publishes
|
|
423
|
+
* no separate rate for that dimension: an input-side dimension (cache read,
|
|
424
|
+
* cache write) is then billed at the tier's input rate, which is what the
|
|
425
|
+
* provider charges; an output-side null on a model is refused at the gate when
|
|
426
|
+
* the call could produce output. `cacheWritePerMMicros` is the provider's default
|
|
427
|
+
* (Anthropic: 5-minute) write; `cacheWrite1hPerMMicros` the Anthropic 1-hour write.
|
|
419
428
|
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
* and this platform charges `byoFeeBps` on it, not a per-token rate.
|
|
429
|
+
* Null is never encoded as `"0"`: zero would be a price (free), not the absence
|
|
430
|
+
* of one.
|
|
423
431
|
*/
|
|
424
|
-
export const
|
|
425
|
-
provider: z.enum(BILLING_PROVIDERS),
|
|
426
|
-
modelId: z.string(),
|
|
432
|
+
export const PriceListRatesSchema = z.object({
|
|
427
433
|
inputPerMMicros: MoneyMicrosSchema,
|
|
428
|
-
outputPerMMicros: MoneyMicrosSchema,
|
|
429
|
-
cacheReadPerMMicros: MoneyMicrosSchema,
|
|
430
|
-
cacheWritePerMMicros: MoneyMicrosSchema,
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
hostedOutputPerMMicros: MoneyMicrosSchema,
|
|
434
|
+
outputPerMMicros: MoneyMicrosSchema.nullable(),
|
|
435
|
+
cacheReadPerMMicros: MoneyMicrosSchema.nullable(),
|
|
436
|
+
cacheWritePerMMicros: MoneyMicrosSchema.nullable(),
|
|
437
|
+
cacheWrite1hPerMMicros: MoneyMicrosSchema.nullable(),
|
|
438
|
+
reasoningPerMMicros: MoneyMicrosSchema.nullable(),
|
|
434
439
|
});
|
|
435
440
|
|
|
441
|
+
/**
|
|
442
|
+
* One tier of one processing mode. `promptTokensAbove` is 0 for the base tier;
|
|
443
|
+
* a request whose prompt (per the model's `promptTierMeasure`) is ABOVE it pays
|
|
444
|
+
* this tier's rates on EVERY dimension. `reference` is the provider's list
|
|
445
|
+
* rate; `hosted` is what a hosted call is charged per million (reference plus
|
|
446
|
+
* the surcharge, same rounding as settlement, null preserved).
|
|
447
|
+
*
|
|
448
|
+
* A processing mode is its own explicit rate set, never a multiplier on the
|
|
449
|
+
* standard one: the providers do not publish clean multipliers.
|
|
450
|
+
*/
|
|
451
|
+
export const PriceListTierSchema = z.object({
|
|
452
|
+
processingMode: ProcessingModeSchema,
|
|
453
|
+
promptTokensAbove: z.number().int().min(0),
|
|
454
|
+
reference: PriceListRatesSchema,
|
|
455
|
+
hosted: PriceListRatesSchema,
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* One priced model's card: its provenance, the ids it serves, and its tiers
|
|
460
|
+
* per processing mode. Every model has a `standard` base tier; a model with
|
|
461
|
+
* more than one tier in any mode names the {@link PromptTierMeasureSchema} that
|
|
462
|
+
* decides which tier a request falls in.
|
|
463
|
+
*
|
|
464
|
+
* There is no BYO rate: a BYO call is billed by the provider directly and this
|
|
465
|
+
* platform charges `byoFeeBps` on it, not a per-token rate.
|
|
466
|
+
*/
|
|
467
|
+
export const PriceListModelSchema = z
|
|
468
|
+
.object({
|
|
469
|
+
provider: z.enum(BILLING_PROVIDERS),
|
|
470
|
+
modelId: z.string().min(1),
|
|
471
|
+
/**
|
|
472
|
+
* Ids this version records as served by the same card; [] when the version
|
|
473
|
+
* records none. An id appears once in a price list, as a modelId or as an
|
|
474
|
+
* alias, never both.
|
|
475
|
+
*/
|
|
476
|
+
aliases: z.array(z.string().min(1)),
|
|
477
|
+
/**
|
|
478
|
+
* The provider's official pricing page the card was authored from. Null for
|
|
479
|
+
* a price-book version authored before the catalog recorded provenance.
|
|
480
|
+
*/
|
|
481
|
+
sourceUrl: z.string().url().nullable(),
|
|
482
|
+
/**
|
|
483
|
+
* ISO date (YYYY-MM-DD) the source was last read. Null for a price-book
|
|
484
|
+
* version authored before the catalog recorded provenance.
|
|
485
|
+
*/
|
|
486
|
+
retrievedOn: z
|
|
487
|
+
.string()
|
|
488
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
489
|
+
.nullable(),
|
|
490
|
+
promptTierMeasure: PromptTierMeasureSchema.nullable(),
|
|
491
|
+
/** Prompts above this many tokens are refused (a tier we have not published). Null = no ceiling. */
|
|
492
|
+
promptTokensCeiling: z.number().int().positive().nullable(),
|
|
493
|
+
tiers: z.array(PriceListTierSchema).min(1),
|
|
494
|
+
})
|
|
495
|
+
.superRefine((m, ctx) => {
|
|
496
|
+
const standardBase = m.tiers.some(
|
|
497
|
+
(t) => t.processingMode === "standard" && t.promptTokensAbove === 0,
|
|
498
|
+
);
|
|
499
|
+
if (!standardBase) {
|
|
500
|
+
ctx.addIssue("every priced model has a standard base tier");
|
|
501
|
+
}
|
|
502
|
+
for (const mode of PROCESSING_MODES) {
|
|
503
|
+
const above = m.tiers
|
|
504
|
+
.filter((t) => t.processingMode === mode)
|
|
505
|
+
.map((t) => t.promptTokensAbove);
|
|
506
|
+
if (above.length > 1 && m.promptTierMeasure === null) {
|
|
507
|
+
ctx.addIssue(
|
|
508
|
+
"a model with more than one tier names its promptTierMeasure",
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
if (new Set(above).size !== above.length) {
|
|
512
|
+
ctx.addIssue(`duplicate tier threshold in mode ${mode}`);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
436
517
|
/**
|
|
437
518
|
* The published price book, versioned. Read by the app to show what a model
|
|
438
519
|
* costs before it is used, and by control to audit what it charged after.
|
|
520
|
+
* A correction is a new version; a published version is never recomputed.
|
|
439
521
|
*/
|
|
440
|
-
export const PriceListSchema = z
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
522
|
+
export const PriceListSchema = z
|
|
523
|
+
.object({
|
|
524
|
+
seatPriceMicros: MoneyMicrosSchema.nullable(),
|
|
525
|
+
hostedSurchargeBps: BpsSchema,
|
|
526
|
+
byoFeeBps: BpsSchema,
|
|
527
|
+
priceBookVersion: z.string(),
|
|
528
|
+
/** When this version became the one calls are priced against (ISO timestamp). */
|
|
529
|
+
priceBookEffectiveAt: IsoDateTimeWithOffset,
|
|
530
|
+
models: z.array(PriceListModelSchema),
|
|
531
|
+
})
|
|
532
|
+
.superRefine((list, ctx) => {
|
|
533
|
+
// An id is listed once: as a card's modelId or as one card's alias, never both.
|
|
534
|
+
const seen = new Set<string>();
|
|
535
|
+
for (const m of list.models) {
|
|
536
|
+
for (const id of [m.modelId, ...m.aliases]) {
|
|
537
|
+
if (seen.has(id)) {
|
|
538
|
+
ctx.addIssue(`model id ${id} is listed more than once`);
|
|
539
|
+
}
|
|
540
|
+
seen.add(id);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* `GET /api/billing/price-list?version=<label>` — omit for the active version.
|
|
547
|
+
* A request shape published here by exception (ADR slug price-list-tiers): the
|
|
548
|
+
* app and the backend must agree on how a past version is addressed.
|
|
549
|
+
*/
|
|
550
|
+
export const PriceListQuerySchema = z.object({
|
|
551
|
+
version: z.string().min(1).optional(),
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
/** One published price-book version, newest first in the list. */
|
|
555
|
+
export const PriceBookVersionSummarySchema = z.object({
|
|
556
|
+
versionLabel: z.string(),
|
|
557
|
+
effectiveAt: IsoDateTimeWithOffset,
|
|
558
|
+
source: z.string(),
|
|
559
|
+
notes: z.string().nullable(),
|
|
560
|
+
/** True for exactly the version calls are priced against now. */
|
|
561
|
+
active: z.boolean(),
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
/** The price book's version history, newest first. */
|
|
565
|
+
export const PriceBookVersionListSchema = z.object({
|
|
566
|
+
versions: z.array(PriceBookVersionSummarySchema),
|
|
446
567
|
});
|
|
447
568
|
|
|
448
569
|
/**
|
package/src/billing/types.ts
CHANGED
|
@@ -37,8 +37,13 @@ import type {
|
|
|
37
37
|
ModelAllowlistSchema,
|
|
38
38
|
OrgBillingOverviewSchema,
|
|
39
39
|
PortalSessionResponseSchema,
|
|
40
|
+
PriceBookVersionListSchema,
|
|
41
|
+
PriceBookVersionSummarySchema,
|
|
40
42
|
PriceListModelSchema,
|
|
43
|
+
PriceListQuerySchema,
|
|
44
|
+
PriceListRatesSchema,
|
|
41
45
|
PriceListSchema,
|
|
46
|
+
PriceListTierSchema,
|
|
42
47
|
ProviderKeyListSchema,
|
|
43
48
|
ProviderKeyStatusSchema,
|
|
44
49
|
SpendingControlsSchema,
|
|
@@ -151,11 +156,28 @@ export type ProviderKeyStatus = z.infer<typeof ProviderKeyStatusSchema>;
|
|
|
151
156
|
/** Every provider the org could configure a key for. */
|
|
152
157
|
export type ProviderKeyList = z.infer<typeof ProviderKeyListSchema>;
|
|
153
158
|
|
|
154
|
-
/** One
|
|
159
|
+
/** One rate set, micro-USD per million tokens; null = no separate rate. */
|
|
160
|
+
export type PriceListRates = z.infer<typeof PriceListRatesSchema>;
|
|
161
|
+
|
|
162
|
+
/** One tier of one processing mode: reference and hosted rates. */
|
|
163
|
+
export type PriceListTier = z.infer<typeof PriceListTierSchema>;
|
|
164
|
+
|
|
165
|
+
/** One priced model's card: provenance, aliases and tiers per mode. */
|
|
155
166
|
export type PriceListModel = z.infer<typeof PriceListModelSchema>;
|
|
156
167
|
|
|
157
168
|
/** The published price book, versioned. */
|
|
158
169
|
export type PriceList = z.infer<typeof PriceListSchema>;
|
|
159
170
|
|
|
171
|
+
/** The price-list query: an optional version label, else the active one. */
|
|
172
|
+
export type PriceListQuery = z.infer<typeof PriceListQuerySchema>;
|
|
173
|
+
|
|
174
|
+
/** One published price-book version. */
|
|
175
|
+
export type PriceBookVersionSummary = z.infer<
|
|
176
|
+
typeof PriceBookVersionSummarySchema
|
|
177
|
+
>;
|
|
178
|
+
|
|
179
|
+
/** The price book's version history, newest first. */
|
|
180
|
+
export type PriceBookVersionList = z.infer<typeof PriceBookVersionListSchema>;
|
|
181
|
+
|
|
160
182
|
/** A house-funded org's grant. */
|
|
161
183
|
export type HouseFunding = z.infer<typeof HouseFundingSchema>;
|
|
@@ -332,6 +332,43 @@ export const BILLING_PROVIDERS = [
|
|
|
332
332
|
export type BillingProvider = (typeof BILLING_PROVIDERS)[number];
|
|
333
333
|
export const BillingProviderSchema = z.enum(BILLING_PROVIDERS);
|
|
334
334
|
|
|
335
|
+
/**
|
|
336
|
+
* How a provider processes a request, which selects the rate set it is billed
|
|
337
|
+
* at. Each mode is an EXPLICIT rate set published by the provider, never a
|
|
338
|
+
* multiplier we derive: OpenAI's gpt-4o "Fast" is 1.7x Standard, gpt-6-luna's is
|
|
339
|
+
* 2x, and Anthropic's Batch is 0.5x. "fast" is what OpenAI called "priority"
|
|
340
|
+
* until 2026-07-30 and what Anthropic calls fast mode.
|
|
341
|
+
*/
|
|
342
|
+
export const PROCESSING_MODES = [
|
|
343
|
+
/** The provider's default processing. Every priced model has it. */
|
|
344
|
+
"standard",
|
|
345
|
+
/** Asynchronous batch processing. */
|
|
346
|
+
"batch",
|
|
347
|
+
/** OpenAI Flex: lower price, slower and best-effort capacity. */
|
|
348
|
+
"flex",
|
|
349
|
+
/** Premium latency (OpenAI Fast, formerly Priority; Anthropic fast mode). */
|
|
350
|
+
"fast",
|
|
351
|
+
/** OpenAI Ultrafast. */
|
|
352
|
+
"ultrafast",
|
|
353
|
+
] as const;
|
|
354
|
+
export type ProcessingMode = (typeof PROCESSING_MODES)[number];
|
|
355
|
+
export const ProcessingModeSchema = z.enum(PROCESSING_MODES);
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* What a provider counts to decide which prompt-length tier a request pays.
|
|
359
|
+
* Anthropic (Claude Haiku 5.5, >100K): every input token, INCLUDING cache reads
|
|
360
|
+
* and cache writes. A model with more than one tier must name its measure; an
|
|
361
|
+
* ambiguous provider rule is never guessed (the long tier stays unpublished).
|
|
362
|
+
*/
|
|
363
|
+
export const PROMPT_TIER_MEASURES = [
|
|
364
|
+
/** input + cache read + cache write. */
|
|
365
|
+
"input_incl_cache",
|
|
366
|
+
/** Fresh (uncached) input only. */
|
|
367
|
+
"input_excl_cache",
|
|
368
|
+
] as const;
|
|
369
|
+
export type PromptTierMeasure = (typeof PROMPT_TIER_MEASURES)[number];
|
|
370
|
+
export const PromptTierMeasureSchema = z.enum(PROMPT_TIER_MEASURES);
|
|
371
|
+
|
|
335
372
|
/* -------------------------------------------------------------------------- */
|
|
336
373
|
/* Derived tables */
|
|
337
374
|
/* -------------------------------------------------------------------------- */
|
|
@@ -11,6 +11,9 @@ export const openApiRoutes = {
|
|
|
11
11
|
'/api/account/sessions/{sessionId}': ['DELETE'],
|
|
12
12
|
'/api/affordances/state': ['GET'],
|
|
13
13
|
'/api/analysis-workflows/{id}': ['GET'],
|
|
14
|
+
'/api/analysis-workflows/{id}/continue': ['POST'],
|
|
15
|
+
'/api/analysis-workflows/{id}/retry': ['POST'],
|
|
16
|
+
'/api/analysis-workflows/{id}/run-again': ['POST'],
|
|
14
17
|
'/api/analysis-workflows/{id}/settlement': ['POST'],
|
|
15
18
|
'/api/analysis-workflows/{id}/stop': ['POST'],
|
|
16
19
|
'/api/auth/consent/grant': ['POST'],
|
|
@@ -215,9 +215,41 @@ export type {
|
|
|
215
215
|
// chat part and by the generic projection read. Namespaced `<domain>/<flow>`
|
|
216
216
|
// so domains cannot collide, and a CLOSED const array so the discriminator is
|
|
217
217
|
// validated and handled exhaustively. See src/workflows/README.md.
|
|
218
|
-
export {
|
|
218
|
+
export {
|
|
219
|
+
ANALYSIS_WORKFLOW_STATES,
|
|
220
|
+
ANALYSIS_WORKFLOW_TYPES,
|
|
221
|
+
isWorkflowType,
|
|
222
|
+
} from "../workflows/index";
|
|
223
|
+
|
|
224
|
+
export type { AnalysisWorkflowState, WorkflowType } from "../workflows/index";
|
|
225
|
+
|
|
226
|
+
// Workflow execution actions (ADR-CONTRACTS-227): `executionAffordancesFor` is
|
|
227
|
+
// the one total offer table from a workflow's state, its type's support and
|
|
228
|
+
// its frozen outcome to the workflow.* affordances. See
|
|
229
|
+
// src/workflows/execution-affordances.ts.
|
|
230
|
+
export {
|
|
231
|
+
DOMAIN_RELAUNCH_AFFORDANCE_IDS,
|
|
232
|
+
WORKFLOW_EXECUTION_ACTIONS,
|
|
233
|
+
executionAffordancesFor,
|
|
234
|
+
} from "../workflows/index";
|
|
235
|
+
|
|
236
|
+
export type {
|
|
237
|
+
ExecutionAffordancesInput,
|
|
238
|
+
WorkflowExecutionAction,
|
|
239
|
+
WorkflowExecutionAffordance,
|
|
240
|
+
WorkflowExecutionSupport,
|
|
241
|
+
} from "../workflows/index";
|
|
242
|
+
|
|
243
|
+
// Workflow execution projection fields (ADR-CONTRACTS-227): the offered
|
|
244
|
+
// workflow.* affordances, relaunch lineage and per-epoch timing the backend
|
|
245
|
+
// composes into its (backend-local) workflow projection. See
|
|
246
|
+
// src/workflows/projection-fields.ts.
|
|
247
|
+
export {
|
|
248
|
+
WorkflowExecutionAffordanceSchema,
|
|
249
|
+
WorkflowExecutionProjectionFieldsSchema,
|
|
250
|
+
} from "../workflows/index";
|
|
219
251
|
|
|
220
|
-
export type {
|
|
252
|
+
export type { WorkflowExecutionProjectionFields } from "../workflows/index";
|
|
221
253
|
|
|
222
254
|
// The typed outcome of a workflow's terminal transition (control ADR-CTRL-540):
|
|
223
255
|
// per-type closed reasons, typed facts and an explicit disposition, classified
|
|
@@ -317,6 +349,7 @@ export {
|
|
|
317
349
|
ReviewOpenArgsSchema,
|
|
318
350
|
SourceSyncOpenUpdateArgsSchema,
|
|
319
351
|
TeamOpenArgsSchema,
|
|
352
|
+
WorkflowExecutionArgsSchema,
|
|
320
353
|
sameAffordances,
|
|
321
354
|
} from "../affordances/index";
|
|
322
355
|
|
package/src/workflows/README.md
CHANGED
|
@@ -62,13 +62,37 @@ so every producer, validator and renderer agrees on it.
|
|
|
62
62
|
the backend verifies the covering occurrence at route time), and `silent`
|
|
63
63
|
exactly for `stopped_by_user`. A `produced`, `empty` or `partial` ending is
|
|
64
64
|
never explained.
|
|
65
|
+
- **`ANALYSIS_WORKFLOW_STATES` is the backend's six states, in order**
|
|
66
|
+
(`pending`, `running`, `draining`, `stopped`, `complete`, `failed`). A
|
|
67
|
+
workflow id is the logical run; the backend's execution epoch is the attempt.
|
|
68
|
+
- **Execution actions come from one total table** (ADR-CONTRACTS-227).
|
|
69
|
+
`executionAffordancesFor` maps a workflow's state, its type's
|
|
70
|
+
`WorkflowExecutionSupport` and its frozen outcome offer to the `workflow.*`
|
|
71
|
+
affordances: `pending`/`running` → stop; `draining` → nothing; `stopped` →
|
|
72
|
+
continue, runAgain; `failed` → retry, runAgain; `complete` → runAgain. An
|
|
73
|
+
unsupported action is never offered, and `workflow.runAgain` is dropped
|
|
74
|
+
beside a frozen domain relaunch (`DOMAIN_RELAUNCH_AFFORDANCE_IDS`) — one
|
|
75
|
+
relaunch control, the domain's. It never emits `review.open`.
|
|
76
|
+
- **Projection fields, not the projection.** The workflow projection is
|
|
77
|
+
backend-local; `WorkflowExecutionProjectionFieldsSchema` is the one wire
|
|
78
|
+
shape for the fields it composes (`executionAffordances`, `relaunchedFrom`,
|
|
79
|
+
`epochStartedAt`, `epochCompletedAt`), every key required. Contracts does not
|
|
80
|
+
touch the projection's `capabilities` flags.
|
|
65
81
|
- **Workflows never imports proactive.** The covering kinds are literal
|
|
66
82
|
strings pinned against `PROACTIVE_EVENT_KIND_IDS` by a test; the dependency
|
|
67
83
|
runs proactive → workflows only.
|
|
68
84
|
|
|
69
85
|
## Public API
|
|
70
86
|
|
|
71
|
-
- `ANALYSIS_WORKFLOW_TYPES`, `WorkflowType`, `isWorkflowType
|
|
87
|
+
- `ANALYSIS_WORKFLOW_TYPES`, `WorkflowType`, `isWorkflowType`,
|
|
88
|
+
`ANALYSIS_WORKFLOW_STATES`, `AnalysisWorkflowState` (`types.ts`).
|
|
89
|
+
- `WORKFLOW_EXECUTION_ACTIONS`, `WorkflowExecutionAction`,
|
|
90
|
+
`WorkflowExecutionSupport`, `WorkflowExecutionAffordance`,
|
|
91
|
+
`DOMAIN_RELAUNCH_AFFORDANCE_IDS`, `ExecutionAffordancesInput`,
|
|
92
|
+
`executionAffordancesFor` (`execution-affordances.ts`).
|
|
93
|
+
- `WorkflowExecutionAffordanceSchema`,
|
|
94
|
+
`WorkflowExecutionProjectionFieldsSchema` /
|
|
95
|
+
`WorkflowExecutionProjectionFields` (`projection-fields.ts`).
|
|
72
96
|
- `WorkflowOutcomeSchema` / `WorkflowOutcome`, the per-type member schemas and
|
|
73
97
|
`WorkflowOutcomeDispositionSchema` (`outcome.ts`).
|
|
74
98
|
- `WORKFLOW_OUTCOME_KINDS` and the per-type reason tables and fact schemas
|
|
@@ -88,5 +112,6 @@ so every producer, validator and renderer agrees on it.
|
|
|
88
112
|
|
|
89
113
|
`zod` (outcome schemas), `../input-requests/types` (the review disposition's
|
|
90
114
|
request type) and `../affordances/affordance` (the offered-affordance schema,
|
|
91
|
-
its sanity bound and `sameAffordances`)
|
|
115
|
+
its sanity bound and `sameAffordances`) and `../affordances/args` (the
|
|
116
|
+
`workflow.*` args schema). Otherwise pure vocabulary data, one
|
|
92
117
|
guard and pure helpers.
|