@molecule/api-resource-ai-models 1.0.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +238 -27
- package/dist/handlers/list.d.ts +12 -4
- package/dist/handlers/list.d.ts.map +1 -1
- package/dist/handlers/list.js +16 -6
- package/dist/lookup.d.ts +101 -16
- package/dist/lookup.d.ts.map +1 -1
- package/dist/lookup.js +172 -25
- package/dist/models.d.ts +30 -8
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +302 -76
- package/dist/types.d.ts +83 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/lookup.js
CHANGED
|
@@ -4,21 +4,57 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { MODELS } from './models.js';
|
|
7
|
+
/**
|
|
8
|
+
* Whether a model may be offered for selection: not `disabled` (retired
|
|
9
|
+
* upstream) and not `supersededBy` a newer generation of its own family. Both
|
|
10
|
+
* kinds stay in the catalog for pricing — this predicate is the single place
|
|
11
|
+
* that decides *exposure*, so every listing/validation surface agrees.
|
|
12
|
+
*
|
|
13
|
+
* @param model - The model definition (or the two flags from one).
|
|
14
|
+
* @returns True when the model may be listed and chosen.
|
|
15
|
+
*/
|
|
16
|
+
export function isSelectableModel(model) {
|
|
17
|
+
return !model.disabled && !model.supersededBy;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a model id FORWARD to the selectable model that replaces it, following
|
|
21
|
+
* the {@link ModelDefinition.supersededBy} chain (a saved `qwen3.7-max` →
|
|
22
|
+
* `qwen3.8-max`). Lets a persisted selection keep the user's intent — the same
|
|
23
|
+
* tier from the same provider — instead of falling back to the platform default
|
|
24
|
+
* once the older generation stops being offered.
|
|
25
|
+
*
|
|
26
|
+
* @param id - The persisted model id.
|
|
27
|
+
* @returns The selectable successor's id, the id itself when it is already
|
|
28
|
+
* selectable, or `undefined` for an unknown or `disabled` model (nothing to
|
|
29
|
+
* forward to).
|
|
30
|
+
*/
|
|
31
|
+
export function resolveSelectableModelId(id) {
|
|
32
|
+
let model = getModel(id);
|
|
33
|
+
// Bounded by the catalog size: a supersession cycle would otherwise spin here,
|
|
34
|
+
// and the invariant that forbids one is a test, not a runtime guarantee.
|
|
35
|
+
for (let hops = 0; model && !isSelectableModel(model) && hops <= MODELS.length; hops++) {
|
|
36
|
+
if (!model.supersededBy)
|
|
37
|
+
return undefined; // disabled — no successor declared
|
|
38
|
+
model = getModel(model.supersededBy);
|
|
39
|
+
}
|
|
40
|
+
return model && isSelectableModel(model) ? model.id : undefined;
|
|
41
|
+
}
|
|
7
42
|
/**
|
|
8
43
|
* Set of *selectable* model IDs for fast validation.
|
|
9
44
|
*
|
|
10
45
|
* Excludes `disabled` models so a retired model (e.g. `grok-code-fast-1`) can
|
|
11
|
-
* never be chosen for a new chat,
|
|
12
|
-
*
|
|
46
|
+
* never be chosen for a new chat, and `supersededBy` models so an older
|
|
47
|
+
* generation of a family (e.g. `qwen3.7-max` next to `qwen3.8-max`) is never
|
|
48
|
+
* offered — while {@link getModel} still resolves both for historical pricing.
|
|
13
49
|
*/
|
|
14
|
-
export const MODEL_IDS = new Set(MODELS.filter(
|
|
50
|
+
export const MODEL_IDS = new Set(MODELS.filter(isSelectableModel).map((m) => m.id));
|
|
15
51
|
/**
|
|
16
52
|
* Look up a model definition by ID.
|
|
17
53
|
*
|
|
18
|
-
* Returns `disabled` models too: a saved selection or a
|
|
19
|
-
* may reference a since-retired model,
|
|
20
|
-
* {@link MODEL_IDS} / {@link getAvailableModels}
|
|
21
|
-
*
|
|
54
|
+
* Returns `disabled` and `supersededBy` models too: a saved selection or a
|
|
55
|
+
* historical usage row may reference a since-retired or since-superseded model,
|
|
56
|
+
* and it must stay priceable. Use {@link MODEL_IDS} / {@link getAvailableModels}
|
|
57
|
+
* (or {@link isSelectableModel}) to decide what is *selectable*.
|
|
22
58
|
*
|
|
23
59
|
* @param id - The API model ID.
|
|
24
60
|
* @returns The model definition, or `undefined` if not found.
|
|
@@ -39,29 +75,137 @@ export function getModelsByProvider(provider) {
|
|
|
39
75
|
* Get models that are currently usable — filtered to only providers that are available.
|
|
40
76
|
*
|
|
41
77
|
* The caller passes in which provider IDs are active (i.e. have a bond wired).
|
|
42
|
-
*
|
|
78
|
+
* Models that are not {@link isSelectableModel} — `disabled` or superseded by a
|
|
79
|
+
* newer generation — are excluded; they are never offered for selection.
|
|
43
80
|
*
|
|
44
81
|
* @param availableProviders - Set or array of provider IDs that have active bonds.
|
|
45
|
-
* @returns
|
|
82
|
+
* @returns Selectable models whose provider is in the available set.
|
|
46
83
|
*/
|
|
47
84
|
export function getAvailableModels(availableProviders) {
|
|
48
85
|
const providerSet = availableProviders instanceof Set ? availableProviders : new Set(availableProviders);
|
|
49
|
-
return MODELS.filter((m) => providerSet.has(m.provider) &&
|
|
86
|
+
return MODELS.filter((m) => providerSet.has(m.provider) && isSelectableModel(m));
|
|
50
87
|
}
|
|
51
88
|
/**
|
|
52
|
-
*
|
|
89
|
+
* Whether a model's staged {@link ModelDefinition.scheduledPricing} change has
|
|
90
|
+
* taken effect at a given instant.
|
|
91
|
+
*
|
|
92
|
+
* @param modelDef - The model definition.
|
|
93
|
+
* @param at - The instant to evaluate.
|
|
94
|
+
* @returns `true` once `at` is at or past the scheduled `effectiveFrom`.
|
|
95
|
+
*/
|
|
96
|
+
function scheduledPricingApplies(modelDef, at) {
|
|
97
|
+
const scheduled = modelDef.scheduledPricing;
|
|
98
|
+
if (!scheduled)
|
|
99
|
+
return false;
|
|
100
|
+
const effectiveFrom = Date.parse(scheduled.effectiveFrom);
|
|
101
|
+
// An unparseable date must never silently reprice a model. Ignoring the
|
|
102
|
+
// staged entry keeps the current, verified rates in force.
|
|
103
|
+
if (Number.isNaN(effectiveFrom))
|
|
104
|
+
return false;
|
|
105
|
+
return at.getTime() >= effectiveFrom;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* A model's BASE token rates in effect at a given instant — the staged
|
|
109
|
+
* {@link ModelDefinition.scheduledPricing} rates once their `effectiveFrom` has
|
|
110
|
+
* passed, else the base fields.
|
|
111
|
+
*
|
|
112
|
+
* These are the native provider's rates. A `regionPricing` override is a
|
|
113
|
+
* different host's rate card and is resolved separately by
|
|
114
|
+
* {@link modelRegionRates}.
|
|
115
|
+
*
|
|
116
|
+
* @param modelDef - The model definition.
|
|
117
|
+
* @param at - The instant to price at (defaults to now).
|
|
118
|
+
* @returns The base rates in effect at that instant.
|
|
119
|
+
*/
|
|
120
|
+
export function effectiveBaseRates(modelDef, at = new Date()) {
|
|
121
|
+
const scheduled = modelDef.scheduledPricing;
|
|
122
|
+
if (scheduled && scheduledPricingApplies(modelDef, at)) {
|
|
123
|
+
return {
|
|
124
|
+
inputPricePerMTok: scheduled.inputPricePerMTok,
|
|
125
|
+
outputPricePerMTok: scheduled.outputPricePerMTok,
|
|
126
|
+
cacheReadPricePerMTok: scheduled.cacheReadPricePerMTok,
|
|
127
|
+
cacheWritePricePerMTok: scheduled.cacheWritePricePerMTok,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
inputPricePerMTok: modelDef.inputPricePerMTok,
|
|
132
|
+
outputPricePerMTok: modelDef.outputPricePerMTok,
|
|
133
|
+
cacheReadPricePerMTok: modelDef.cacheReadPricePerMTok,
|
|
134
|
+
cacheWritePricePerMTok: modelDef.cacheWritePricePerMTok,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* A model's peak-hour pricing in effect at a given instant: the staged
|
|
139
|
+
* {@link ModelDefinition.scheduledPricing} `peakPricing` once its
|
|
140
|
+
* `effectiveFrom` has passed (when that entry declares one — an omitted one
|
|
141
|
+
* leaves the existing windows in force), else the model's own `peakPricing`.
|
|
142
|
+
*
|
|
143
|
+
* @param modelDef - The model definition.
|
|
144
|
+
* @param at - The instant to evaluate (defaults to now).
|
|
145
|
+
* @returns The peak-pricing config in effect, or `undefined` when none is.
|
|
146
|
+
*/
|
|
147
|
+
export function effectivePeakPricing(modelDef, at = new Date()) {
|
|
148
|
+
const scheduled = modelDef.scheduledPricing;
|
|
149
|
+
if (scheduled?.peakPricing && scheduledPricingApplies(modelDef, at)) {
|
|
150
|
+
return scheduled.peakPricing;
|
|
151
|
+
}
|
|
152
|
+
return modelDef.peakPricing;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* A model projected onto the pricing in effect at a given instant: the staged
|
|
156
|
+
* {@link ModelDefinition.scheduledPricing} rates folded into the base fields
|
|
157
|
+
* (and its peak windows into `peakPricing`) once effective, with the staged
|
|
158
|
+
* entry stripped.
|
|
159
|
+
*
|
|
160
|
+
* This is what the `GET /ai/models` handler serves, so a client renders the
|
|
161
|
+
* rates that are actually billing right now without needing to resolve a
|
|
162
|
+
* schedule against its own clock — the server's clock is the only one that
|
|
163
|
+
* decides when a price change lands.
|
|
164
|
+
*
|
|
165
|
+
* @param modelDef - The model definition.
|
|
166
|
+
* @param at - The instant to project at (defaults to now).
|
|
167
|
+
* @returns The model with effective pricing and no `scheduledPricing`.
|
|
168
|
+
*/
|
|
169
|
+
export function withEffectivePricing(modelDef, at = new Date()) {
|
|
170
|
+
if (!modelDef.scheduledPricing)
|
|
171
|
+
return modelDef;
|
|
172
|
+
const { scheduledPricing: _scheduledPricing, ...rest } = modelDef;
|
|
173
|
+
const peakPricing = effectivePeakPricing(modelDef, at);
|
|
174
|
+
return {
|
|
175
|
+
...rest,
|
|
176
|
+
...effectiveBaseRates(modelDef, at),
|
|
177
|
+
...(peakPricing ? { peakPricing } : {}),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The price multiplier in effect for a model at a given instant, in a region.
|
|
53
182
|
*
|
|
54
183
|
* Consults the model's {@link ModelDefinition.peakPricing} windows (UTC,
|
|
55
184
|
* half-open, may wrap midnight). Metering MUST call this with each request's
|
|
56
185
|
* own timestamp so peak-hour usage bills at the provider's real rate — pricing
|
|
57
186
|
* everything at the flat rate silently under-meters peak traffic.
|
|
58
187
|
*
|
|
188
|
+
* Peak windows belong to the NATIVE provider, so they apply only where the base
|
|
189
|
+
* rates do. A region with a {@link ModelDefinition.regionPricing} override is a
|
|
190
|
+
* different host billing its own complete rate card, including whether it has
|
|
191
|
+
* time-of-day pricing at all — and re-hosts generally do not. Applying the
|
|
192
|
+
* native provider's surcharge on top of a re-host's flat rates would over-bill
|
|
193
|
+
* every turn in its windows (DeepSeek's 2× Beijing-hours pricing charged
|
|
194
|
+
* against DeepInfra, which has no peak pricing).
|
|
195
|
+
*
|
|
59
196
|
* @param modelDef - The model definition (or undefined).
|
|
60
197
|
* @param at - The instant the request was made.
|
|
61
|
-
* @
|
|
198
|
+
* @param region - The user's per-model region choice, if any (omitted → the
|
|
199
|
+
* model's default region).
|
|
200
|
+
* @returns The multiplier (`1` outside peak windows, when none are declared, or
|
|
201
|
+
* in a region that prices off its own override).
|
|
62
202
|
*/
|
|
63
|
-
export function priceMultiplierAt(modelDef, at) {
|
|
64
|
-
|
|
203
|
+
export function priceMultiplierAt(modelDef, at, region) {
|
|
204
|
+
if (!modelDef)
|
|
205
|
+
return 1;
|
|
206
|
+
if (modelDef.regionPricing?.[effectiveModelRegion(modelDef, region)])
|
|
207
|
+
return 1;
|
|
208
|
+
const peak = effectivePeakPricing(modelDef, at);
|
|
65
209
|
if (!peak || peak.windows.length === 0)
|
|
66
210
|
return 1;
|
|
67
211
|
const minute = at.getUTCHours() * 60 + at.getUTCMinutes();
|
|
@@ -92,25 +236,28 @@ export function effectiveModelRegion(modelDef, requested) {
|
|
|
92
236
|
/**
|
|
93
237
|
* The token rates for a model in a given processing region: the model's
|
|
94
238
|
* {@link ModelDefinition.regionPricing} override for the region when one
|
|
95
|
-
* exists, else the base rates (the native provider's list
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
239
|
+
* exists, else the base rates in effect at `at` (the native provider's list
|
|
240
|
+
* prices, including any staged {@link ModelDefinition.scheduledPricing} change
|
|
241
|
+
* that has landed). Omitted cache fields in an override fall back to the
|
|
242
|
+
* override's input price (hosts with no cache discount / no write premium). The
|
|
243
|
+
* region is resolved via {@link effectiveModelRegion}, so callers may pass the
|
|
244
|
+
* raw user choice.
|
|
245
|
+
*
|
|
246
|
+
* `at` defaults to NOW rather than being required, so an existing caller cannot
|
|
247
|
+
* keep billing a superseded rate by omitting it — metering should still pass
|
|
248
|
+
* each request's own timestamp, the same way it must for
|
|
249
|
+
* {@link priceMultiplierAt}.
|
|
99
250
|
*
|
|
100
251
|
* @param modelDef - The model definition.
|
|
101
252
|
* @param requested - The user's per-model region choice, if any.
|
|
253
|
+
* @param at - The instant to price at (defaults to now).
|
|
102
254
|
* @returns The region-effective rates.
|
|
103
255
|
*/
|
|
104
|
-
export function modelRegionRates(modelDef, requested) {
|
|
256
|
+
export function modelRegionRates(modelDef, requested, at = new Date()) {
|
|
105
257
|
const region = effectiveModelRegion(modelDef, requested);
|
|
106
258
|
const override = modelDef.regionPricing?.[region];
|
|
107
259
|
if (!override) {
|
|
108
|
-
return
|
|
109
|
-
inputPricePerMTok: modelDef.inputPricePerMTok,
|
|
110
|
-
outputPricePerMTok: modelDef.outputPricePerMTok,
|
|
111
|
-
cacheReadPricePerMTok: modelDef.cacheReadPricePerMTok,
|
|
112
|
-
cacheWritePricePerMTok: modelDef.cacheWritePricePerMTok,
|
|
113
|
-
};
|
|
260
|
+
return effectiveBaseRates(modelDef, at);
|
|
114
261
|
}
|
|
115
262
|
return {
|
|
116
263
|
inputPricePerMTok: override.inputPricePerMTok,
|
package/dist/models.d.ts
CHANGED
|
@@ -28,6 +28,18 @@ import type { ModelDefinition } from './types.js';
|
|
|
28
28
|
* control) carries `thinkingConfigurable: false` and OMITS both fields —
|
|
29
29
|
* there is nothing to tune.
|
|
30
30
|
*
|
|
31
|
+
* ONE GENERATION PER FAMILY. When a provider ships a newer generation of a
|
|
32
|
+
* model line, the older entry gets `supersededBy: '<newer id>'` and stops being
|
|
33
|
+
* offered — the picker never shows both `qwen3.7-max` and `qwen3.8-max`. The
|
|
34
|
+
* entry is NEVER deleted: `getModel()` still resolves it so saved selections and
|
|
35
|
+
* historical usage stay priceable, and a persisted id resolves forward to the
|
|
36
|
+
* successor. Supersede only within the same TIER: a cheaper or specialist model
|
|
37
|
+
* with no newer equivalent (`gemini-3.1-pro-preview`, `qwen3-coder-plus`,
|
|
38
|
+
* `kimi-k2.7-code`, `grok-build-0.1`) keeps at most `deprecatedAt`, so every
|
|
39
|
+
* provider keeps a real choice. `__tests__/lookup.test.ts` fails on any two
|
|
40
|
+
* selectable models of one family at different versions that aren't a
|
|
41
|
+
* documented exception.
|
|
42
|
+
*
|
|
31
43
|
* Sources (verified 2026-07-28; OpenAI re-verified 2026-07-31 after the
|
|
32
44
|
* 2026-07-30 GPT-5.6 repricing — cross-check prices against models.dev with
|
|
33
45
|
* `npm run check:model-freshness` from the workspace root):
|
|
@@ -49,21 +61,31 @@ import type { ModelDefinition } from './types.js';
|
|
|
49
61
|
* 2026-07-21 $1.50/$7.50 supersedes 3.5-flash as the agentic flagship;
|
|
50
62
|
* gemini-3.1-pro-preview still the pro tier — "3.5 Pro" has NOT shipped as
|
|
51
63
|
* of 2026-07-28 despite the coming-soon badge; do not add until it has an id)
|
|
64
|
+
* (re-verified 2026-08-13: gemini-3.7-flash "New Stable" — supersedes
|
|
65
|
+
* 3.6-flash as the flash flagship at the SAME list price ($1.50/$7.50, cache
|
|
66
|
+
* read $0.15), with a launch promo ($0.75/$3.75, cache read $0.075) through
|
|
67
|
+
* 2026-12-31 billed here at list; specs from /docs/models/gemini-3.7-flash:
|
|
68
|
+
* 1M ctx / 65,536 out, thinking low|medium|high (no minimal), vision, tools,
|
|
69
|
+
* caching, search grounding, code execution, url context)
|
|
52
70
|
* - xAI: https://docs.x.ai/developers/models + /developers/grok-4-5
|
|
53
71
|
* (grok-4.5 flagship 2026-07-08: $2/$6, 500K ctx, ≥200K prompts bill 2× —
|
|
54
72
|
* not modeled; reasoning_effort low|medium|high default high, image input;
|
|
55
73
|
* grok-4.3 still served at $1.25/$2.50 with the bigger 1M window;
|
|
56
74
|
* grok-code-fast-1 no longer listed — retires 2026-08-15)
|
|
57
|
-
* - DeepSeek: https://api-docs.deepseek.com/quick_start/pricing (
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
75
|
+
* - DeepSeek: https://api-docs.deepseek.com/quick_start/pricing (verified
|
|
76
|
+
* 2026-08-14; legacy deepseek-chat/-reasoner ids fully retired 2026-07-24 —
|
|
77
|
+
* never in this catalog. V4-Pro GA on 2026-08-13 came with a price RISE
|
|
78
|
+
* effective 2026-08-16T16:00Z plus the long-announced peak-hour 2×: both
|
|
79
|
+
* entries carry it as `scheduledPricing`, so today's rates bill until that
|
|
80
|
+
* instant and the new ones after. Re-verify weekday-vs-daily peak windows and
|
|
81
|
+
* the CN/US region default once it lands — see the entries.)
|
|
82
|
+
* - Moonshot: https://platform.kimi.ai/docs/models + DeepInfra's model API for
|
|
83
|
+
* the US re-host (kimi-k3 flagship 2026-07-16
|
|
62
84
|
* — 2.8T MoE, 1M ctx, $3/$15 — NOT added: thinking is forced-on with
|
|
63
85
|
* reasoning_content that must be replayed through tool loops, the same
|
|
64
|
-
* constraint that
|
|
65
|
-
*
|
|
66
|
-
*
|
|
86
|
+
* constraint that kept kimi-k2.7-code out. BOTH are now in the catalog: the
|
|
87
|
+
* moonshot bond gained preserved thinking (reasoning replayed through tool
|
|
88
|
+
* loops), so kimi-k3 is the Moonshot pick.)
|
|
67
89
|
* - MiniMax: https://platform.minimax.io/docs/guides/pricing-paygo (unchanged;
|
|
68
90
|
* minimax-m3 $0.30/$1.20 is a "permanent 50% off" list rate)
|
|
69
91
|
* - Alibaba: https://www.alibabacloud.com/help/en/model-studio/deep-thinking
|
package/dist/models.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AACH,eAAO,MAAM,MAAM,EAAE,SAAS,eAAe,EA+5CnC,CAAA"}
|