ai-lcr 0.4.0 → 0.5.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 +22 -0
- package/dist/index.cjs +75 -2
- package/dist/index.d.cts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +74 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to `ai-lcr` are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/), and the project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.5.0] — 2026-06-02
|
|
8
|
+
|
|
9
|
+
All additions are optional and backward compatible.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Official-price savings baseline for media.** A media model's savings baseline
|
|
14
|
+
is now the model-maker's first-party list price — what a user pays going
|
|
15
|
+
*direct*, bypassing the cheaper providers we route to — instead of the priciest
|
|
16
|
+
provider we happen to route between. For the common case of a model served by a
|
|
17
|
+
single aggregator (Runware, fal, …), the old baseline equalled the actual cost,
|
|
18
|
+
so savings showed as `$0`; the official price surfaces the real saving.
|
|
19
|
+
- `MediaModelDef.official?: MediaPricing` — an inline first-party price on a
|
|
20
|
+
model def. When set, it wins.
|
|
21
|
+
- `MediaLCRConfig.officialPrices?: Record<string, MediaPricing>` — a modelId →
|
|
22
|
+
price map so a downstream registry gets correct baselines without inlining
|
|
23
|
+
prices. Defaults to the bundled **`OFFICIAL_PRICES`** (now exported), lifted
|
|
24
|
+
from the cross-provider price table by `scripts/gen-media-official.mjs`.
|
|
25
|
+
- When no official price is known (e.g. open-weight models served only by
|
|
26
|
+
aggregators), the baseline falls back to the priciest configured route — or
|
|
27
|
+
none if there's a single route — exactly as before.
|
|
28
|
+
|
|
7
29
|
## [0.4.0] — 2026-06-02
|
|
8
30
|
|
|
9
31
|
All additions are optional and backward compatible.
|
package/dist/index.cjs
CHANGED
|
@@ -22,6 +22,7 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
DEFAULT_REFERENCE: () => DEFAULT_REFERENCE,
|
|
24
24
|
MEDIA_PRICING: () => MEDIA_PRICING,
|
|
25
|
+
OFFICIAL_PRICES: () => OFFICIAL_PRICES,
|
|
25
26
|
cheapestRoute: () => cheapestRoute,
|
|
26
27
|
classifyError: () => classifyError,
|
|
27
28
|
classifyErrorKind: () => classifyErrorKind,
|
|
@@ -569,6 +570,68 @@ function createHttpSink(options) {
|
|
|
569
570
|
};
|
|
570
571
|
}
|
|
571
572
|
|
|
573
|
+
// src/media-official.ts
|
|
574
|
+
var OFFICIAL_PRICES = {
|
|
575
|
+
"alibaba/qwen-image": { unit: "image", cents: 3.5 },
|
|
576
|
+
"alibaba/qwen-image-edit": { unit: "image", cents: 4.5 },
|
|
577
|
+
"alibaba/wan-2-2-i2v": { unit: "call", cents: 50 },
|
|
578
|
+
"alibaba/wan-2-2-t2v": { unit: "call", cents: 50 },
|
|
579
|
+
"alibaba/wan-2-5-i2v": { unit: "call", cents: 75 },
|
|
580
|
+
"alibaba/wan-2-5-t2v": { unit: "call", cents: 75 },
|
|
581
|
+
"alibaba/wan-2-7-image-pro": { unit: "image", cents: 7.5 },
|
|
582
|
+
"alibaba/wan-2-7-t2v": { unit: "call", cents: 75 },
|
|
583
|
+
"alibaba/z-image-turbo": { unit: "image", cents: 1.5 },
|
|
584
|
+
"bfl/flux-1.1-pro": { unit: "image", cents: 4 },
|
|
585
|
+
"bfl/flux-2-flex": { unit: "image", cents: 6 },
|
|
586
|
+
"bfl/flux-2-klein-4b": { unit: "image", cents: 1.4 },
|
|
587
|
+
"bfl/flux-2-klein-9b": { unit: "image", cents: 1.5 },
|
|
588
|
+
"bfl/flux-2-pro": { unit: "image", cents: 3 },
|
|
589
|
+
"bfl/flux-kontext-max": { unit: "image", cents: 8 },
|
|
590
|
+
"bfl/flux-kontext-pro": { unit: "image", cents: 4 },
|
|
591
|
+
"bria/rmbg-2": { unit: "image", cents: 1.8 },
|
|
592
|
+
"bytedance/seedance-2-0": { unit: "call", cents: 187 },
|
|
593
|
+
"bytedance/seedance-2-0-fast": { unit: "call", cents: 60 },
|
|
594
|
+
"bytedance/seedance-2-0-fast-i2v": { unit: "call", cents: 60 },
|
|
595
|
+
"bytedance/seedance-v1-pro": { unit: "call", cents: 61 },
|
|
596
|
+
"bytedance/seedance-v1-pro-i2v": { unit: "call", cents: 61 },
|
|
597
|
+
"bytedance/seedream-4-5": { unit: "image", cents: 3 },
|
|
598
|
+
"bytedance/seedream-5-lite": { unit: "image", cents: 3.5 },
|
|
599
|
+
"google/imagen-4-ultra": { unit: "image", cents: 6 },
|
|
600
|
+
"google/nano-banana": { unit: "image", cents: 3.9 },
|
|
601
|
+
"google/nano-banana-2": { unit: "image", cents: 6.7 },
|
|
602
|
+
"google/nano-banana-pro": { unit: "image", cents: 13.4 },
|
|
603
|
+
"google/veo-3-1": { unit: "call", cents: 200 },
|
|
604
|
+
"google/veo-3-1-lite": { unit: "call", cents: 40 },
|
|
605
|
+
"google/veo-3-quality": { unit: "call", cents: 200 },
|
|
606
|
+
"ideogram/v3-balanced": { unit: "image", cents: 6 },
|
|
607
|
+
"kuaishou/kling-image-3": { unit: "image", cents: 2.8 },
|
|
608
|
+
"kuaishou/kling-image-o3": { unit: "image", cents: 2.8 },
|
|
609
|
+
"kuaishou/kling-motion-control": { unit: "call", cents: 56 },
|
|
610
|
+
"kuaishou/kling-v21-master": { unit: "call", cents: 56 },
|
|
611
|
+
"kuaishou/kling-v21-master-i2v": { unit: "call", cents: 56 },
|
|
612
|
+
"kuaishou/kling-v26-pro-i2v": { unit: "call", cents: 56 },
|
|
613
|
+
"kuaishou/kling-v3-pro": { unit: "call", cents: 56 },
|
|
614
|
+
"kuaishou/kling-v3-pro-i2v": { unit: "call", cents: 56 },
|
|
615
|
+
"lightricks/ltx-2": { unit: "call", cents: 30 },
|
|
616
|
+
"lightricks/ltx-2-i2v": { unit: "call", cents: 30 },
|
|
617
|
+
"minimax/hailuo-02-pro": { unit: "call", cents: 53 },
|
|
618
|
+
"minimax/hailuo-02-standard": { unit: "call", cents: 27 },
|
|
619
|
+
"minimax/hailuo-2-3-pro": { unit: "call", cents: 53 },
|
|
620
|
+
"openai/gpt-image-1-5": { unit: "image", cents: 3.4 },
|
|
621
|
+
"openai/gpt-image-2": { unit: "image", cents: 5.3 },
|
|
622
|
+
"openai/gpt-image-2-high": { unit: "image", cents: 21.1 },
|
|
623
|
+
"openai/sora-2": { unit: "call", cents: 50 },
|
|
624
|
+
"openai/sora-2-i2v": { unit: "call", cents: 50 },
|
|
625
|
+
"pixverse/v5-5-i2v": { unit: "call", cents: 60 },
|
|
626
|
+
"pixverse/v6": { unit: "call", cents: 45 },
|
|
627
|
+
"recraft/v3": { unit: "image", cents: 4 },
|
|
628
|
+
"recraft/v4-1": { unit: "image", cents: 25 },
|
|
629
|
+
"runway/gen-4-image": { unit: "image", cents: 8 },
|
|
630
|
+
"stability/fast-sdxl": { unit: "image", cents: 0.9 },
|
|
631
|
+
"stability/stable-diffusion-3": { unit: "image", cents: 6.5 },
|
|
632
|
+
"xai/grok-image-quality": { unit: "image", cents: 7 }
|
|
633
|
+
};
|
|
634
|
+
|
|
572
635
|
// src/media.ts
|
|
573
636
|
var DEFAULT_REFERENCE = {
|
|
574
637
|
image: { width: 1920, height: 1080 },
|
|
@@ -620,7 +683,15 @@ function newMediaCallId() {
|
|
|
620
683
|
return c?.randomUUID ? c.randomUUID() : `lcr_${Date.now().toString(36)}`;
|
|
621
684
|
}
|
|
622
685
|
function createMediaLCR(config) {
|
|
623
|
-
const {
|
|
686
|
+
const {
|
|
687
|
+
registry,
|
|
688
|
+
adapters,
|
|
689
|
+
reference = DEFAULT_REFERENCE,
|
|
690
|
+
officialPrices = OFFICIAL_PRICES,
|
|
691
|
+
onError,
|
|
692
|
+
onCost,
|
|
693
|
+
onCall
|
|
694
|
+
} = config;
|
|
624
695
|
const safeError = (error, provider) => {
|
|
625
696
|
try {
|
|
626
697
|
onError?.(error, provider);
|
|
@@ -645,7 +716,8 @@ function createMediaLCR(config) {
|
|
|
645
716
|
throw new Error(`ai-lcr: unknown media model "${modelId}" \u2014 add it to the registry`);
|
|
646
717
|
}
|
|
647
718
|
const ranked = rankRoutes(def, reference);
|
|
648
|
-
const
|
|
719
|
+
const official = def.official ?? officialPrices[modelId];
|
|
720
|
+
const baselineUsd = official !== void 0 ? normalizedCents(official, reference) / 100 : ranked.length > 0 ? Math.max(...ranked.map((r) => r.refCents)) / 100 : 0;
|
|
649
721
|
const startedAt = Date.now();
|
|
650
722
|
const attempts = [];
|
|
651
723
|
let lastErr;
|
|
@@ -1130,6 +1202,7 @@ function createLCR(config) {
|
|
|
1130
1202
|
0 && (module.exports = {
|
|
1131
1203
|
DEFAULT_REFERENCE,
|
|
1132
1204
|
MEDIA_PRICING,
|
|
1205
|
+
OFFICIAL_PRICES,
|
|
1133
1206
|
cheapestRoute,
|
|
1134
1207
|
classifyError,
|
|
1135
1208
|
classifyErrorKind,
|
package/dist/index.d.cts
CHANGED
|
@@ -264,6 +264,15 @@ interface MediaModelDef {
|
|
|
264
264
|
modality: MediaModality;
|
|
265
265
|
/** Providers that serve this model. Order is irrelevant — routing sorts by cost. */
|
|
266
266
|
routes: MediaRoute[];
|
|
267
|
+
/**
|
|
268
|
+
* The model-maker's first-party list price — what a user pays going DIRECT,
|
|
269
|
+
* bypassing the cheaper providers we route to. When set, it's the savings
|
|
270
|
+
* baseline (savings = official − actual cost). Omit for open-weight models
|
|
271
|
+
* with no first-party API price; those fall back to the priciest configured
|
|
272
|
+
* route, or no baseline if there's only one. Can also be supplied out-of-band
|
|
273
|
+
* via {@link MediaLCRConfig.officialPrices} so a registry needn't carry it inline.
|
|
274
|
+
*/
|
|
275
|
+
official?: MediaPricing;
|
|
267
276
|
}
|
|
268
277
|
type MediaRegistry = Record<string, MediaModelDef>;
|
|
269
278
|
/**
|
|
@@ -347,6 +356,14 @@ interface MediaLCRConfig {
|
|
|
347
356
|
/** Adapters keyed by provider. A route with no adapter is skipped. */
|
|
348
357
|
adapters: Record<string, MediaAdapter>;
|
|
349
358
|
reference?: ReferenceSpec;
|
|
359
|
+
/**
|
|
360
|
+
* Model-maker first-party list prices keyed by modelId — the savings baseline
|
|
361
|
+
* for a model whose registry def carries no inline `official` price. Lets a
|
|
362
|
+
* downstream registry (e.g. ai-art's) get correct baselines without inlining
|
|
363
|
+
* prices. Defaults to the bundled {@link OFFICIAL_PRICES} (lifted from the
|
|
364
|
+
* cross-provider price table). A def's inline `official` wins over this.
|
|
365
|
+
*/
|
|
366
|
+
officialPrices?: Record<string, MediaPricing>;
|
|
350
367
|
onError?: (error: Error, provider: string) => void;
|
|
351
368
|
onCost?: (event: MediaCostEvent) => void;
|
|
352
369
|
/**
|
|
@@ -387,6 +404,8 @@ declare function createMediaLCR(config: MediaLCRConfig): (modelId: string, input
|
|
|
387
404
|
|
|
388
405
|
declare const MEDIA_PRICING: MediaRegistry;
|
|
389
406
|
|
|
407
|
+
declare const OFFICIAL_PRICES: Record<string, MediaPricing>;
|
|
408
|
+
|
|
390
409
|
/**
|
|
391
410
|
* Kunavo media adapter — image (sync) + video (async poll).
|
|
392
411
|
*
|
|
@@ -545,4 +564,4 @@ type LCRRouter = (modelName: string) => LanguageModelV3;
|
|
|
545
564
|
*/
|
|
546
565
|
declare function createLCR(config: LCRConfig): LCRRouter;
|
|
547
566
|
|
|
548
|
-
export { type CallRecord, type CostEvent, DEFAULT_REFERENCE, type ErrorKind, type FormatOptions, type HttpSinkOptions, type LCRConfig, type LCRRouter, MEDIA_PRICING, type MediaAdapter, type MediaCostEvent, type MediaGenerateRequest, type MediaGenerateResult, type MediaLCRConfig, type MediaModality, type MediaModelDef, type MediaOutput, type MediaPricing, type MediaRegistry, type MediaRoute, type MediaRunResult, type MediaUnit, type PriceComparisonRow, type ProviderCost, type ProviderEntry, type RankedRoute, type ReferenceSpec, type RouteAttempt, cheapestRoute, classifyError, classifyErrorKind, comparePrices, createFalMediaAdapter, createHttpSink, createKunavoMediaAdapter, createLCR, createMediaLCR, createRunwareMediaAdapter, formatCallRecord, normalizedCents, rankRoutes, referenceMegapixels };
|
|
567
|
+
export { type CallRecord, type CostEvent, DEFAULT_REFERENCE, type ErrorKind, type FormatOptions, type HttpSinkOptions, type LCRConfig, type LCRRouter, MEDIA_PRICING, type MediaAdapter, type MediaCostEvent, type MediaGenerateRequest, type MediaGenerateResult, type MediaLCRConfig, type MediaModality, type MediaModelDef, type MediaOutput, type MediaPricing, type MediaRegistry, type MediaRoute, type MediaRunResult, type MediaUnit, OFFICIAL_PRICES, type PriceComparisonRow, type ProviderCost, type ProviderEntry, type RankedRoute, type ReferenceSpec, type RouteAttempt, cheapestRoute, classifyError, classifyErrorKind, comparePrices, createFalMediaAdapter, createHttpSink, createKunavoMediaAdapter, createLCR, createMediaLCR, createRunwareMediaAdapter, formatCallRecord, normalizedCents, rankRoutes, referenceMegapixels };
|
package/dist/index.d.ts
CHANGED
|
@@ -264,6 +264,15 @@ interface MediaModelDef {
|
|
|
264
264
|
modality: MediaModality;
|
|
265
265
|
/** Providers that serve this model. Order is irrelevant — routing sorts by cost. */
|
|
266
266
|
routes: MediaRoute[];
|
|
267
|
+
/**
|
|
268
|
+
* The model-maker's first-party list price — what a user pays going DIRECT,
|
|
269
|
+
* bypassing the cheaper providers we route to. When set, it's the savings
|
|
270
|
+
* baseline (savings = official − actual cost). Omit for open-weight models
|
|
271
|
+
* with no first-party API price; those fall back to the priciest configured
|
|
272
|
+
* route, or no baseline if there's only one. Can also be supplied out-of-band
|
|
273
|
+
* via {@link MediaLCRConfig.officialPrices} so a registry needn't carry it inline.
|
|
274
|
+
*/
|
|
275
|
+
official?: MediaPricing;
|
|
267
276
|
}
|
|
268
277
|
type MediaRegistry = Record<string, MediaModelDef>;
|
|
269
278
|
/**
|
|
@@ -347,6 +356,14 @@ interface MediaLCRConfig {
|
|
|
347
356
|
/** Adapters keyed by provider. A route with no adapter is skipped. */
|
|
348
357
|
adapters: Record<string, MediaAdapter>;
|
|
349
358
|
reference?: ReferenceSpec;
|
|
359
|
+
/**
|
|
360
|
+
* Model-maker first-party list prices keyed by modelId — the savings baseline
|
|
361
|
+
* for a model whose registry def carries no inline `official` price. Lets a
|
|
362
|
+
* downstream registry (e.g. ai-art's) get correct baselines without inlining
|
|
363
|
+
* prices. Defaults to the bundled {@link OFFICIAL_PRICES} (lifted from the
|
|
364
|
+
* cross-provider price table). A def's inline `official` wins over this.
|
|
365
|
+
*/
|
|
366
|
+
officialPrices?: Record<string, MediaPricing>;
|
|
350
367
|
onError?: (error: Error, provider: string) => void;
|
|
351
368
|
onCost?: (event: MediaCostEvent) => void;
|
|
352
369
|
/**
|
|
@@ -387,6 +404,8 @@ declare function createMediaLCR(config: MediaLCRConfig): (modelId: string, input
|
|
|
387
404
|
|
|
388
405
|
declare const MEDIA_PRICING: MediaRegistry;
|
|
389
406
|
|
|
407
|
+
declare const OFFICIAL_PRICES: Record<string, MediaPricing>;
|
|
408
|
+
|
|
390
409
|
/**
|
|
391
410
|
* Kunavo media adapter — image (sync) + video (async poll).
|
|
392
411
|
*
|
|
@@ -545,4 +564,4 @@ type LCRRouter = (modelName: string) => LanguageModelV3;
|
|
|
545
564
|
*/
|
|
546
565
|
declare function createLCR(config: LCRConfig): LCRRouter;
|
|
547
566
|
|
|
548
|
-
export { type CallRecord, type CostEvent, DEFAULT_REFERENCE, type ErrorKind, type FormatOptions, type HttpSinkOptions, type LCRConfig, type LCRRouter, MEDIA_PRICING, type MediaAdapter, type MediaCostEvent, type MediaGenerateRequest, type MediaGenerateResult, type MediaLCRConfig, type MediaModality, type MediaModelDef, type MediaOutput, type MediaPricing, type MediaRegistry, type MediaRoute, type MediaRunResult, type MediaUnit, type PriceComparisonRow, type ProviderCost, type ProviderEntry, type RankedRoute, type ReferenceSpec, type RouteAttempt, cheapestRoute, classifyError, classifyErrorKind, comparePrices, createFalMediaAdapter, createHttpSink, createKunavoMediaAdapter, createLCR, createMediaLCR, createRunwareMediaAdapter, formatCallRecord, normalizedCents, rankRoutes, referenceMegapixels };
|
|
567
|
+
export { type CallRecord, type CostEvent, DEFAULT_REFERENCE, type ErrorKind, type FormatOptions, type HttpSinkOptions, type LCRConfig, type LCRRouter, MEDIA_PRICING, type MediaAdapter, type MediaCostEvent, type MediaGenerateRequest, type MediaGenerateResult, type MediaLCRConfig, type MediaModality, type MediaModelDef, type MediaOutput, type MediaPricing, type MediaRegistry, type MediaRoute, type MediaRunResult, type MediaUnit, OFFICIAL_PRICES, type PriceComparisonRow, type ProviderCost, type ProviderEntry, type RankedRoute, type ReferenceSpec, type RouteAttempt, cheapestRoute, classifyError, classifyErrorKind, comparePrices, createFalMediaAdapter, createHttpSink, createKunavoMediaAdapter, createLCR, createMediaLCR, createRunwareMediaAdapter, formatCallRecord, normalizedCents, rankRoutes, referenceMegapixels };
|
package/dist/index.js
CHANGED
|
@@ -528,6 +528,68 @@ function createHttpSink(options) {
|
|
|
528
528
|
};
|
|
529
529
|
}
|
|
530
530
|
|
|
531
|
+
// src/media-official.ts
|
|
532
|
+
var OFFICIAL_PRICES = {
|
|
533
|
+
"alibaba/qwen-image": { unit: "image", cents: 3.5 },
|
|
534
|
+
"alibaba/qwen-image-edit": { unit: "image", cents: 4.5 },
|
|
535
|
+
"alibaba/wan-2-2-i2v": { unit: "call", cents: 50 },
|
|
536
|
+
"alibaba/wan-2-2-t2v": { unit: "call", cents: 50 },
|
|
537
|
+
"alibaba/wan-2-5-i2v": { unit: "call", cents: 75 },
|
|
538
|
+
"alibaba/wan-2-5-t2v": { unit: "call", cents: 75 },
|
|
539
|
+
"alibaba/wan-2-7-image-pro": { unit: "image", cents: 7.5 },
|
|
540
|
+
"alibaba/wan-2-7-t2v": { unit: "call", cents: 75 },
|
|
541
|
+
"alibaba/z-image-turbo": { unit: "image", cents: 1.5 },
|
|
542
|
+
"bfl/flux-1.1-pro": { unit: "image", cents: 4 },
|
|
543
|
+
"bfl/flux-2-flex": { unit: "image", cents: 6 },
|
|
544
|
+
"bfl/flux-2-klein-4b": { unit: "image", cents: 1.4 },
|
|
545
|
+
"bfl/flux-2-klein-9b": { unit: "image", cents: 1.5 },
|
|
546
|
+
"bfl/flux-2-pro": { unit: "image", cents: 3 },
|
|
547
|
+
"bfl/flux-kontext-max": { unit: "image", cents: 8 },
|
|
548
|
+
"bfl/flux-kontext-pro": { unit: "image", cents: 4 },
|
|
549
|
+
"bria/rmbg-2": { unit: "image", cents: 1.8 },
|
|
550
|
+
"bytedance/seedance-2-0": { unit: "call", cents: 187 },
|
|
551
|
+
"bytedance/seedance-2-0-fast": { unit: "call", cents: 60 },
|
|
552
|
+
"bytedance/seedance-2-0-fast-i2v": { unit: "call", cents: 60 },
|
|
553
|
+
"bytedance/seedance-v1-pro": { unit: "call", cents: 61 },
|
|
554
|
+
"bytedance/seedance-v1-pro-i2v": { unit: "call", cents: 61 },
|
|
555
|
+
"bytedance/seedream-4-5": { unit: "image", cents: 3 },
|
|
556
|
+
"bytedance/seedream-5-lite": { unit: "image", cents: 3.5 },
|
|
557
|
+
"google/imagen-4-ultra": { unit: "image", cents: 6 },
|
|
558
|
+
"google/nano-banana": { unit: "image", cents: 3.9 },
|
|
559
|
+
"google/nano-banana-2": { unit: "image", cents: 6.7 },
|
|
560
|
+
"google/nano-banana-pro": { unit: "image", cents: 13.4 },
|
|
561
|
+
"google/veo-3-1": { unit: "call", cents: 200 },
|
|
562
|
+
"google/veo-3-1-lite": { unit: "call", cents: 40 },
|
|
563
|
+
"google/veo-3-quality": { unit: "call", cents: 200 },
|
|
564
|
+
"ideogram/v3-balanced": { unit: "image", cents: 6 },
|
|
565
|
+
"kuaishou/kling-image-3": { unit: "image", cents: 2.8 },
|
|
566
|
+
"kuaishou/kling-image-o3": { unit: "image", cents: 2.8 },
|
|
567
|
+
"kuaishou/kling-motion-control": { unit: "call", cents: 56 },
|
|
568
|
+
"kuaishou/kling-v21-master": { unit: "call", cents: 56 },
|
|
569
|
+
"kuaishou/kling-v21-master-i2v": { unit: "call", cents: 56 },
|
|
570
|
+
"kuaishou/kling-v26-pro-i2v": { unit: "call", cents: 56 },
|
|
571
|
+
"kuaishou/kling-v3-pro": { unit: "call", cents: 56 },
|
|
572
|
+
"kuaishou/kling-v3-pro-i2v": { unit: "call", cents: 56 },
|
|
573
|
+
"lightricks/ltx-2": { unit: "call", cents: 30 },
|
|
574
|
+
"lightricks/ltx-2-i2v": { unit: "call", cents: 30 },
|
|
575
|
+
"minimax/hailuo-02-pro": { unit: "call", cents: 53 },
|
|
576
|
+
"minimax/hailuo-02-standard": { unit: "call", cents: 27 },
|
|
577
|
+
"minimax/hailuo-2-3-pro": { unit: "call", cents: 53 },
|
|
578
|
+
"openai/gpt-image-1-5": { unit: "image", cents: 3.4 },
|
|
579
|
+
"openai/gpt-image-2": { unit: "image", cents: 5.3 },
|
|
580
|
+
"openai/gpt-image-2-high": { unit: "image", cents: 21.1 },
|
|
581
|
+
"openai/sora-2": { unit: "call", cents: 50 },
|
|
582
|
+
"openai/sora-2-i2v": { unit: "call", cents: 50 },
|
|
583
|
+
"pixverse/v5-5-i2v": { unit: "call", cents: 60 },
|
|
584
|
+
"pixverse/v6": { unit: "call", cents: 45 },
|
|
585
|
+
"recraft/v3": { unit: "image", cents: 4 },
|
|
586
|
+
"recraft/v4-1": { unit: "image", cents: 25 },
|
|
587
|
+
"runway/gen-4-image": { unit: "image", cents: 8 },
|
|
588
|
+
"stability/fast-sdxl": { unit: "image", cents: 0.9 },
|
|
589
|
+
"stability/stable-diffusion-3": { unit: "image", cents: 6.5 },
|
|
590
|
+
"xai/grok-image-quality": { unit: "image", cents: 7 }
|
|
591
|
+
};
|
|
592
|
+
|
|
531
593
|
// src/media.ts
|
|
532
594
|
var DEFAULT_REFERENCE = {
|
|
533
595
|
image: { width: 1920, height: 1080 },
|
|
@@ -579,7 +641,15 @@ function newMediaCallId() {
|
|
|
579
641
|
return c?.randomUUID ? c.randomUUID() : `lcr_${Date.now().toString(36)}`;
|
|
580
642
|
}
|
|
581
643
|
function createMediaLCR(config) {
|
|
582
|
-
const {
|
|
644
|
+
const {
|
|
645
|
+
registry,
|
|
646
|
+
adapters,
|
|
647
|
+
reference = DEFAULT_REFERENCE,
|
|
648
|
+
officialPrices = OFFICIAL_PRICES,
|
|
649
|
+
onError,
|
|
650
|
+
onCost,
|
|
651
|
+
onCall
|
|
652
|
+
} = config;
|
|
583
653
|
const safeError = (error, provider) => {
|
|
584
654
|
try {
|
|
585
655
|
onError?.(error, provider);
|
|
@@ -604,7 +674,8 @@ function createMediaLCR(config) {
|
|
|
604
674
|
throw new Error(`ai-lcr: unknown media model "${modelId}" \u2014 add it to the registry`);
|
|
605
675
|
}
|
|
606
676
|
const ranked = rankRoutes(def, reference);
|
|
607
|
-
const
|
|
677
|
+
const official = def.official ?? officialPrices[modelId];
|
|
678
|
+
const baselineUsd = official !== void 0 ? normalizedCents(official, reference) / 100 : ranked.length > 0 ? Math.max(...ranked.map((r) => r.refCents)) / 100 : 0;
|
|
608
679
|
const startedAt = Date.now();
|
|
609
680
|
const attempts = [];
|
|
610
681
|
let lastErr;
|
|
@@ -1088,6 +1159,7 @@ function createLCR(config) {
|
|
|
1088
1159
|
export {
|
|
1089
1160
|
DEFAULT_REFERENCE,
|
|
1090
1161
|
MEDIA_PRICING,
|
|
1162
|
+
OFFICIAL_PRICES,
|
|
1091
1163
|
cheapestRoute,
|
|
1092
1164
|
classifyError,
|
|
1093
1165
|
classifyErrorKind,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-lcr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Least Cost Routing for LLMs — route every model call to the cheapest available provider, fall back automatically, and track real cost. Built for the Vercel AI SDK.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|