dsh-model-groups 0.3.9 → 0.3.10
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 +25 -0
- package/README.md +10 -2
- package/docs/capabilities-beta.md +34 -0
- package/docs/maintenance.md +6 -0
- package/lib/client.js +1 -1
- package/native/pi-ai.patch.json +1336 -53
- package/native/settings-models.patch.json +613 -1
- package/package.json +2 -1
- package/scripts/settings-capabilities.mjs +37 -0
- package/src/capability-defaults.js +93 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-model-groups",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "在 DSH 网页端按供应商和密钥配置选择模型,并为同一供应商的不同密钥分别设置模型和 API 协议。支持 DSH 0.2.2-rc.1。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"README.md",
|
|
41
41
|
"CHANGELOG.md",
|
|
42
42
|
"docs/maintenance.md",
|
|
43
|
+
"docs/capabilities-beta.md",
|
|
43
44
|
"docs/images/*.webp",
|
|
44
45
|
"LICENSE"
|
|
45
46
|
],
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { applyModelCapabilities, validateCapabilityBatch, applyModelBatchCapabilities, materializeCapabilityEntry, applyCapabilityBatch, mergeCapabilityModels } from '../src/capability-defaults.js';
|
|
2
|
+
|
|
3
|
+
// Embedded into the verified host bundle. These functions modify drafts only.
|
|
4
|
+
export function GroupCapabilityBatch({ entry, onChange, disabled, t }) {
|
|
5
|
+
const migrated = materializeCapabilityEntry(entry);
|
|
6
|
+
const jsx = react_jsx_runtime.jsx, jsxs = react_jsx_runtime.jsxs;
|
|
7
|
+
const css = ModelsSection_module_css_default;
|
|
8
|
+
return jsxs('div', { 'data-group-capabilities': true, className: css.field, children: ['vision', 'reasoning'].map(key => jsxs('div', {
|
|
9
|
+
className: css.modelReasoningHead,
|
|
10
|
+
children: [jsx('span', { className: css.fieldLabel, children: t(key === 'vision' ? 'capabilityVision' : 'capabilityReasoning') }), jsx(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
11
|
+
'data-capability-batch': key, label: t(key === 'vision' ? 'capabilityVision' : 'capabilityReasoning'), checked: migrated.capabilityBatch?.[key] === true, disabled,
|
|
12
|
+
onChange: enabled => { if (!disabled && typeof enabled === 'boolean') onChange(applyCapabilityBatch(entry, key, enabled)); }
|
|
13
|
+
})]
|
|
14
|
+
}, key)) });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function addSettingsCapabilities(initial, apply) {
|
|
18
|
+
let source = initial;
|
|
19
|
+
const replace = (before, after) => { if (source.split(before).length !== 2) throw new Error('Expected unique capability batch anchor: ' + before.slice(0, 120)); apply(before, after); source = source.replace(before, after); };
|
|
20
|
+
const helpers = [applyModelCapabilities, validateCapabilityBatch, applyModelBatchCapabilities, materializeCapabilityEntry, applyCapabilityBatch, mergeCapabilityModels, GroupCapabilityBatch].map(fn => fn.toString()).join('\n');
|
|
21
|
+
replace('const MULTI_KEY_PROTOCOLS =', helpers + '\n\t\tconst MULTI_KEY_PROTOCOLS =');
|
|
22
|
+
replace('JSON.stringify(entry) !== JSON.stringify(saved)', 'JSON.stringify(materializeCapabilityEntry(entry)) !== JSON.stringify(materializeCapabilityEntry(saved))');
|
|
23
|
+
// Normalization materializes old beta policies when the existing save is used.
|
|
24
|
+
// Rendering retains raw entries for unchanged dirty/saved snapshot comparison.
|
|
25
|
+
replace('return Object.fromEntries(orderedKeyEntries(entries).map(([id, entry]) => {', 'return Object.fromEntries(orderedKeyEntries(entries).map(([id, rawEntry]) => {\n\t\t\t\tconst entry = materializeCapabilityEntry(rawEntry);');
|
|
26
|
+
const groupModel = source.indexOf('models: modelDrafts(entry.models), onChange: (models) => update(id, { models }),');
|
|
27
|
+
const groupStart = source.lastIndexOf('(0, react_jsx_runtime.jsx)(ModelListEditor, {', groupModel);
|
|
28
|
+
if (groupModel < 0 || groupStart < 0) throw new Error('Missing group model editor batch anchor');
|
|
29
|
+
const groupBefore = source.slice(groupStart, groupModel);
|
|
30
|
+
replace(groupBefore + 'models: modelDrafts(entry.models), onChange: (models) => update(id, { models }),',
|
|
31
|
+
'(0, react_jsx_runtime.jsx)(GroupCapabilityBatch, { entry, t, disabled: disabled || anyBusy, onChange: (next) => { if (!disabled && !anyBusy) onChange({ ...entries, [id]: next }); } }), ' + groupBefore + 'models: modelDrafts(materializeCapabilityEntry(entry).models), onChange: (models) => { if (!disabled && !anyBusy) onChange({ ...entries, [id]: mergeCapabilityModels(entry, models) }); },');
|
|
32
|
+
for (const [locale, labels] of Object.entries({ en: { capabilityVision: 'Image input', capabilityReasoning: 'Reasoning effort' }, zh: { capabilityVision: '识图', capabilityReasoning: '思考强度' } })) {
|
|
33
|
+
const text = Object.entries(labels).map(([key, value]) => `\t\t\t${key}: ${JSON.stringify(value)},\n`).join('');
|
|
34
|
+
replace(`const ${locale} = {\n`, `const ${locale} = {\n${text}`);
|
|
35
|
+
}
|
|
36
|
+
return source;
|
|
37
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Resolve group capability policy without mutating the saved upstream descriptor.
|
|
2
|
+
* Self-contained so the verified host generators can serialize this function.
|
|
3
|
+
*/
|
|
4
|
+
export function applyModelCapabilities(model, defaults) {
|
|
5
|
+
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
6
|
+
const overrides = model.capabilityOverrides;
|
|
7
|
+
if (defaults !== undefined && (!object(defaults) || Object.keys(defaults).some(key => key !== 'vision' && key !== 'reasoning') || typeof defaults.vision !== 'boolean' || typeof defaults.reasoning !== 'boolean')) {
|
|
8
|
+
throw new TypeError('capabilityDefaults must contain only boolean vision and reasoning');
|
|
9
|
+
}
|
|
10
|
+
if (overrides !== undefined && (!object(overrides) || Object.keys(overrides).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(overrides).some(value => typeof value !== 'boolean' && value !== 'original'))) {
|
|
11
|
+
throw new TypeError('capabilityOverrides must contain only boolean or original vision and reasoning');
|
|
12
|
+
}
|
|
13
|
+
const { capabilityOverrides: ignored, ...effective } = model;
|
|
14
|
+
if (defaults === undefined) return effective;
|
|
15
|
+
const choice = key => overrides?.[key] === 'original' ? undefined : overrides?.[key] ?? defaults[key];
|
|
16
|
+
const vision = choice('vision');
|
|
17
|
+
if (vision !== undefined) {
|
|
18
|
+
const input = Array.isArray(model.input) ? [...model.input] : ['text'];
|
|
19
|
+
effective.input = [...new Set(input.filter(value => vision || value !== 'image'))];
|
|
20
|
+
if (effective.input.length === 0) effective.input.push('text');
|
|
21
|
+
if (vision && !effective.input.includes('image')) effective.input.push('image');
|
|
22
|
+
}
|
|
23
|
+
const reasoning = choice('reasoning');
|
|
24
|
+
if (reasoning === false) effective.reasoningEfforts = false;
|
|
25
|
+
if (reasoning === true) {
|
|
26
|
+
const levels = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
27
|
+
const efforts = model.reasoningEfforts;
|
|
28
|
+
const valid = object(efforts) && Object.keys(efforts).every(key => levels.includes(key)) && Object.entries(efforts).every(([key, value]) => key === 'off' && value === null || typeof value === 'string' && value.length > 0) && Object.keys(efforts).some(key => key !== 'off');
|
|
29
|
+
effective.reasoningEfforts = valid ? { ...efforts } : { off: null, low: 'low', medium: 'medium', high: 'high' };
|
|
30
|
+
}
|
|
31
|
+
return effective;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function validateCapabilityDefaults(defaults) {
|
|
35
|
+
applyModelCapabilities({}, defaults);
|
|
36
|
+
return defaults;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function validateCapabilityOverrides(overrides) {
|
|
40
|
+
applyModelCapabilities({ capabilityOverrides: overrides });
|
|
41
|
+
return overrides;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Last batch selections affect newly introduced models, never runtime overrides. */
|
|
45
|
+
export function validateCapabilityBatch(batch) {
|
|
46
|
+
if (batch !== undefined && (batch === null || typeof batch !== 'object' || Array.isArray(batch) || Object.keys(batch).some(key => key !== 'vision' && key !== 'reasoning') || Object.values(batch).some(value => typeof value !== 'boolean'))) {
|
|
47
|
+
throw new TypeError('capabilityBatch must contain only boolean vision and reasoning');
|
|
48
|
+
}
|
|
49
|
+
return batch;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function applyModelBatchCapabilities(model, batch) {
|
|
53
|
+
validateCapabilityBatch(batch);
|
|
54
|
+
const original = applyModelCapabilities(model);
|
|
55
|
+
if (batch === undefined || Object.keys(batch).length === 0) return original;
|
|
56
|
+
return applyModelCapabilities({ ...original, capabilityOverrides: {
|
|
57
|
+
...(batch.vision === undefined ? { vision: 'original' } : {}),
|
|
58
|
+
...(batch.reasoning === undefined ? { reasoning: 'original' } : {}),
|
|
59
|
+
} }, { vision: batch.vision ?? false, reasoning: batch.reasoning ?? false });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Upgrade a beta.1 entry in the draft, preserving its actual capabilities. */
|
|
63
|
+
export function materializeCapabilityEntry(entry) {
|
|
64
|
+
validateCapabilityBatch(entry.capabilityBatch);
|
|
65
|
+
const { capabilityDefaults, ...rest } = entry;
|
|
66
|
+
applyModelCapabilities({}, capabilityDefaults);
|
|
67
|
+
const models = (Array.isArray(entry.models) ? entry.models : []).map(model => applyModelCapabilities(model, capabilityDefaults));
|
|
68
|
+
const capabilityBatch = capabilityDefaults === undefined ? entry.capabilityBatch : { ...capabilityDefaults, ...entry.capabilityBatch };
|
|
69
|
+
return { ...rest, models, ...(capabilityBatch === undefined ? {} : { capabilityBatch }) };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function applyCapabilityBatch(entry, key, enabled) {
|
|
73
|
+
if (!['vision', 'reasoning'].includes(key) || typeof enabled !== 'boolean') throw new TypeError('capability batch needs vision/reasoning and a boolean');
|
|
74
|
+
const migrated = materializeCapabilityEntry(entry);
|
|
75
|
+
return { ...migrated, capabilityBatch: { ...migrated.capabilityBatch, [key]: enabled }, models: migrated.models.map(model => applyModelBatchCapabilities(model, { [key]: enabled })) };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Existing incoming rows are edits; only genuinely new IDs inherit batch values. */
|
|
79
|
+
export function mergeCapabilityModels(entry, incoming) {
|
|
80
|
+
const migrated = materializeCapabilityEntry(entry);
|
|
81
|
+
const rows = new Map();
|
|
82
|
+
for (const model of migrated.models) {
|
|
83
|
+
if (!rows.has(model.id)) rows.set(model.id, []);
|
|
84
|
+
rows.get(model.id).push(model);
|
|
85
|
+
}
|
|
86
|
+
const grows = incoming.length > migrated.models.length;
|
|
87
|
+
return { ...migrated, models: incoming.map(model => {
|
|
88
|
+
const previous = rows.get(model.id)?.shift();
|
|
89
|
+
const nativeOff = migrated.capabilityBatch?.reasoning !== undefined && previous?.reasoningEfforts !== null && typeof previous?.reasoningEfforts === 'object' && !Array.isArray(previous.reasoningEfforts) && model.reasoningEfforts === undefined;
|
|
90
|
+
const edited = nativeOff ? { ...model, reasoningEfforts: false } : model;
|
|
91
|
+
return !grows || previous !== undefined ? applyModelCapabilities(edited) : applyModelBatchCapabilities(edited, migrated.capabilityBatch);
|
|
92
|
+
}) };
|
|
93
|
+
}
|