@oh-my-pi/pi-catalog 16.3.10 → 16.3.12
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 +21 -0
- package/package.json +3 -3
- package/src/compat/anthropic.ts +5 -8
- package/src/models.json +104 -27
- package/src/provider-models/openai-compat.ts +91 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.3.12] - 2026-07-08
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed LiteLLM discovery stopping at `/model_group/info` when that endpoint omitted `supports_vision`; it now continues to `/model/info` and preserves `model_info.supports_vision=true` for vision-capable proxy models. ([#4747](https://github.com/can1357/oh-my-pi/issues/4747))
|
|
10
|
+
- Fixed LiteLLM discovery to fall back to bundled catalog metadata when `models.dev` lacks a model reference, preserving reasoning and thinking support for models such as `glm-5.2`. ([#4695](https://github.com/can1357/oh-my-pi/issues/4695))
|
|
11
|
+
- Detected Azure AI Inference / Foundry Anthropic routes as strict-tool-incompatible so resolved Anthropic compat disables strict tools before request construction ([#4679](https://github.com/can1357/oh-my-pi/issues/4679)).
|
|
12
|
+
|
|
13
|
+
## [16.3.11] - 2026-07-06
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added Claude Haiku 4.5 (JP) model support
|
|
18
|
+
- Added tencent/hy3 model support via ZenMux
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Updated naming format for various synthetic models to include provider prefix
|
|
23
|
+
- Adjusted context window limit for MiniMax-M3 model
|
|
24
|
+
- Updated pricing for select models
|
|
25
|
+
|
|
5
26
|
## [16.3.10] - 2026-07-06
|
|
6
27
|
|
|
7
28
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-catalog",
|
|
4
|
-
"version": "16.3.
|
|
4
|
+
"version": "16.3.12",
|
|
5
5
|
"description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@bufbuild/protobuf": "^2.12.0",
|
|
37
|
-
"@oh-my-pi/pi-utils": "16.3.
|
|
37
|
+
"@oh-my-pi/pi-utils": "16.3.12",
|
|
38
38
|
"arktype": "^2.2.0",
|
|
39
39
|
"zod": "^4"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@oh-my-pi/pi-ai": "16.3.
|
|
42
|
+
"@oh-my-pi/pi-ai": "16.3.12",
|
|
43
43
|
"@types/bun": "^1.3.14"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
package/src/compat/anthropic.ts
CHANGED
|
@@ -99,18 +99,15 @@ export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): Res
|
|
|
99
99
|
// (issue #4192).
|
|
100
100
|
const isZenmux = modelMatchesHost(spec, "zenmux");
|
|
101
101
|
const requiresThinkingEnabled = modelMatchesHost(spec, "moonshotNative") && matchesKimiK27CodeFamily(spec);
|
|
102
|
+
const isVertex = isVertexAnthropicRoute(baseUrl);
|
|
103
|
+
const isBedrock = isBedrockAnthropicRoute(baseUrl);
|
|
104
|
+
const isAzure = isAzureAnthropicRoute(baseUrl);
|
|
102
105
|
const signingEndpoint =
|
|
103
|
-
official ||
|
|
104
|
-
isCopilot ||
|
|
105
|
-
isZenmux ||
|
|
106
|
-
isCloudflareAnthropicGateway(baseUrl) ||
|
|
107
|
-
isVertexAnthropicRoute(baseUrl) ||
|
|
108
|
-
isBedrockAnthropicRoute(baseUrl) ||
|
|
109
|
-
isAzureAnthropicRoute(baseUrl);
|
|
106
|
+
official || isCopilot || isZenmux || isCloudflareAnthropicGateway(baseUrl) || isVertex || isBedrock || isAzure;
|
|
110
107
|
const compat: ResolvedAnthropicCompat = {
|
|
111
108
|
officialEndpoint: official,
|
|
112
109
|
signingEndpoint,
|
|
113
|
-
disableStrictTools:
|
|
110
|
+
disableStrictTools: isAzure,
|
|
114
111
|
disableAdaptiveThinking: false,
|
|
115
112
|
supportsEagerToolInputStreaming: !isCopilot,
|
|
116
113
|
// Long cache retention is only sent to the official API by default;
|
package/src/models.json
CHANGED
|
@@ -9073,6 +9073,35 @@
|
|
|
9073
9073
|
"contextWindow": 128000,
|
|
9074
9074
|
"maxTokens": 4096
|
|
9075
9075
|
},
|
|
9076
|
+
"jp.anthropic.claude-haiku-4-5-20251001-v1:0": {
|
|
9077
|
+
"id": "jp.anthropic.claude-haiku-4-5-20251001-v1:0",
|
|
9078
|
+
"name": "Claude Haiku 4.5 (JP)",
|
|
9079
|
+
"api": "bedrock-converse-stream",
|
|
9080
|
+
"provider": "amazon-bedrock",
|
|
9081
|
+
"baseUrl": "https://bedrock-runtime.us-east-1.amazonaws.com",
|
|
9082
|
+
"reasoning": true,
|
|
9083
|
+
"input": [
|
|
9084
|
+
"text",
|
|
9085
|
+
"image"
|
|
9086
|
+
],
|
|
9087
|
+
"cost": {
|
|
9088
|
+
"input": 1,
|
|
9089
|
+
"output": 5,
|
|
9090
|
+
"cacheRead": 0.1,
|
|
9091
|
+
"cacheWrite": 1.25
|
|
9092
|
+
},
|
|
9093
|
+
"contextWindow": 200000,
|
|
9094
|
+
"maxTokens": 64000,
|
|
9095
|
+
"thinking": {
|
|
9096
|
+
"mode": "budget",
|
|
9097
|
+
"efforts": [
|
|
9098
|
+
"minimal",
|
|
9099
|
+
"low",
|
|
9100
|
+
"medium",
|
|
9101
|
+
"high"
|
|
9102
|
+
]
|
|
9103
|
+
}
|
|
9104
|
+
},
|
|
9076
9105
|
"jp.anthropic.claude-opus-4-7": {
|
|
9077
9106
|
"id": "jp.anthropic.claude-opus-4-7",
|
|
9078
9107
|
"name": "Claude Opus 4.7 (JP)",
|
|
@@ -51831,6 +51860,25 @@
|
|
|
51831
51860
|
"contextWindow": null,
|
|
51832
51861
|
"maxTokens": null
|
|
51833
51862
|
},
|
|
51863
|
+
"tencent/hy3": {
|
|
51864
|
+
"id": "tencent/hy3",
|
|
51865
|
+
"name": "tencent/hy3",
|
|
51866
|
+
"api": "openai-completions",
|
|
51867
|
+
"provider": "nanogpt",
|
|
51868
|
+
"baseUrl": "https://nano-gpt.com/api/v1",
|
|
51869
|
+
"reasoning": false,
|
|
51870
|
+
"input": [
|
|
51871
|
+
"text"
|
|
51872
|
+
],
|
|
51873
|
+
"cost": {
|
|
51874
|
+
"input": 0,
|
|
51875
|
+
"output": 0,
|
|
51876
|
+
"cacheRead": 0,
|
|
51877
|
+
"cacheWrite": 0
|
|
51878
|
+
},
|
|
51879
|
+
"contextWindow": 262144,
|
|
51880
|
+
"maxTokens": null
|
|
51881
|
+
},
|
|
51834
51882
|
"tencent/hy3-preview": {
|
|
51835
51883
|
"id": "tencent/hy3-preview",
|
|
51836
51884
|
"name": "Hy3 Preview",
|
|
@@ -71923,9 +71971,9 @@
|
|
|
71923
71971
|
"text"
|
|
71924
71972
|
],
|
|
71925
71973
|
"cost": {
|
|
71926
|
-
"input": 0.
|
|
71927
|
-
"output":
|
|
71928
|
-
"cacheRead": 0.
|
|
71974
|
+
"input": 0.9086,
|
|
71975
|
+
"output": 2.8556,
|
|
71976
|
+
"cacheRead": 0.16874,
|
|
71929
71977
|
"cacheWrite": 0
|
|
71930
71978
|
},
|
|
71931
71979
|
"contextWindow": 1048576,
|
|
@@ -72145,7 +72193,7 @@
|
|
|
72145
72193
|
"synthetic": {
|
|
72146
72194
|
"hf:MiniMaxAI/MiniMax-M3": {
|
|
72147
72195
|
"id": "hf:MiniMaxAI/MiniMax-M3",
|
|
72148
|
-
"name": "MiniMax-M3",
|
|
72196
|
+
"name": "MiniMaxAI/MiniMax-M3",
|
|
72149
72197
|
"api": "openai-completions",
|
|
72150
72198
|
"provider": "synthetic",
|
|
72151
72199
|
"baseUrl": "https://api.synthetic.new/openai/v1",
|
|
@@ -72160,7 +72208,7 @@
|
|
|
72160
72208
|
"cacheRead": 0.6,
|
|
72161
72209
|
"cacheWrite": 0
|
|
72162
72210
|
},
|
|
72163
|
-
"contextWindow":
|
|
72211
|
+
"contextWindow": 262144,
|
|
72164
72212
|
"maxTokens": 65536,
|
|
72165
72213
|
"thinking": {
|
|
72166
72214
|
"mode": "effort",
|
|
@@ -72175,7 +72223,7 @@
|
|
|
72175
72223
|
},
|
|
72176
72224
|
"hf:moonshotai/Kimi-K2.7-Code": {
|
|
72177
72225
|
"id": "hf:moonshotai/Kimi-K2.7-Code",
|
|
72178
|
-
"name": "Kimi
|
|
72226
|
+
"name": "moonshotai/Kimi-K2.7-Code",
|
|
72179
72227
|
"api": "openai-completions",
|
|
72180
72228
|
"provider": "synthetic",
|
|
72181
72229
|
"baseUrl": "https://api.synthetic.new/openai/v1",
|
|
@@ -72205,7 +72253,7 @@
|
|
|
72205
72253
|
},
|
|
72206
72254
|
"hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4": {
|
|
72207
72255
|
"id": "hf:nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4",
|
|
72208
|
-
"name": "Nemotron
|
|
72256
|
+
"name": "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4",
|
|
72209
72257
|
"api": "openai-completions",
|
|
72210
72258
|
"provider": "synthetic",
|
|
72211
72259
|
"baseUrl": "https://api.synthetic.new/openai/v1",
|
|
@@ -72234,7 +72282,7 @@
|
|
|
72234
72282
|
},
|
|
72235
72283
|
"hf:openai/gpt-oss-120b": {
|
|
72236
72284
|
"id": "hf:openai/gpt-oss-120b",
|
|
72237
|
-
"name": "
|
|
72285
|
+
"name": "openai/gpt-oss-120b",
|
|
72238
72286
|
"api": "openai-completions",
|
|
72239
72287
|
"provider": "synthetic",
|
|
72240
72288
|
"baseUrl": "https://api.synthetic.new/openai/v1",
|
|
@@ -72261,7 +72309,7 @@
|
|
|
72261
72309
|
},
|
|
72262
72310
|
"hf:Qwen/Qwen3.6-27B": {
|
|
72263
72311
|
"id": "hf:Qwen/Qwen3.6-27B",
|
|
72264
|
-
"name": "Qwen3.6
|
|
72312
|
+
"name": "Qwen/Qwen3.6-27B",
|
|
72265
72313
|
"api": "openai-completions",
|
|
72266
72314
|
"provider": "synthetic",
|
|
72267
72315
|
"baseUrl": "https://api.synthetic.new/openai/v1",
|
|
@@ -72290,7 +72338,7 @@
|
|
|
72290
72338
|
},
|
|
72291
72339
|
"hf:zai-org/GLM-4.7-Flash": {
|
|
72292
72340
|
"id": "hf:zai-org/GLM-4.7-Flash",
|
|
72293
|
-
"name": "GLM-4.7-Flash",
|
|
72341
|
+
"name": "zai-org/GLM-4.7-Flash",
|
|
72294
72342
|
"api": "openai-completions",
|
|
72295
72343
|
"provider": "synthetic",
|
|
72296
72344
|
"baseUrl": "https://api.synthetic.new/openai/v1",
|
|
@@ -72319,7 +72367,7 @@
|
|
|
72319
72367
|
},
|
|
72320
72368
|
"hf:zai-org/GLM-5.2": {
|
|
72321
72369
|
"id": "hf:zai-org/GLM-5.2",
|
|
72322
|
-
"name": "GLM-5.2",
|
|
72370
|
+
"name": "zai-org/GLM-5.2",
|
|
72323
72371
|
"api": "openai-completions",
|
|
72324
72372
|
"provider": "synthetic",
|
|
72325
72373
|
"baseUrl": "https://api.synthetic.new/openai/v1",
|
|
@@ -83219,14 +83267,6 @@
|
|
|
83219
83267
|
},
|
|
83220
83268
|
"contextWindow": 2000000,
|
|
83221
83269
|
"maxTokens": 2000000,
|
|
83222
|
-
"compat": {
|
|
83223
|
-
"reasoningEffortMap": {
|
|
83224
|
-
"minimal": "low"
|
|
83225
|
-
},
|
|
83226
|
-
"includeEncryptedReasoning": false,
|
|
83227
|
-
"filterReasoningHistory": true,
|
|
83228
|
-
"omitReasoningEffort": false
|
|
83229
|
-
},
|
|
83230
83270
|
"thinking": {
|
|
83231
83271
|
"mode": "effort",
|
|
83232
83272
|
"efforts": [
|
|
@@ -83239,6 +83279,14 @@
|
|
|
83239
83279
|
"effortMap": {
|
|
83240
83280
|
"minimal": "low"
|
|
83241
83281
|
}
|
|
83282
|
+
},
|
|
83283
|
+
"compat": {
|
|
83284
|
+
"reasoningEffortMap": {
|
|
83285
|
+
"minimal": "low"
|
|
83286
|
+
},
|
|
83287
|
+
"includeEncryptedReasoning": false,
|
|
83288
|
+
"filterReasoningHistory": true,
|
|
83289
|
+
"omitReasoningEffort": false
|
|
83242
83290
|
}
|
|
83243
83291
|
},
|
|
83244
83292
|
"grok-4.3": {
|
|
@@ -83260,14 +83308,6 @@
|
|
|
83260
83308
|
},
|
|
83261
83309
|
"contextWindow": 1000000,
|
|
83262
83310
|
"maxTokens": 1000000,
|
|
83263
|
-
"compat": {
|
|
83264
|
-
"reasoningEffortMap": {
|
|
83265
|
-
"minimal": "low"
|
|
83266
|
-
},
|
|
83267
|
-
"includeEncryptedReasoning": false,
|
|
83268
|
-
"filterReasoningHistory": true,
|
|
83269
|
-
"omitReasoningEffort": false
|
|
83270
|
-
},
|
|
83271
83311
|
"thinking": {
|
|
83272
83312
|
"mode": "effort",
|
|
83273
83313
|
"efforts": [
|
|
@@ -83280,6 +83320,14 @@
|
|
|
83280
83320
|
"effortMap": {
|
|
83281
83321
|
"minimal": "low"
|
|
83282
83322
|
}
|
|
83323
|
+
},
|
|
83324
|
+
"compat": {
|
|
83325
|
+
"reasoningEffortMap": {
|
|
83326
|
+
"minimal": "low"
|
|
83327
|
+
},
|
|
83328
|
+
"includeEncryptedReasoning": false,
|
|
83329
|
+
"filterReasoningHistory": true,
|
|
83330
|
+
"omitReasoningEffort": false
|
|
83283
83331
|
}
|
|
83284
83332
|
},
|
|
83285
83333
|
"grok-build": {
|
|
@@ -88423,6 +88471,35 @@
|
|
|
88423
88471
|
"requiresEffort": true
|
|
88424
88472
|
}
|
|
88425
88473
|
},
|
|
88474
|
+
"tencent/hy3": {
|
|
88475
|
+
"id": "tencent/hy3",
|
|
88476
|
+
"name": "Hy3",
|
|
88477
|
+
"api": "openai-completions",
|
|
88478
|
+
"provider": "zenmux",
|
|
88479
|
+
"baseUrl": "https://zenmux.ai/api/v1",
|
|
88480
|
+
"reasoning": true,
|
|
88481
|
+
"input": [
|
|
88482
|
+
"text"
|
|
88483
|
+
],
|
|
88484
|
+
"cost": {
|
|
88485
|
+
"input": 0.134561595,
|
|
88486
|
+
"output": 0.539161765,
|
|
88487
|
+
"cacheRead": 0.033869245,
|
|
88488
|
+
"cacheWrite": 0
|
|
88489
|
+
},
|
|
88490
|
+
"contextWindow": 262144,
|
|
88491
|
+
"maxTokens": null,
|
|
88492
|
+
"thinking": {
|
|
88493
|
+
"mode": "effort",
|
|
88494
|
+
"efforts": [
|
|
88495
|
+
"minimal",
|
|
88496
|
+
"low",
|
|
88497
|
+
"medium",
|
|
88498
|
+
"high",
|
|
88499
|
+
"xhigh"
|
|
88500
|
+
]
|
|
88501
|
+
}
|
|
88502
|
+
},
|
|
88426
88503
|
"tencent/hy3-preview": {
|
|
88427
88504
|
"id": "tencent/hy3-preview",
|
|
88428
88505
|
"name": "Hy3 preview",
|
|
@@ -3032,6 +3032,15 @@ export interface FetchLiteLLMRichModelsOptions<TApi extends Api> {
|
|
|
3032
3032
|
}
|
|
3033
3033
|
|
|
3034
3034
|
type LiteLLMRichModelEntry = Record<string, unknown>;
|
|
3035
|
+
type LiteLLMRichEndpointModel<TApi extends Api> = {
|
|
3036
|
+
model: ModelSpec<TApi>;
|
|
3037
|
+
supportsVision: unknown;
|
|
3038
|
+
supportsReasoning: unknown;
|
|
3039
|
+
hasContextWindow: boolean;
|
|
3040
|
+
hasMaxTokens: boolean;
|
|
3041
|
+
hasToolMetadata: boolean;
|
|
3042
|
+
hasSupportedOpenAIParams: boolean;
|
|
3043
|
+
};
|
|
3035
3044
|
|
|
3036
3045
|
const LITELLM_RICH_ENDPOINTS = ["/model_group/info", "/v2/model/info", "/model/info", "/v1/model/info"] as const;
|
|
3037
3046
|
export const OPENAI_COMPAT_DISCOVERY_DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
@@ -3264,7 +3273,7 @@ async function fetchLiteLLMRichEndpoint<TApi extends Api>(
|
|
|
3264
3273
|
managementBaseUrl: string,
|
|
3265
3274
|
runtimeBaseUrl: string,
|
|
3266
3275
|
signal?: AbortSignal,
|
|
3267
|
-
): Promise<
|
|
3276
|
+
): Promise<{ models: LiteLLMRichEndpointModel<TApi>[]; incompleteVisionMetadata: boolean } | null> {
|
|
3268
3277
|
const fetchImpl = discoveryFetch(options.fetch);
|
|
3269
3278
|
const requestHeaders: Record<string, string> = {
|
|
3270
3279
|
Accept: "application/json",
|
|
@@ -3296,17 +3305,39 @@ async function fetchLiteLLMRichEndpoint<TApi extends Api>(
|
|
|
3296
3305
|
if (!entries || entries.length === 0) {
|
|
3297
3306
|
return null;
|
|
3298
3307
|
}
|
|
3299
|
-
const deduped = new Map<string,
|
|
3308
|
+
const deduped = new Map<string, LiteLLMRichEndpointModel<TApi>>();
|
|
3309
|
+
let incompleteVisionMetadata = false;
|
|
3300
3310
|
for (const entry of entries) {
|
|
3301
3311
|
const model = mapLiteLLMRichEntry(entry, options, runtimeBaseUrl);
|
|
3302
3312
|
if (model) {
|
|
3303
|
-
|
|
3313
|
+
const supportsVision = getLiteLLMMetadataValue(entry, "supports_vision");
|
|
3314
|
+
const supportsReasoning = getLiteLLMMetadataValue(entry, "supports_reasoning");
|
|
3315
|
+
const supportsFunctionCalling = getLiteLLMMetadataValue(entry, "supports_function_calling");
|
|
3316
|
+
const supportedOpenAIParams = getSupportedOpenAIParams(entry);
|
|
3317
|
+
if (supportsVision !== true && supportsVision !== false) {
|
|
3318
|
+
incompleteVisionMetadata = true;
|
|
3319
|
+
}
|
|
3320
|
+
deduped.set(model.id, {
|
|
3321
|
+
model,
|
|
3322
|
+
supportsVision,
|
|
3323
|
+
supportsReasoning,
|
|
3324
|
+
hasContextWindow: toPositiveNumber(getLiteLLMMetadataValue(entry, "max_input_tokens"), null) !== null,
|
|
3325
|
+
hasMaxTokens: toPositiveNumber(getLiteLLMMetadataValue(entry, "max_output_tokens"), null) !== null,
|
|
3326
|
+
hasToolMetadata:
|
|
3327
|
+
supportsFunctionCalling === true ||
|
|
3328
|
+
supportsFunctionCalling === false ||
|
|
3329
|
+
supportedOpenAIParams !== undefined,
|
|
3330
|
+
hasSupportedOpenAIParams: supportedOpenAIParams !== undefined,
|
|
3331
|
+
});
|
|
3304
3332
|
}
|
|
3305
3333
|
}
|
|
3306
3334
|
if (deduped.size === 0) {
|
|
3307
3335
|
return null;
|
|
3308
3336
|
}
|
|
3309
|
-
return
|
|
3337
|
+
return {
|
|
3338
|
+
models: Array.from(deduped.values()).sort((left, right) => left.model.id.localeCompare(right.model.id)),
|
|
3339
|
+
incompleteVisionMetadata,
|
|
3340
|
+
};
|
|
3310
3341
|
}
|
|
3311
3342
|
|
|
3312
3343
|
export async function fetchLiteLLMRichModels<TApi extends Api>(
|
|
@@ -3318,13 +3349,55 @@ export async function fetchLiteLLMRichModels<TApi extends Api>(
|
|
|
3318
3349
|
return null;
|
|
3319
3350
|
}
|
|
3320
3351
|
const fetchModels = async (signal?: AbortSignal): Promise<ModelSpec<TApi>[] | null> => {
|
|
3352
|
+
const deduped = new Map<string, LiteLLMRichEndpointModel<TApi>>();
|
|
3321
3353
|
for (const endpoint of LITELLM_RICH_ENDPOINTS) {
|
|
3322
|
-
const
|
|
3323
|
-
if (
|
|
3324
|
-
|
|
3354
|
+
const result = await fetchLiteLLMRichEndpoint(endpoint, options, managementBaseUrl, runtimeBaseUrl, signal);
|
|
3355
|
+
if (!result) {
|
|
3356
|
+
continue;
|
|
3357
|
+
}
|
|
3358
|
+
const hadPriorModels = deduped.size > 0;
|
|
3359
|
+
for (const next of result.models) {
|
|
3360
|
+
const existing = deduped.get(next.model.id);
|
|
3361
|
+
if (!existing) {
|
|
3362
|
+
if (!hadPriorModels) {
|
|
3363
|
+
deduped.set(next.model.id, next);
|
|
3364
|
+
}
|
|
3365
|
+
continue;
|
|
3366
|
+
}
|
|
3367
|
+
const model: ModelSpec<TApi> = {
|
|
3368
|
+
...existing.model,
|
|
3369
|
+
name: next.model.name === next.model.id ? existing.model.name : next.model.name,
|
|
3370
|
+
contextWindow: next.hasContextWindow ? next.model.contextWindow : existing.model.contextWindow,
|
|
3371
|
+
maxTokens: next.hasMaxTokens ? next.model.maxTokens : existing.model.maxTokens,
|
|
3372
|
+
input:
|
|
3373
|
+
next.supportsVision === true || next.supportsVision === false
|
|
3374
|
+
? next.model.input
|
|
3375
|
+
: existing.model.input,
|
|
3376
|
+
reasoning: typeof next.supportsReasoning === "boolean" ? next.model.reasoning : existing.model.reasoning,
|
|
3377
|
+
compat: next.hasSupportedOpenAIParams ? next.model.compat : existing.model.compat,
|
|
3378
|
+
};
|
|
3379
|
+
if (next.hasToolMetadata) {
|
|
3380
|
+
model.supportsTools = next.model.supportsTools;
|
|
3381
|
+
}
|
|
3382
|
+
deduped.set(next.model.id, { ...next, model });
|
|
3383
|
+
}
|
|
3384
|
+
let hasIncompleteVisionMetadata = false;
|
|
3385
|
+
for (const entry of deduped.values()) {
|
|
3386
|
+
if (entry.supportsVision !== true && entry.supportsVision !== false) {
|
|
3387
|
+
hasIncompleteVisionMetadata = true;
|
|
3388
|
+
break;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
if (!hasIncompleteVisionMetadata) {
|
|
3392
|
+
break;
|
|
3325
3393
|
}
|
|
3326
3394
|
}
|
|
3327
|
-
|
|
3395
|
+
if (deduped.size === 0) {
|
|
3396
|
+
return null;
|
|
3397
|
+
}
|
|
3398
|
+
return Array.from(deduped.values())
|
|
3399
|
+
.map(entry => entry.model)
|
|
3400
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
3328
3401
|
};
|
|
3329
3402
|
if (options.signal !== undefined) {
|
|
3330
3403
|
return fetchModels(options.signal);
|
|
@@ -3339,18 +3412,20 @@ export function litellmModelManagerOptions(
|
|
|
3339
3412
|
const baseUrl = config?.baseUrl ?? Bun.env.LITELLM_BASE_URL ?? "http://localhost:4000/v1";
|
|
3340
3413
|
return {
|
|
3341
3414
|
providerId: "litellm",
|
|
3342
|
-
// rich-
|
|
3343
|
-
//
|
|
3344
|
-
//
|
|
3345
|
-
//
|
|
3346
|
-
|
|
3415
|
+
// rich-v4 invalidates rows cached before LiteLLM ids gained bundled
|
|
3416
|
+
// reference fallback and before discovery continued past `/model_group/info`
|
|
3417
|
+
// when that endpoint omitted vision metadata. Earlier versions handled
|
|
3418
|
+
// reseller usage-suffix stripping and placeholder-only `all-team-models`
|
|
3419
|
+
// filtering; bump the version whenever the mappers below change, or warm
|
|
3420
|
+
// authoritative caches keep serving pre-change rows for the full TTL.
|
|
3421
|
+
cacheProviderId: `litellm:rich-v4:${Bun.hash(baseUrl).toString(36)}`,
|
|
3347
3422
|
// litellm is a local-only proxy and is never bundled in models.json (that
|
|
3348
3423
|
// would leak the machine's localhost catalog). Prefer the proxy's richer
|
|
3349
|
-
// management metadata, then
|
|
3350
|
-
//
|
|
3424
|
+
// management metadata, then enrich ids against models.dev with the bundled
|
|
3425
|
+
// catalog as a fallback before using /v1/models.
|
|
3351
3426
|
fetchDynamicModels: async () => {
|
|
3352
3427
|
const modelsDevReferences = await loadModelsDevReferences<"openai-completions">(config?.fetch);
|
|
3353
|
-
const resolveReference = (
|
|
3428
|
+
const resolveReference = createReferenceResolver(modelsDevReferences);
|
|
3354
3429
|
const richModels = await fetchLiteLLMRichModels({
|
|
3355
3430
|
api: "openai-completions",
|
|
3356
3431
|
provider: "litellm",
|