@linxiraos/pi-catalog 1.1.27 → 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/CHANGELOG.md +4 -17
- package/dist/types/compat/auth-ids.d.ts +2 -2
- package/dist/types/compat/judgment-questions.d.ts +7 -0
- package/dist/types/compat/prompt-cache-lookback.d.ts +7 -0
- package/dist/types/compat/provider-ids.d.ts +1 -1
- package/dist/types/compat/reasoning-carry.d.ts +21 -0
- package/dist/types/compat/request-size.d.ts +7 -0
- package/dist/types/compat/taxonomy.d.ts +0 -10
- package/dist/types/compat/types.d.ts +4 -0
- package/dist/types/discovery/antigravity.d.ts +13 -3
- package/dist/types/model-manager.d.ts +16 -0
- package/dist/types/provider-models/account-access.d.ts +11 -0
- package/dist/types/provider-models/google.d.ts +23 -1
- package/dist/types/provider-models/openai-compat.d.ts +19 -1
- package/dist/types/provider-models/special.d.ts +2 -0
- package/dist/types/types.d.ts +106 -20
- package/dist/types/wire/cloudflare-ai-gateway.d.ts +0 -10
- package/dist/types/wire/cloudflare.d.ts +10 -0
- package/dist/types/wire/factory-droid.d.ts +1 -1
- package/package.json +4 -4
- package/src/build.ts +39 -35
- package/src/compat/auth-ids.ts +4 -0
- package/src/compat/axes.ts +37 -3
- package/src/compat/cascade.ts +6 -1
- package/src/compat/judgment-questions.ts +12 -0
- package/src/compat/prompt-cache-lookback.ts +23 -0
- package/src/compat/provider-ids.d.ts +1 -1
- package/src/compat/provider-ids.ts +2 -0
- package/src/compat/reasoning-carry.ts +64 -0
- package/src/compat/request-size.ts +25 -0
- package/src/compat/resolve.ts +16 -2
- package/src/compat/rules/README.md +12 -2
- package/src/compat/rules/auth/_order.kdl +2 -2
- package/src/compat/rules/auth/cloudflare-workers-ai.kdl +9 -0
- package/src/compat/rules/auth/coralbricks.kdl +13 -0
- package/src/compat/rules/auth/minimax-code-cn.kdl +1 -1
- package/src/compat/rules/auth/minimax-code.kdl +1 -1
- package/src/compat/rules/auth/openai-codex.kdl +1 -1
- package/src/compat/rules/auth/stencil.kdl +1 -1
- package/src/compat/rules/classes/anthropic.kdl +87 -1
- package/src/compat/rules/classes/deepseek.kdl +6 -0
- package/src/compat/rules/classes/glm.kdl +16 -7
- package/src/compat/rules/classes/kimi.kdl +8 -0
- package/src/compat/rules/classes/minimax.kdl +31 -1
- package/src/compat/rules/classes/openai.kdl +5 -3
- package/src/compat/rules/classes/qwen.kdl +2 -1
- package/src/compat/rules/providers/abliteration.kdl +7 -0
- package/src/compat/rules/providers/anthropic.kdl +13 -0
- package/src/compat/rules/providers/apple.kdl +1 -0
- package/src/compat/rules/providers/cloudflare-workers-ai.kdl +34 -0
- package/src/compat/rules/providers/coralbricks.kdl +62 -0
- package/src/compat/rules/providers/cursor.kdl +22 -0
- package/src/compat/rules/providers/devin.kdl +2 -2
- package/src/compat/rules/providers/factory-droid.kdl +77 -13
- package/src/compat/rules/providers/github-copilot.kdl +29 -0
- package/src/compat/rules/providers/groq.kdl +21 -0
- package/src/compat/rules/providers/judgment-questions.kdl +53 -0
- package/src/compat/rules/providers/litellm.kdl +4 -1
- package/src/compat/rules/providers/minimax-code-cn.kdl +10 -5
- package/src/compat/rules/providers/minimax-code.kdl +10 -5
- package/src/compat/rules/providers/mistral.kdl +25 -1
- package/src/compat/rules/providers/moonshot.kdl +11 -0
- package/src/compat/rules/providers/openai-codex.kdl +17 -13
- package/src/compat/rules/providers/openai.kdl +22 -0
- package/src/compat/rules/providers/opencode-zen.kdl +20 -0
- package/src/compat/rules/providers/openrouter.kdl +6 -0
- package/src/compat/rules/providers/zhipu-coding-plan.kdl +10 -0
- package/src/compat/rules/runtime/behavior.kdl +7 -4
- package/src/compat/rules/taxonomy/_collapse.kdl +18 -0
- package/src/compat/rules.json +1 -1
- package/src/compat/taxonomy.d.ts +0 -10
- package/src/compat/taxonomy.ts +3 -3
- package/src/compat/types.d.ts +4 -0
- package/src/compat/types.ts +4 -0
- package/src/discovery/antigravity.d.ts +13 -3
- package/src/discovery/antigravity.ts +33 -17
- package/src/discovery/cursor.ts +2 -1
- package/src/discovery/devin.ts +20 -2
- package/src/model-cache.ts +113 -47
- package/src/model-manager.d.ts +16 -0
- package/src/model-manager.ts +107 -41
- package/src/model-tokenizer.ts +3 -0
- package/src/models.json +1 -1
- package/src/provider-models/account-access.d.ts +11 -0
- package/src/provider-models/account-access.ts +30 -0
- package/src/provider-models/cache-provider-id.ts +3 -2
- package/src/provider-models/descriptors.ts +4 -0
- package/src/provider-models/google.d.ts +23 -1
- package/src/provider-models/google.ts +70 -14
- package/src/provider-models/openai-compat.d.ts +19 -1
- package/src/provider-models/openai-compat.ts +215 -31
- package/src/provider-models/special.d.ts +2 -0
- package/src/provider-models/special.ts +15 -18
- package/src/types.d.ts +106 -20
- package/src/types.ts +149 -17
- package/src/wire/cloudflare-ai-gateway.d.ts +0 -10
- package/src/wire/cloudflare-ai-gateway.ts +0 -36
- package/src/wire/cloudflare.ts +40 -0
- package/src/wire/factory-droid.d.ts +1 -1
- package/src/wire/factory-droid.ts +1 -1
package/src/model-manager.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VERSION } from "@linxiraos/pi-utils";
|
|
1
|
+
import { logger, VERSION } from "@linxiraos/pi-utils";
|
|
2
2
|
import { buildModel } from "./build";
|
|
3
3
|
import { collapseBuiltVariants } from "./compat/collapse";
|
|
4
4
|
import { applyCatalogMetrics, CatalogMetricsIndex } from "./identity/metrics";
|
|
@@ -57,6 +57,22 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
|
|
|
57
57
|
cacheTtlMs?: number;
|
|
58
58
|
/** When true, a successful dynamic fetch is the complete provider catalog and prunes static-only models. */
|
|
59
59
|
dynamicModelsAuthoritative?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* When true, a provider's dynamic rows are the deployment's whole truth
|
|
62
|
+
* for input modality: a live text-only row strips the bundled reference's
|
|
63
|
+
* image support instead of OR-merging it back. Declared by providers whose
|
|
64
|
+
* capability flags are authoritative and whose endpoints reject
|
|
65
|
+
* unsupported content (`github-copilot`, `deepinfra`, `coralbricks`).
|
|
66
|
+
*/
|
|
67
|
+
dynamicInputAuthoritative?: boolean;
|
|
68
|
+
/** When true, live reasoning capability replaces the fallback, including an explicit false. */
|
|
69
|
+
dynamicReasoningAuthoritative?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* When true, a live per-million price replaces the fallback even when it is
|
|
72
|
+
* an explicit `0` (a free model or a dropped cache-write charge). Without it,
|
|
73
|
+
* a zero discovery price is treated as unknown and the bundled rate is kept.
|
|
74
|
+
*/
|
|
75
|
+
dynamicCostAuthoritative?: boolean;
|
|
60
76
|
/**
|
|
61
77
|
* When true, a fresh cache never satisfies an online-eligible refresh: the
|
|
62
78
|
* dynamic fetch always runs (an explicit `"offline"` strategy is still
|
|
@@ -255,6 +271,11 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
255
271
|
const usableCachedModels = restoredCache.models.filter(model => !restoredCache.unresolvedModelIds.has(model.id));
|
|
256
272
|
const cacheHasUnresolvedHeaders = restoredCache.unresolvedModelIds.size > 0;
|
|
257
273
|
const dynamicModelsAuthoritative = options.dynamicModelsAuthoritative ?? false;
|
|
274
|
+
const mergeAuthority: DynamicMergeAuthority = {
|
|
275
|
+
input: options.dynamicInputAuthoritative ?? false,
|
|
276
|
+
reasoning: options.dynamicReasoningAuthoritative ?? false,
|
|
277
|
+
cost: options.dynamicCostAuthoritative ?? false,
|
|
278
|
+
};
|
|
258
279
|
const cacheDropIds = options.dropCachedModelIdsOnStaticMismatch;
|
|
259
280
|
const staticCatalogFingerprint = fingerprintStaticModels(staticModels, dynamicModelsAuthoritative);
|
|
260
281
|
// Endpoint-migration policy is cache identity: adding an id must invalidate
|
|
@@ -298,7 +319,10 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
298
319
|
? restoredCache.models.filter(model => !additiveStaticModelIds.has(model.id))
|
|
299
320
|
: restoredCache.models;
|
|
300
321
|
const cachedModels = additiveStaticModelIds
|
|
301
|
-
? mergeCatalogMetrics(
|
|
322
|
+
? mergeCatalogMetrics(
|
|
323
|
+
mergeDynamicModels(staticModels, cacheContribution, mergeAuthority),
|
|
324
|
+
restoredCache.models,
|
|
325
|
+
)
|
|
302
326
|
: restoredCache.models;
|
|
303
327
|
const source: ModelResolutionSource = cacheContribution.length > 0 ? "cache" : "bundled";
|
|
304
328
|
return {
|
|
@@ -351,10 +375,11 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
351
375
|
dynamicModels.length > 0 &&
|
|
352
376
|
(dynamicModelsAuthoritative || !hasModelsDevFetcher || modelsDevFetchSucceeded)
|
|
353
377
|
: modelsDevFetchSucceeded;
|
|
354
|
-
const mergedWithCache = mergeDynamicModels(staticModels, cacheModels);
|
|
378
|
+
const mergedWithCache = mergeDynamicModels(staticModels, cacheModels, mergeAuthority);
|
|
355
379
|
const mergedWithModelsDev = mergeDynamicModels(
|
|
356
380
|
mergedWithCache,
|
|
357
381
|
modelsDevModels,
|
|
382
|
+
mergeAuthority,
|
|
358
383
|
fetchedModelsDevModels?.explicitKindModels,
|
|
359
384
|
);
|
|
360
385
|
const catalogMetricsSource = modelsDevFetchSucceeded ? normalizedModelsDevModels : preparedCacheModels;
|
|
@@ -364,6 +389,7 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
364
389
|
const mergedModels = mergeDynamicModels(
|
|
365
390
|
mergedWithCatalogMetrics,
|
|
366
391
|
dynamicModels,
|
|
392
|
+
mergeAuthority,
|
|
367
393
|
fetchedDynamicModels?.explicitKindModels,
|
|
368
394
|
);
|
|
369
395
|
const models = collapseBuiltVariants(
|
|
@@ -416,7 +442,11 @@ export async function resolveProviderModels<TApi extends Api = Api, TModelsDevPa
|
|
|
416
442
|
? preparedLatestCacheModels.filter(model => !additiveStaticModelIds.has(model.id))
|
|
417
443
|
: preparedLatestCacheModels;
|
|
418
444
|
const fallbackSnapshotModels = collapseBuiltVariants(
|
|
419
|
-
mergeDynamicModels(
|
|
445
|
+
mergeDynamicModels(
|
|
446
|
+
mergeDynamicModels(staticModels, latestCacheModels, mergeAuthority),
|
|
447
|
+
modelsDevModels,
|
|
448
|
+
mergeAuthority,
|
|
449
|
+
),
|
|
420
450
|
);
|
|
421
451
|
if (fallbackSnapshotModels.length > 0 || latestCache !== null || cache !== null) {
|
|
422
452
|
writeModelCache(
|
|
@@ -544,9 +574,20 @@ function mergeCatalogMetrics<TApi extends Api>(
|
|
|
544
574
|
return applyCatalogMetrics(models, new CatalogMetricsIndex(catalogModels));
|
|
545
575
|
}
|
|
546
576
|
|
|
577
|
+
/**
|
|
578
|
+
* Which dynamic-row fields replace the existing row outright instead of
|
|
579
|
+
* merging additively. Set per provider by `ModelManagerOptions`.
|
|
580
|
+
*/
|
|
581
|
+
interface DynamicMergeAuthority {
|
|
582
|
+
readonly input: boolean;
|
|
583
|
+
readonly reasoning: boolean;
|
|
584
|
+
readonly cost: boolean;
|
|
585
|
+
}
|
|
586
|
+
|
|
547
587
|
function mergeDynamicModels<TApi extends Api>(
|
|
548
588
|
baseModels: readonly Model<TApi>[],
|
|
549
589
|
dynamicModels: readonly Model<TApi>[],
|
|
590
|
+
authority: DynamicMergeAuthority,
|
|
550
591
|
explicitKindModels?: ReadonlySet<Model<TApi>>,
|
|
551
592
|
): Model<TApi>[] {
|
|
552
593
|
// Empty-side fast paths: `mergeDynamicModels(base, [])` is the common shape
|
|
@@ -567,7 +608,7 @@ function mergeDynamicModels<TApi extends Api>(
|
|
|
567
608
|
// A policy-derived kind on a chat row is not permission to replace an
|
|
568
609
|
// authored runner. Only a kind present before materialization can do so.
|
|
569
610
|
if (modelKind(existingModel) !== "chat" && !explicitKindModels?.has(dynamicModel)) continue;
|
|
570
|
-
merged.set(dynamicModel.id, mergeDynamicModel(existingModel, dynamicModel));
|
|
611
|
+
merged.set(dynamicModel.id, mergeDynamicModel(existingModel, dynamicModel, authority));
|
|
571
612
|
}
|
|
572
613
|
return Array.from(merged.values());
|
|
573
614
|
}
|
|
@@ -598,39 +639,30 @@ export function fingerprintStaticModels<TApi extends Api>(
|
|
|
598
639
|
return `${MODEL_CACHE_FINGERPRINT_VERSION}:${Bun.hash(JSON.stringify(models)).toString(36)}`;
|
|
599
640
|
}
|
|
600
641
|
|
|
601
|
-
function mergeDynamicModel<TApi extends Api>(
|
|
642
|
+
function mergeDynamicModel<TApi extends Api>(
|
|
643
|
+
existingModel: Model<TApi>,
|
|
644
|
+
dynamicModel: Model<TApi>,
|
|
645
|
+
authority: DynamicMergeAuthority,
|
|
646
|
+
): Model<TApi> {
|
|
602
647
|
// When discovery resolves the same model id to a different endpoint (e.g.
|
|
603
648
|
// a GitHub Copilot business/enterprise host), the bundled reference's
|
|
604
|
-
// capabilities are pinned to another endpoint and no longer apply.
|
|
605
|
-
//
|
|
606
|
-
//
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
//
|
|
611
|
-
// there: without this carve-out a model that dropped those tags would keep
|
|
612
|
-
// the bundled reference's image support and the agent would go on sending
|
|
649
|
+
// capabilities are pinned to another endpoint and no longer apply.
|
|
650
|
+
// `authority.input` carries the descriptor's declaration that a
|
|
651
|
+
// provider's discovery rows are the deployment's whole truth for input
|
|
652
|
+
// modality (Copilot's pre-applied `supports.vision` fallback, DeepInfra's
|
|
653
|
+
// `vision`/`vlm` tags, CoralBricks' authoritative `supports_image_input`
|
|
654
|
+
// flag): without it, a live row that dropped image support would keep the
|
|
655
|
+
// bundled reference's image support and the agent would go on sending
|
|
613
656
|
// images to a now text-only route.
|
|
614
657
|
const endpointChanged = existingModel.baseUrl !== dynamicModel.baseUrl;
|
|
615
|
-
const dynamicInputAuthoritative =
|
|
616
|
-
endpointChanged ||
|
|
617
|
-
(existingModel.provider === "github-copilot" && dynamicModel.provider === "github-copilot") ||
|
|
618
|
-
(existingModel.provider === "deepinfra" && dynamicModel.provider === "deepinfra");
|
|
658
|
+
const dynamicInputAuthoritative = endpointChanged || authority.input;
|
|
619
659
|
const supportsImage = dynamicInputAuthoritative
|
|
620
660
|
? dynamicModel.input.includes("image")
|
|
621
661
|
: existingModel.input.includes("image") || dynamicModel.input.includes("image");
|
|
622
|
-
//
|
|
623
|
-
//
|
|
624
|
-
//
|
|
625
|
-
|
|
626
|
-
// stale `reasoning: true` back would re-arm an effort dial the route
|
|
627
|
-
// doesn't expose. Other providers keep the OR so a bundled reasoning flag
|
|
628
|
-
// survives a discovery row that simply omits the capability.
|
|
629
|
-
const dynamicReasoningAuthoritative =
|
|
630
|
-
existingModel.provider === "synthetic" && dynamicModel.provider === "synthetic";
|
|
631
|
-
const reasoning = dynamicReasoningAuthoritative
|
|
632
|
-
? dynamicModel.reasoning
|
|
633
|
-
: existingModel.reasoning || dynamicModel.reasoning;
|
|
662
|
+
// Providers with authoritative reasoning metadata must not regain a
|
|
663
|
+
// stale bundled dial after explicitly reporting no reasoning. Others
|
|
664
|
+
// retain the additive fallback for discovery that omits capabilities.
|
|
665
|
+
const reasoning = authority.reasoning ? dynamicModel.reasoning : existingModel.reasoning || dynamicModel.reasoning;
|
|
634
666
|
const longContextCost = dynamicModel.cost.longContext ?? existingModel.cost.longContext;
|
|
635
667
|
const timeBasedCost = dynamicModel.cost.timeBased ?? existingModel.cost.timeBased;
|
|
636
668
|
const existingHeaders = existingModel.resolveHeaders ?? existingModel.headers;
|
|
@@ -651,17 +683,26 @@ function mergeDynamicModel<TApi extends Api>(existingModel: Model<TApi>, dynamic
|
|
|
651
683
|
// the effort dial once the id is pinned to Responses (#12901).
|
|
652
684
|
const compat =
|
|
653
685
|
dynamicModel.compatConfig ?? (dynamicModel.api === existingModel.api ? existingModel.compatConfig : undefined);
|
|
686
|
+
// A live row that trusts only its own thinking (e.g. CoralBricks' advertised
|
|
687
|
+
// ladder) must not inherit the fallback's dial. Cached rows lose an
|
|
688
|
+
// explicit `thinking: undefined` to JSON, so the spread alone would.
|
|
689
|
+
const liveCompat = dynamicModel.compatConfig;
|
|
690
|
+
const trustsOwnThinking =
|
|
691
|
+
liveCompat !== undefined &&
|
|
692
|
+
"trustExplicitThinkingOnly" in liveCompat &&
|
|
693
|
+
liveCompat.trustExplicitThinkingOnly === true;
|
|
654
694
|
return buildModel({
|
|
655
695
|
...existingModel,
|
|
656
696
|
...dynamicModel,
|
|
657
697
|
name: preferDiscoveryName(dynamicModel.name, existingModel.name, dynamicModel.id),
|
|
658
698
|
reasoning,
|
|
699
|
+
...(trustsOwnThinking ? { thinking: dynamicModel.thinking } : {}),
|
|
659
700
|
input: supportsImage ? ["text", "image"] : ["text"],
|
|
660
701
|
cost: {
|
|
661
|
-
input: preferDiscoveryCost(dynamicModel.cost.input, existingModel.cost.input),
|
|
662
|
-
output: preferDiscoveryCost(dynamicModel.cost.output, existingModel.cost.output),
|
|
663
|
-
cacheRead: preferDiscoveryCost(dynamicModel.cost.cacheRead, existingModel.cost.cacheRead),
|
|
664
|
-
cacheWrite: preferDiscoveryCost(dynamicModel.cost.cacheWrite, existingModel.cost.cacheWrite),
|
|
702
|
+
input: preferDiscoveryCost(dynamicModel.cost.input, existingModel.cost.input, authority.cost),
|
|
703
|
+
output: preferDiscoveryCost(dynamicModel.cost.output, existingModel.cost.output, authority.cost),
|
|
704
|
+
cacheRead: preferDiscoveryCost(dynamicModel.cost.cacheRead, existingModel.cost.cacheRead, authority.cost),
|
|
705
|
+
cacheWrite: preferDiscoveryCost(dynamicModel.cost.cacheWrite, existingModel.cost.cacheWrite, authority.cost),
|
|
665
706
|
...(longContextCost ? { longContext: longContextCost } : {}),
|
|
666
707
|
...(timeBasedCost ? { timeBased: timeBasedCost } : {}),
|
|
667
708
|
},
|
|
@@ -678,8 +719,12 @@ function mergeDynamicModel<TApi extends Api>(existingModel: Model<TApi>, dynamic
|
|
|
678
719
|
} as ModelSpec<TApi>);
|
|
679
720
|
}
|
|
680
721
|
|
|
681
|
-
|
|
682
|
-
|
|
722
|
+
/**
|
|
723
|
+
* A zero discovery price usually means "not reported", so the fallback wins
|
|
724
|
+
* unless the provider declared its live prices authoritative (`zeroIsPrice`).
|
|
725
|
+
*/
|
|
726
|
+
function preferDiscoveryCost(discoveryCost: number, fallbackCost: number, zeroIsPrice: boolean): number {
|
|
727
|
+
if (Number.isFinite(discoveryCost) && (discoveryCost > 0 || (zeroIsPrice && discoveryCost === 0))) {
|
|
683
728
|
return discoveryCost;
|
|
684
729
|
}
|
|
685
730
|
return fallbackCost;
|
|
@@ -708,19 +753,40 @@ function preferDiscoveryLimit(discoveryLimit: number | null, fallbackLimit: numb
|
|
|
708
753
|
return discoveryLimit;
|
|
709
754
|
}
|
|
710
755
|
|
|
756
|
+
/**
|
|
757
|
+
* Materializes a remote source's rows. A row `buildModel` rejects (an id newer
|
|
758
|
+
* than this build's rule tree that lands on an equal-rank overlap) is skipped
|
|
759
|
+
* with a warning instead of discarding the source's other rows.
|
|
760
|
+
*
|
|
761
|
+
* @throws the first build error when rows were present but none built, so the
|
|
762
|
+
* caller treats the source as failed (cache/static fallback) rather than as an
|
|
763
|
+
* intentionally empty catalog that an authoritative provider would prune to.
|
|
764
|
+
*/
|
|
711
765
|
function buildDiscoveredModelSpecs<TApi extends Api>(value: unknown): DiscoveredModelSet<TApi> {
|
|
712
766
|
const models: Model<TApi>[] = [];
|
|
713
767
|
const explicitKindModels = new Set<Model<TApi>>();
|
|
714
768
|
if (!Array.isArray(value)) {
|
|
715
769
|
return { models, explicitKindModels };
|
|
716
770
|
}
|
|
771
|
+
const failures: unknown[] = [];
|
|
717
772
|
for (const item of value) {
|
|
718
|
-
if (isModelLike(item))
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
773
|
+
if (!isModelLike(item)) continue;
|
|
774
|
+
let model: Model<TApi>;
|
|
775
|
+
try {
|
|
776
|
+
model = buildModel(item as ModelSpec<TApi>);
|
|
777
|
+
} catch (error) {
|
|
778
|
+
failures.push(error);
|
|
779
|
+
logger.warn("model discovery skipped a model that failed to build", {
|
|
780
|
+
provider: item.provider,
|
|
781
|
+
model: item.id,
|
|
782
|
+
error: error instanceof Error ? error.message : String(error),
|
|
783
|
+
});
|
|
784
|
+
continue;
|
|
722
785
|
}
|
|
786
|
+
models.push(model);
|
|
787
|
+
if (item.kind !== undefined) explicitKindModels.add(model);
|
|
723
788
|
}
|
|
789
|
+
if (models.length === 0 && failures.length > 0) throw failures[0];
|
|
724
790
|
return { models, explicitKindModels };
|
|
725
791
|
}
|
|
726
792
|
|
package/src/model-tokenizer.ts
CHANGED
|
@@ -27,6 +27,9 @@ function claudeTokenizer(identity: ModelIdentity): ModelTokenizer | undefined {
|
|
|
27
27
|
if (identity.family === "sonnet" || identity.family === "fable" || identity.family === "mythos") {
|
|
28
28
|
return revisionAtLeast(identity.revision, "5") ? "claude-v5-sonnet" : "claude-v3";
|
|
29
29
|
}
|
|
30
|
+
// Haiku 5.5 moved to the post-4.7 tokenizer (~30% more tokens than Haiku
|
|
31
|
+
// 4.5). No Haiku-measured vocabulary is embedded; Sonnet 5's is the nearest.
|
|
32
|
+
if (identity.family === "haiku" && revisionAtLeast(identity.revision, "5.5")) return "claude-v5-sonnet";
|
|
30
33
|
return "claude-v3";
|
|
31
34
|
}
|
|
32
35
|
|