@heventure/model-provider-x 0.2.5-beta.0 → 0.2.5

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 CHANGED
@@ -145,7 +145,7 @@ The merge order is:
145
145
  1. User overrides such as `--modalities`.
146
146
  2. Provider or native runtime metadata.
147
147
  3. Project-local registry overrides.
148
- 4. Built-in models.dev-shaped metadata.
148
+ 4. Built-in models.dev metadata (5000+ models from 140+ providers).
149
149
  5. Conservative model-name heuristics.
150
150
 
151
151
  By default, the CLI reads `model-provider-x.models.jsonc` from the current working directory when it exists.
@@ -180,6 +180,16 @@ Example registry:
180
180
  }
181
181
  ```
182
182
 
183
+ ### Updating Models.dev Data
184
+
185
+ The built-in models.dev data is bundled with the package. To update it to the latest version:
186
+
187
+ ```bash
188
+ npm run update-models-dev
189
+ ```
190
+
191
+ This fetches the latest model metadata from [models.dev](https://models.dev) and updates `src/data/models-dev.json`.
192
+
183
193
  ## Commands
184
194
 
185
195
  ```bash
@@ -1,23 +1,28 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { parse } from "jsonc-parser";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, resolve } from "node:path";
3
5
  export const DEFAULT_MODEL_REGISTRY_FILE = "model-provider-x.models.jsonc";
4
- const BUILT_IN_MODELS_DEV_REGISTRY = {
5
- source: "models-dev",
6
- providers: {
7
- openai: {
8
- models: {
9
- "gpt-oss-20b": {
10
- type: "llm",
11
- contextLength: 131072,
12
- capabilities: {
13
- reasoning: true,
14
- toolCall: true
15
- }
16
- }
17
- }
18
- }
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const MODELS_DEV_DATA_PATH = resolve(__dirname, "../data/models-dev.json");
8
+ let modelsDevCache = null;
9
+ async function loadModelsDevRegistry() {
10
+ if (modelsDevCache) {
11
+ return modelsDevCache;
12
+ }
13
+ try {
14
+ const text = await readFile(MODELS_DEV_DATA_PATH, "utf-8");
15
+ const data = JSON.parse(text);
16
+ modelsDevCache = {
17
+ source: data.source ?? "models-dev",
18
+ providers: data.providers
19
+ };
20
+ return modelsDevCache;
21
+ }
22
+ catch {
23
+ return { source: "models-dev", providers: {} };
19
24
  }
20
- };
25
+ }
21
26
  export async function loadModelRegistryFile(path) {
22
27
  const text = await readFile(path, "utf8");
23
28
  const value = parse(text);
@@ -27,8 +32,9 @@ export async function loadModelRegistryFile(path) {
27
32
  return normalizeRegistry(value, "local-registry");
28
33
  }
29
34
  export async function resolveModelRegistryMetadata(input) {
35
+ const builtInRegistry = input.includeBuiltIn === false ? undefined : await loadModelsDevRegistry();
30
36
  const registries = [
31
- ...(input.includeBuiltIn === false ? [] : [BUILT_IN_MODELS_DEV_REGISTRY]),
37
+ ...(builtInRegistry ? [builtInRegistry] : []),
32
38
  ...(input.registries ?? [])
33
39
  ];
34
40
  let result;
@@ -51,6 +57,7 @@ function normalizeRegistry(value, fallbackSource) {
51
57
  }
52
58
  function lookupRegistryModel(registry, providerId, modelId) {
53
59
  const aliases = modelAliases(modelId);
60
+ // First try the specified provider
54
61
  if (providerId) {
55
62
  const providerModels = registry.providers?.[providerId]?.models;
56
63
  const match = lookupModel(providerModels, aliases);
@@ -66,6 +73,15 @@ function lookupRegistryModel(registry, providerId, modelId) {
66
73
  }
67
74
  }
68
75
  }
76
+ // Search across all providers if not found in specified provider
77
+ if (registry.providers) {
78
+ for (const [, provider] of Object.entries(registry.providers)) {
79
+ const match = lookupModel(provider.models, aliases);
80
+ if (match) {
81
+ return match;
82
+ }
83
+ }
84
+ }
69
85
  return lookupModel(registry.models, aliases);
70
86
  }
71
87
  function lookupModel(models, aliases) {
@@ -159,9 +175,33 @@ function normalizeModels(value) {
159
175
  return Object.keys(models).length ? models : undefined;
160
176
  }
161
177
  function modelAliases(modelId) {
162
- const normalized = modelId.trim();
178
+ const normalized = modelId.trim().toLowerCase();
163
179
  const withoutPrefix = normalized.includes("/") ? normalized.split("/").pop() : normalized;
164
- return [...new Set([normalized, withoutPrefix])];
180
+ // Strip common suffixes for fuzzy matching
181
+ const suffixes = [
182
+ "-it", "-qat", "-instruct", "-chat", "-gguf", "-gptq", "-awq", "-exl2",
183
+ "-fp16", "-fp32", "-int4", "-int8", "-4bit", "-8bit", "-16bit",
184
+ "-preview", "-latest", "-beta", "-alpha", "-rc", "-snapshot",
185
+ "-mlx", "-mlxc", "-bnb", "-hqq",
186
+ "-ud", "-xl", "-xs", "-small", "-medium", "-large", "-mini", "-nano", "-micro",
187
+ "-turbo", "-fast", "-pro", "-plus", "-max", "-ultra", "-flash", "-lite",
188
+ "-mtp", "-moe", "-a17b", "-a22b", "-a3b", "-a10b", "-a12b", "-a55b",
189
+ "-chat", "-base", "-raw", "-uncensored", "-abliterated"
190
+ ];
191
+ let fuzzy = withoutPrefix;
192
+ for (const suffix of suffixes) {
193
+ if (fuzzy.endsWith(suffix)) {
194
+ fuzzy = fuzzy.slice(0, -suffix.length);
195
+ break;
196
+ }
197
+ }
198
+ // Also try removing version suffixes like "-v1", "-v2", etc.
199
+ const versionMatch = fuzzy.match(/-v\d+$/);
200
+ const withoutVersion = versionMatch ? fuzzy.slice(0, -versionMatch[0].length) : fuzzy;
201
+ // Try removing parameter size suffixes like "-12b", "-7b", "-70b", etc.
202
+ const paramMatch = fuzzy.match(/-\d+[bBmMkKtT]$/);
203
+ const withoutParams = paramMatch ? fuzzy.slice(0, -paramMatch[0].length) : fuzzy;
204
+ return [...new Set([normalized, withoutPrefix, fuzzy, withoutVersion, withoutParams])];
165
205
  }
166
206
  function cleanModelInfo(model) {
167
207
  const capabilities = model.capabilities?.toolCall || model.capabilities?.reasoning