dsh-model-params 0.1.0 → 0.1.1
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/lib/modelsdev.js +77 -43
- package/package.json +1 -1
package/lib/modelsdev.js
CHANGED
|
@@ -1,39 +1,62 @@
|
|
|
1
1
|
// dsh-model-params — pure models.dev catalog parsing and matching helpers.
|
|
2
2
|
//
|
|
3
|
-
// models.dev/api.json shape (
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// REAL models.dev/api.json shape (verified live):
|
|
4
|
+
// root = flat provider map { <id>: { id, name, ..., models: {
|
|
5
|
+
// <modelId>: { id, name, family, reasoning?, reasoning_options?:
|
|
6
|
+
// [ {type:'toggle'}, {type:'effort', values:[...]} ], limit?: { context?, output? } } } } }
|
|
6
7
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// validation.
|
|
8
|
+
// Matching mirrors the upstream dsh-llm-newapi approach: keys tried are the
|
|
9
|
+
// full gateway id and its last path segment; family-prefix hints decide which
|
|
10
|
+
// catalog provider leads; inside the hinted provider a NEAR key (one side
|
|
11
|
+
// contains the other) still yields facts for versioned ids like
|
|
12
|
+
// `deepseek-v4-flash-0731`. Reasoning-effort values are intersected with the
|
|
13
|
+
// pi-ai THINKING_LEVELS space so writes can never fail schema validation.
|
|
13
14
|
|
|
14
15
|
export const THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max']
|
|
15
16
|
|
|
17
|
+
/** Family-prefix → catalog provider, mirroring upstream defaults. */
|
|
18
|
+
export const DEFAULT_PROVIDER_HINTS = Object.freeze({
|
|
19
|
+
glm: 'zai',
|
|
20
|
+
gpt: 'openai',
|
|
21
|
+
o: 'openai',
|
|
22
|
+
claude: 'anthropic',
|
|
23
|
+
deepseek: 'deepseek',
|
|
24
|
+
gemini: 'google',
|
|
25
|
+
grok: 'xai',
|
|
26
|
+
hunyuan: 'tencent',
|
|
27
|
+
qwen: 'alibaba',
|
|
28
|
+
kimi: 'moonshotai',
|
|
29
|
+
mimo: 'xiaomi',
|
|
30
|
+
minimax: 'minimax',
|
|
31
|
+
})
|
|
32
|
+
|
|
16
33
|
const nonEmpty = (value) => typeof value === 'string' && value.trim().length > 0
|
|
17
34
|
const clean = (value) => nonEmpty(value) ? value.trim() : undefined
|
|
18
35
|
const positiveInt = (value) => Number.isSafeInteger(value) && value > 0 ? value : undefined
|
|
19
36
|
|
|
20
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Parse a models.dev document into a detached map keyed by lower-cased model
|
|
39
|
+
* id. Tolerant of both the real flat-provider root and a `{providers}` wrap.
|
|
40
|
+
* @returns {Map<string, object[]>} entries with providerId/providerName/modelId/name/context/maxOutput/efforts.
|
|
41
|
+
*/
|
|
21
42
|
export function parseModelsDevCatalog(document) {
|
|
22
|
-
const
|
|
23
|
-
const
|
|
43
|
+
const root0 = document && typeof document === 'object' ? document : {}
|
|
44
|
+
const providersRoot = root0.providers && typeof root0.providers === 'object' && !Array.isArray(root0.providers)
|
|
45
|
+
? root0.providers
|
|
46
|
+
: root0
|
|
24
47
|
const byId = new Map()
|
|
25
|
-
for (const [providerId, provider] of Object.entries(
|
|
26
|
-
if (!provider || typeof provider !== 'object') continue
|
|
48
|
+
for (const [providerId, provider] of Object.entries(providersRoot)) {
|
|
49
|
+
if (!provider || typeof provider !== 'object' || Array.isArray(provider.models)) continue
|
|
27
50
|
const providerName = clean(provider.name) || clean(provider.id) || providerId
|
|
28
|
-
const models =
|
|
29
|
-
for (const model of models) {
|
|
51
|
+
const models = provider.models && typeof provider.models === 'object' ? provider.models : {}
|
|
52
|
+
for (const [key, model] of Object.entries(models)) {
|
|
30
53
|
if (!model || typeof model !== 'object') continue
|
|
31
|
-
const modelId = clean(model.id)
|
|
54
|
+
const modelId = clean(model.id) || clean(key)
|
|
32
55
|
if (!modelId) continue
|
|
33
56
|
const limit = model.limit && typeof model.limit === 'object' ? model.limit : {}
|
|
34
57
|
const context = positiveInt(limit.context)
|
|
35
58
|
const output = positiveInt(limit.output)
|
|
36
|
-
|
|
59
|
+
const efforts = []
|
|
37
60
|
const options = Array.isArray(model.reasoning_options) ? model.reasoning_options : []
|
|
38
61
|
for (const option of options) {
|
|
39
62
|
if (!option || option.type !== 'effort' || !Array.isArray(option.values)) continue
|
|
@@ -43,7 +66,6 @@ export function parseModelsDevCatalog(document) {
|
|
|
43
66
|
}
|
|
44
67
|
}
|
|
45
68
|
if (context === undefined && output === undefined && efforts.length === 0) continue
|
|
46
|
-
const key = modelId.toLowerCase()
|
|
47
69
|
const entry = {
|
|
48
70
|
providerId,
|
|
49
71
|
providerName,
|
|
@@ -53,49 +75,61 @@ export function parseModelsDevCatalog(document) {
|
|
|
53
75
|
...(output !== undefined ? { maxOutput: output } : {}),
|
|
54
76
|
...(efforts.length ? { efforts } : {}),
|
|
55
77
|
}
|
|
56
|
-
const list = byId.get(
|
|
78
|
+
const list = byId.get(modelId.toLowerCase())
|
|
57
79
|
if (list) list.push(entry)
|
|
58
|
-
else byId.set(
|
|
80
|
+
else byId.set(modelId.toLowerCase(), [entry])
|
|
59
81
|
}
|
|
60
82
|
}
|
|
61
83
|
return byId
|
|
62
84
|
}
|
|
63
85
|
|
|
64
|
-
/**
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return
|
|
86
|
+
/** Provider hinted for one gateway id by exact model rule or longest prefix. */
|
|
87
|
+
export function hintedProviderOf(id, bare, hints = {}) {
|
|
88
|
+
const exact = hints.models ? (hints.models[id] || hints.models[bare]) : undefined
|
|
89
|
+
if (exact) return exact
|
|
90
|
+
const lower = String(bare).toLowerCase()
|
|
91
|
+
const entries = Object.entries({ ...DEFAULT_PROVIDER_HINTS, ...(hints.defaults || {}) })
|
|
92
|
+
const hit = entries
|
|
93
|
+
.filter(([prefix]) => lower.startsWith(prefix.toLowerCase()))
|
|
94
|
+
.sort((a, b) => b[0].length - a[0].length)[0]
|
|
95
|
+
return hit ? hit[1] : undefined
|
|
74
96
|
}
|
|
75
97
|
|
|
76
98
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* @param {Map<string, object[]>} index - output
|
|
80
|
-
* @param {string[]} ids -
|
|
81
|
-
* @param {
|
|
99
|
+
* Match requested ids against the catalog. Keys tried are the full id and its
|
|
100
|
+
* last path segment; within the hinted provider a NEAR key also matches.
|
|
101
|
+
* @param {Map<string, object[]>} index - parseModelsDevCatalog output.
|
|
102
|
+
* @param {string[]} ids - gateway model ids.
|
|
103
|
+
* @param {{ models?: object, defaults?: object }} hints - optional provider hints.
|
|
82
104
|
* @returns {{ matched: object[], unmatched: string[] }}
|
|
83
105
|
*/
|
|
84
|
-
export function lookupModels(index, ids,
|
|
106
|
+
export function lookupModels(index, ids, hints = {}) {
|
|
85
107
|
const matched = []
|
|
86
108
|
const unmatched = []
|
|
87
109
|
for (const raw of ids || []) {
|
|
88
110
|
const modelId = clean(raw)
|
|
89
111
|
if (!modelId) continue
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
if (
|
|
112
|
+
const bare = modelId.slice(modelId.lastIndexOf('/') + 1)
|
|
113
|
+
const exact = index.get(modelId.toLowerCase()) || index.get(bare.toLowerCase())
|
|
114
|
+
const hinted = hintedProviderOf(modelId, bare, hints)
|
|
115
|
+
let selected
|
|
116
|
+
if (exact) {
|
|
117
|
+
const hintedEntry = exact.find((entry) => entry.providerId === hinted)
|
|
118
|
+
selected = hintedEntry || exact[0]
|
|
119
|
+
} else if (hinted) {
|
|
120
|
+
// Near match inside the hinted vendor only: versioned ids such as
|
|
121
|
+
// `deepseek-v4-flash-0731` resolve to the family's facts.
|
|
122
|
+
const candidates = [...index.values()].flat().filter((entry) => entry.providerId === hinted)
|
|
123
|
+
const near = candidates
|
|
124
|
+
.filter((entry) => entry.modelId.includes(bare) || bare.includes(entry.modelId))
|
|
125
|
+
.sort((a, b) => a.modelId.length - b.modelId.length)[0]
|
|
126
|
+
selected = near
|
|
127
|
+
}
|
|
128
|
+
if (!selected) {
|
|
95
129
|
unmatched.push(modelId)
|
|
96
130
|
continue
|
|
97
131
|
}
|
|
98
|
-
matched.push({ requested: modelId, ...
|
|
132
|
+
matched.push({ requested: modelId, ...selected, ...(selected.providerId === hinted ? { hinted: true } : {}) })
|
|
99
133
|
}
|
|
100
134
|
return { matched, unmatched }
|
|
101
135
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-model-params",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "EN: models.dev parameter assistant for the official DeepSeek Harness Models page: per-provider one-click context / max-output / reasoning-effort fill. ZH: 官方模型设置页的 models.dev 参数助手:按 provider 一键补全上下文、max 输出与推理档位。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|