@tzyc/dsh-model-visibility 0.0.0-stage → 1.9.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/LICENSE +21 -0
- package/README.md +367 -2
- package/client.js +2258 -0
- package/cordis.patch.yml +7 -0
- package/docs/capability-tags.png +0 -0
- package/docs/icon-180.png +0 -0
- package/docs/settings-page.png +0 -0
- package/icon.svg +9 -0
- package/index.js +476 -0
- package/lib/capability-catalog.js +137 -0
- package/lib/capability-corrections.js +65 -0
- package/lib/image-input-gate.js +113 -0
- package/lib/model-catalog.js +670 -0
- package/lib/model-info.js +786 -0
- package/lib/model-overrides.js +262 -0
- package/lib/provider-probe.js +596 -0
- package/lib/provider-write.js +175 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +64 -6
- package/screenshots.json +4 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write manual capabilities into the model provider's own settings.
|
|
3
|
+
*
|
|
4
|
+
* A manual override fixes what the page shows, but the composer, the adapter
|
|
5
|
+
* and every other reader still see the provider config. When the user edits a
|
|
6
|
+
* model here they usually mean "this is what the model really is", so this
|
|
7
|
+
* module mirrors the writable fields into the `llm-pi-ai` provider profile:
|
|
8
|
+
* `input`, `contextWindow` and `reasoningEfforts`.
|
|
9
|
+
*
|
|
10
|
+
* Only provider fields with an exact equivalent are written:
|
|
11
|
+
* - `kind` has no provider field (it is a display grouping);
|
|
12
|
+
* - `defaultEffort` is not per model in the provider schema (the route-level
|
|
13
|
+
* `reasoning` default applies to a whole provider), so it stays local;
|
|
14
|
+
* - an off-only ladder is rejected by the provider schema ("offers no level
|
|
15
|
+
* beyond off"), so it also stays local.
|
|
16
|
+
*
|
|
17
|
+
* The settings service exposes live, volatile provider configs; writing goes
|
|
18
|
+
* through `settings.mutate(ns, ops)` with explicit path ops so the existing
|
|
19
|
+
* `models` array entry is edited by index instead of being replaced wholesale
|
|
20
|
+
* (`modelOverrides` is illegal once a `models` list exists, so appending is not
|
|
21
|
+
* an option either).
|
|
22
|
+
*
|
|
23
|
+
* @module lib/provider-write
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Fields this module knows how to mirror into a provider profile. */
|
|
27
|
+
export const PROVIDER_WRITE_FIELDS = ['input', 'contextWindow', 'reasoningEfforts']
|
|
28
|
+
|
|
29
|
+
/** Plain-object guard. */
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Best-effort message from an unknown thrown value. */
|
|
35
|
+
function messageOf(error) {
|
|
36
|
+
if (error === null || error === undefined) return undefined
|
|
37
|
+
if (typeof error === 'string') return error
|
|
38
|
+
if (typeof error.message === 'string') return error.message
|
|
39
|
+
return String(error)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Convert a manual ladder into the provider `reasoningEfforts` shape.
|
|
44
|
+
*
|
|
45
|
+
* `off` maps to `null` ("supported, send nothing"), every other level maps to
|
|
46
|
+
* its own id as the wire value. Returns undefined when there is nothing worth
|
|
47
|
+
* writing: an unset ladder, or one that only offers `off` (provider config
|
|
48
|
+
* rejects that as "offers no level beyond off").
|
|
49
|
+
* @param override - sanitized override entry.
|
|
50
|
+
* @returns a provider ladder, `false` for "no reasoning", or undefined.
|
|
51
|
+
*/
|
|
52
|
+
export function effortsForProvider(override) {
|
|
53
|
+
if (override?.reasoningSupported === false) return false
|
|
54
|
+
if (!Array.isArray(override?.efforts) || override.efforts.length === 0) return undefined
|
|
55
|
+
const ladder = {}
|
|
56
|
+
let beyondOff = false
|
|
57
|
+
for (const effort of override.efforts) {
|
|
58
|
+
const id = typeof effort === 'string' ? effort : effort?.id
|
|
59
|
+
if (typeof id !== 'string' || id === '') continue
|
|
60
|
+
if (id === 'off') ladder.off = null
|
|
61
|
+
else {
|
|
62
|
+
ladder[id] = id
|
|
63
|
+
beyondOff = true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return beyondOff ? ladder : undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The provider fields an override implies.
|
|
71
|
+
* @param override - sanitized override entry.
|
|
72
|
+
* @returns `{input?, contextWindow?, reasoningEfforts?}`; empty when nothing is writable.
|
|
73
|
+
*/
|
|
74
|
+
export function providerPatch(override) {
|
|
75
|
+
const patch = {}
|
|
76
|
+
if (Array.isArray(override?.input) && override.input.length > 0) patch.input = [...override.input]
|
|
77
|
+
if (Number.isSafeInteger(override?.contextWindow) && override.contextWindow > 0) {
|
|
78
|
+
patch.contextWindow = override.contextWindow
|
|
79
|
+
}
|
|
80
|
+
const efforts = effortsForProvider(override)
|
|
81
|
+
if (efforts !== undefined) patch.reasoningEfforts = efforts
|
|
82
|
+
return patch
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Find the settings entry that owns one provider.
|
|
87
|
+
*
|
|
88
|
+
* Several settings namespaces can carry a `providers` dict (for example a test
|
|
89
|
+
* double or a future second adapter), so prefer the `llm-pi-ai` namespace and
|
|
90
|
+
* otherwise fall back to the first namespace that declares the provider.
|
|
91
|
+
* @param settings - the `settings` service.
|
|
92
|
+
* @param providerId - provider id from the override.
|
|
93
|
+
* @returns `{ns, models}` or undefined.
|
|
94
|
+
*/
|
|
95
|
+
export function findProviderEntry(settings, providerId) {
|
|
96
|
+
if (typeof settings?.describe !== 'function' || typeof providerId !== 'string') return undefined
|
|
97
|
+
const descriptors = settings.describe()
|
|
98
|
+
if (!Array.isArray(descriptors)) return undefined
|
|
99
|
+
const matches = []
|
|
100
|
+
for (const descriptor of descriptors) {
|
|
101
|
+
const providers = descriptor?.value?.providers
|
|
102
|
+
if (!isRecord(providers)) continue
|
|
103
|
+
const profile = providers[providerId]
|
|
104
|
+
if (!isRecord(profile)) continue
|
|
105
|
+
matches.push({
|
|
106
|
+
ns: descriptor.ns,
|
|
107
|
+
models: Array.isArray(profile.models) ? profile.models : undefined,
|
|
108
|
+
preferred: String(descriptor.ns ?? '').includes('pi-ai') ? 1 : 0,
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
if (matches.length === 0) return undefined
|
|
112
|
+
matches.sort((left, right) => right.preferred - left.preferred)
|
|
113
|
+
const best = matches[0]
|
|
114
|
+
return { ns: best.ns, models: best.models }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Mirror the writable parts of one override into the provider settings.
|
|
119
|
+
*
|
|
120
|
+
* Never throws: the caller keeps the local override when a write cannot happen,
|
|
121
|
+
* so a read-only profile or a probe-only install still behaves like before.
|
|
122
|
+
* @param settings - the `settings` service (needs `describe` and `mutate`).
|
|
123
|
+
* @param override - sanitized override entry.
|
|
124
|
+
* @returns `{written, fields, ns?, reason?, message?}`.
|
|
125
|
+
*/
|
|
126
|
+
export async function writeCapabilities(settings, override) {
|
|
127
|
+
const fields = Object.keys(providerPatch(override))
|
|
128
|
+
if (fields.length === 0) return { written: false, fields, reason: 'nothing' }
|
|
129
|
+
if (typeof settings?.mutate !== 'function' || typeof settings?.describe !== 'function') {
|
|
130
|
+
return { written: false, fields, reason: 'unsupported' }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let entry
|
|
134
|
+
try {
|
|
135
|
+
entry = findProviderEntry(settings, override.provider)
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return { written: false, fields, reason: 'describe-failed', message: messageOf(error) }
|
|
138
|
+
}
|
|
139
|
+
if (entry === undefined) return { written: false, fields, reason: 'provider-missing' }
|
|
140
|
+
if (entry.models === undefined) return { written: false, fields, reason: 'models-missing' }
|
|
141
|
+
|
|
142
|
+
const index = entry.models.findIndex((model) => isRecord(model) && model.id === override.model)
|
|
143
|
+
if (index < 0) return { written: false, fields, reason: 'model-missing' }
|
|
144
|
+
|
|
145
|
+
const patch = providerPatch(override)
|
|
146
|
+
const path = ['providers', override.provider, 'models', String(index)]
|
|
147
|
+
const ops = fields.map((field) => ({ op: 'set', path: [...path, field], value: patch[field] }))
|
|
148
|
+
try {
|
|
149
|
+
await settings.mutate(entry.ns, ops)
|
|
150
|
+
} catch (error) {
|
|
151
|
+
return { written: false, fields, reason: 'settings-failed', message: messageOf(error) }
|
|
152
|
+
}
|
|
153
|
+
return { written: true, fields, ns: entry.ns }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Remove the fields that have just been mirrored into the provider config.
|
|
158
|
+
* @param override - sanitized override entry that was written.
|
|
159
|
+
* @param fields - fields reported as written.
|
|
160
|
+
* @returns the remaining local override (may contain only provider/model).
|
|
161
|
+
*/
|
|
162
|
+
export function stripWrittenFields(override, fields) {
|
|
163
|
+
const remaining = { provider: override.provider, model: override.model }
|
|
164
|
+
if (!fields.includes('input') && override.input !== undefined) remaining.input = override.input
|
|
165
|
+
if (!fields.includes('contextWindow') && override.contextWindow !== undefined) {
|
|
166
|
+
remaining.contextWindow = override.contextWindow
|
|
167
|
+
}
|
|
168
|
+
if (!fields.includes('reasoningEfforts')) {
|
|
169
|
+
if (override.efforts !== undefined) remaining.efforts = override.efforts
|
|
170
|
+
if (override.defaultEffort !== undefined) remaining.defaultEffort = override.defaultEffort
|
|
171
|
+
if (override.reasoningSupported === false) remaining.reasoningSupported = false
|
|
172
|
+
}
|
|
173
|
+
if (override.kind !== undefined) remaining.kind = override.kind
|
|
174
|
+
return remaining
|
|
175
|
+
}
|
package/locale/en.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "Model Visibility",
|
|
4
|
+
"description": "Choose which models appear in the chat composer's model list, toggle or collapse whole provider groups, and read each model's real capabilities (adapter declarations, relay model lists, built-in catalog, vendor corrections) with colour-coded image, video and embedding labels, plus a hard re-fetch of every model's modalities and reasoning efforts, manual per-model overrides for kind, context window, input modalities and effort levels (optionally written back into the model provider's settings), and an optional image-input alignment for relay routes."
|
|
5
|
+
}
|
|
6
|
+
}
|
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tzyc/dsh-model-visibility",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@tzyc/dsh-model-visibility",
|
|
3
|
+
"version": "1.9.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"description": "Choose which models appear in the chat composer's model list, with per-provider groups, per-model capability tags sourced from the adapter, the relay model list, a built-in catalog, vendor corrections and manual overrides, separate coloured labels for image/video generation models, name search and capability filters, collapsible provider groups, a hard capability re-fetch, per-model overrides that can also be written back into the model provider's own settings, and an image-input alignment (on by default) for relay routes.",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./index.js",
|
|
11
|
+
"./client": "./client.js",
|
|
12
|
+
"./locale/*.json": "./locale/*.json",
|
|
13
|
+
"./icon": "./icon.svg"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"index.js",
|
|
17
|
+
"client.js",
|
|
18
|
+
"lib",
|
|
19
|
+
"cordis.patch.yml",
|
|
20
|
+
"locale/*.json",
|
|
21
|
+
"icon.svg",
|
|
22
|
+
"screenshots.json",
|
|
23
|
+
"docs"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "node --test test/*.test.mjs",
|
|
27
|
+
"catalog": "node scripts/build-catalog.mjs"
|
|
28
|
+
},
|
|
29
|
+
"dsh": {
|
|
30
|
+
"bundle": {
|
|
31
|
+
"patch": "./cordis.patch.yml"
|
|
32
|
+
},
|
|
33
|
+
"client": {
|
|
34
|
+
"platform": "web",
|
|
35
|
+
"immediately": true,
|
|
36
|
+
"inject": [
|
|
37
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
38
|
+
"@deepseek-ai/dsh-client-ui-model-selection",
|
|
39
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
40
|
+
"@deepseek-ai/dsh-client-locale"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@deepseek-ai/schemastery": "^3.18.5"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/TZYCeng/dsh-model-visibility.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://github.com/TZYCeng/dsh-model-visibility#readme",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/TZYCeng/dsh-model-visibility/issues"
|
|
54
|
+
},
|
|
55
|
+
"keywords": [
|
|
56
|
+
"dsh-plugin",
|
|
57
|
+
"deepseek-harness",
|
|
58
|
+
"dsh",
|
|
59
|
+
"model-visibility",
|
|
60
|
+
"model-selector"
|
|
61
|
+
],
|
|
62
|
+
"author": "TZYCeng",
|
|
63
|
+
"license": "MIT"
|
|
64
|
+
}
|
package/screenshots.json
ADDED