dsh-auto-model-routing 0.4.2 → 0.5.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/CHANGELOG.md +10 -0
- package/README.md +8 -8
- package/README.zh.md +8 -8
- package/docs/feedback-evaluation.md +4 -4
- package/docs/host-capability-report.md +2 -0
- package/docs/jev-policy-v2-validation.md +3 -1
- package/docs/magpie-live-acceptance.md +2 -0
- package/docs/policy-v2-development.md +5 -3
- package/docs/policy-v2-real-evaluation-plan.md +2 -0
- package/lib/client/control.d.ts +2 -2
- package/lib/client/control.js +10 -20
- package/lib/client/index.d.ts +0 -1
- package/lib/client/index.js +9 -6
- package/lib/client/label.d.ts +7 -10
- package/lib/client/label.js +17 -30
- package/lib/client/settings-section.d.ts +2 -2
- package/lib/client/settings-section.js +7 -69
- package/lib/client/strategy-details.js +3 -4
- package/lib/client.js +165 -481
- package/lib/context.d.ts +1 -3
- package/lib/context.js +3 -4
- package/lib/effort.d.ts +2 -10
- package/lib/effort.js +1 -31
- package/lib/endpoints.d.ts +11 -10
- package/lib/endpoints.js +29 -46
- package/lib/feedback.d.ts +60 -7
- package/lib/feedback.js +15 -5
- package/lib/index.d.ts +4 -0
- package/lib/index.js +17 -86
- package/lib/jev.d.ts +17 -29
- package/lib/jev.js +6 -34
- package/lib/policy-host.d.ts +6 -8
- package/lib/policy-host.js +39 -38
- package/lib/policy-store.d.ts +9 -20
- package/lib/policy-store.js +28 -16
- package/lib/routing.d.ts +20 -0
- package/lib/routing.js +51 -0
- package/lib/rubric.json +15 -6
- package/lib/settings-contract.d.ts +5 -21
- package/lib/settings-contract.js +3 -2
- package/lib/settings-host.d.ts +4 -8
- package/lib/settings-host.js +9 -10
- package/lib/settings-validation.d.ts +1 -7
- package/lib/settings-validation.js +0 -51
- package/lib/settings.d.ts +3 -8
- package/lib/settings.js +18 -177
- package/lib/state.d.ts +2 -4
- package/lib/state.js +2 -7
- package/lib/strategies/claude.d.ts +21 -0
- package/lib/strategies/claude.js +8 -0
- package/lib/strategies/codex.d.ts +21 -0
- package/lib/strategies/codex.js +9 -0
- package/lib/strategies/index.d.ts +22 -0
- package/lib/strategies/index.js +85 -0
- package/lib/strategies/types.d.ts +14 -0
- package/lib/strategies/types.js +1 -0
- package/lib/subagent-tool.d.ts +7 -7
- package/lib/subagent-tool.js +33 -76
- package/lib/support.d.ts +7 -8
- package/lib/support.js +9 -13
- package/lib/usage-report.d.ts +2 -2
- package/lib/usage-report.js +25 -17
- package/package.json +3 -4
- package/skills/auto-model-routing-report/SKILL.md +4 -3
- package/skills/subagent-model-routing/SKILL.md +14 -30
- package/lib/client/route-picker.d.ts +0 -21
- package/lib/client/route-picker.js +0 -75
- package/lib/judgements.d.ts +0 -41
- package/lib/judgements.js +0 -55
- package/lib/policy-v2-rubric.json +0 -21
- package/lib/policy-v2.d.ts +0 -21
- package/lib/policy-v2.js +0 -54
- package/lib/router.d.ts +0 -68
- package/lib/router.js +0 -153
- package/scripts/label-report.mjs +0 -50
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { createElement as h, useEffect, useState } from 'react';
|
|
2
|
-
import { AUTO_EFFORTS
|
|
3
|
-
import { selectableModels, supportedFamilyEfforts, validateChildRoute, validateFamilyMainEffort } from '../settings-validation.js';
|
|
4
|
-
import { CHILD_MODELS, mapEffort, tiers } from '../effort.js';
|
|
5
|
-
import { RoutePicker } from './route-picker.js';
|
|
2
|
+
import { AUTO_EFFORTS } from '../settings-contract.js';
|
|
6
3
|
import { DECISION_SERVICES, resolveDecisionService } from '../decision-service.js';
|
|
7
4
|
import { CATEGORY_HINTS } from '../hints.js';
|
|
8
5
|
import { StrategyDetails } from './strategy-details.js';
|
|
9
6
|
const CSS = `
|
|
10
7
|
.amr-settings{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-primary);min-width:0;container-type:inline-size}.amr-settings-section{padding:16px 0;border-bottom:.5px solid var(--dsw-alias-border-l2)}.amr-settings h3{font-size:13px;font-weight:600;margin:0 0 12px}.amr-settings p{margin:6px 0;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere}.amr-settings-row{display:flex;align-items:center;justify-content:space-between;gap:16px;margin:14px 0}.amr-settings-row>div:first-child{min-width:0}.amr-settings-number{width:76px;text-align:right;font:inherit;font-variant-numeric:tabular-nums;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);color:inherit;background:var(--dsw-alias-bg-layer-1);padding:6px}.amr-settings-number:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary)}
|
|
11
|
-
.amr-settings-
|
|
8
|
+
.amr-settings-info,.amr-settings-reset{border:0;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;font:inherit;padding:2px}.amr-settings-info{display:inline-flex}.amr-settings-reset{font-size:12px;margin-top:4px}.amr-settings-reset:hover{color:var(--dsw-alias-label-primary)}.amr-settings-info:focus-visible,.amr-settings-reset:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary)}.amr-settings .amr-settings-error{color:var(--dsw-alias-state-error-primary)}.amr-settings .amr-settings-segments{display:inline-grid;flex:none;width:max-content;max-width:100%;height:28px;box-sizing:border-box;gap:2px;padding:2px;border-radius:var(--dsw-radius-md)}.amr-settings .amr-settings-segments>button{height:24px;min-width:44px;padding:0 8px;border-radius:var(--dsw-radius-sm);font-size:12px;line-height:20px}.amr-settings .amr-settings-segments>[aria-hidden=true]{top:2px;bottom:2px;left:2px;height:calc(100% - 4px);width:calc((100% - 4px - 2px * (var(--dsh-segment-count) - 1)) / var(--dsh-segment-count))}
|
|
12
9
|
.amr-test-row{display:flex;align-items:center;flex-wrap:wrap;gap:10px;margin:12px 0 6px}.amr-test-row>span{font-size:12px;color:var(--dsw-alias-label-secondary)}.amr-test-button{height:28px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;cursor:pointer}.amr-test-button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.amr-test-button:disabled{opacity:.5;cursor:not-allowed}.amr-test-button:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
|
|
13
10
|
.amr-service-trigger,.amr-service-input{box-sizing:border-box;min-width:0;width:240px;max-width:100%;height:32px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit}.amr-service-trigger{display:flex;align-items:center;justify-content:space-between;gap:12px;cursor:pointer;text-align:left}.amr-service-trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.amr-service-trigger svg{color:var(--dsw-alias-label-tertiary);flex:none}.amr-service-trigger:focus-visible,.amr-service-input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}
|
|
14
|
-
@container(max-width:480px){.amr-settings-
|
|
11
|
+
@container(max-width:480px){.amr-settings-row{flex-wrap:wrap}}
|
|
15
12
|
`;
|
|
16
13
|
async function request(rpc, action, payload = {}) {
|
|
17
14
|
const response = await rpc.call('/api', `auto-model-routing.${action}`, payload);
|
|
@@ -22,13 +19,10 @@ async function request(rpc, action, payload = {}) {
|
|
|
22
19
|
throw Object.assign(new Error(action === 'setKey' ? 'API key 未保存' : '设置未保存,请检查字段错误并重试'), { issues: value.issues });
|
|
23
20
|
return value;
|
|
24
21
|
}
|
|
25
|
-
const has = (object, key) => !!object && Object.hasOwn(object, key);
|
|
26
22
|
/** Settings drafts are independent from the official session model selection. */
|
|
27
23
|
export function RoutingSettings({ rpc, ui }) {
|
|
28
24
|
const [view, setView] = useState();
|
|
29
25
|
const [patch, setPatch] = useState({});
|
|
30
|
-
const [childDrafts, setChildDrafts] = useState({});
|
|
31
|
-
const [provider, setProvider] = useState('claude');
|
|
32
26
|
const [serviceMenu, setServiceMenu] = useState(false);
|
|
33
27
|
const [modelMenu, setModelMenu] = useState(false);
|
|
34
28
|
const [key, setKey] = useState('');
|
|
@@ -63,60 +57,10 @@ export function RoutingSettings({ rpc, ui }) {
|
|
|
63
57
|
catch {
|
|
64
58
|
invalidService = true;
|
|
65
59
|
}
|
|
66
|
-
const invalidFamily = (p) => {
|
|
67
|
-
const family = view.settings.mainEfforts?.[p];
|
|
68
|
-
return family !== undefined && (!family || typeof family !== 'object' || Array.isArray(family) || Object.keys(family).some(key => !tiers.includes(key)));
|
|
69
|
-
};
|
|
70
|
-
const familyUnresolved = (p) => invalidFamily(p) && !has(patch.mainEfforts, p);
|
|
71
|
-
const mainValue = (p, tier) => {
|
|
72
|
-
const cells = patch.mainEfforts?.[p];
|
|
73
|
-
if (has(patch.mainEfforts, p) && (cells === null || invalidFamily(p)))
|
|
74
|
-
return cells?.[tier] ?? undefined;
|
|
75
|
-
return has(cells ?? undefined, tier) ? cells[tier] ?? undefined : view.settings.mainEfforts?.[p]?.[tier];
|
|
76
|
-
};
|
|
77
|
-
const childValue = (p, tier) => {
|
|
78
|
-
if (childDrafts[`${p}.${tier}`])
|
|
79
|
-
return childDrafts[`${p}.${tier}`];
|
|
80
|
-
const cells = patch.childRoutes?.[p];
|
|
81
|
-
const explicit = has(cells, tier) ? cells[tier] ?? undefined : view.settings.childRoutes?.[p]?.[tier];
|
|
82
|
-
if (explicit)
|
|
83
|
-
return {
|
|
84
|
-
model: typeof explicit.model === 'string' ? explicit.model : '无效配置',
|
|
85
|
-
reasoningEffort: typeof explicit.reasoningEffort === 'string' ? explicit.reasoningEffort : undefined,
|
|
86
|
-
};
|
|
87
|
-
const model = CHILD_MODELS[p][tiers.indexOf(tier)];
|
|
88
|
-
const info = view.models.find(m => m.provider === p && m.model === model);
|
|
89
|
-
return { model, reasoningEffort: info ? mapEffort(model, tier, info.reasoningEfforts, cap) : undefined };
|
|
90
|
-
};
|
|
91
|
-
const mainExplicit = (p, tier) => mainValue(p, tier) !== undefined;
|
|
92
|
-
const childExplicit = (p, tier) => !!childDrafts[`${p}.${tier}`] || (has(patch.childRoutes?.[p], tier) ? patch.childRoutes[p][tier] !== null : !!view.settings.childRoutes?.[p]?.[tier]);
|
|
93
|
-
const changed = (field) => Object.hasOwn(patch, field);
|
|
94
|
-
const capChanged = changed('effortCap') && cap !== view.settings.effortCap;
|
|
95
60
|
const conflicts = [];
|
|
96
|
-
|
|
97
|
-
for (const tier of tiers) {
|
|
98
|
-
const value = mainValue(p, tier);
|
|
99
|
-
if (value !== undefined && (capChanged || has(patch.mainEfforts?.[p] ?? undefined, tier)))
|
|
100
|
-
conflicts.push(...validateFamilyMainEffort(view, p, value, cap, `mainEfforts.${p}.${tier}`));
|
|
101
|
-
}
|
|
102
|
-
if (capChanged && familyUnresolved(p))
|
|
103
|
-
conflicts.push({ path: `mainEfforts.${p}`, code: 'invalid', message: '主会话映射格式无效,请恢复该体系默认值或重新配置' });
|
|
104
|
-
for (const tier of tiers)
|
|
105
|
-
if (childExplicit(p, tier) && (capChanged || has(patch.childRoutes?.[p], tier) || !!childDrafts[`${p}.${tier}`])) {
|
|
106
|
-
conflicts.push(...validateChildRoute(view, p, childValue(p, tier), cap, `childRoutes.${p}.${tier}`));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const invalidTimeout = !Number.isInteger(settings.jevTimeoutMs) || settings.jevTimeoutMs < 1000 || settings.jevTimeoutMs > 4500;
|
|
61
|
+
const invalidTimeout = !Number.isInteger(settings.jevTimeoutMs) || settings.jevTimeoutMs < 1000 || settings.jevTimeoutMs > 8000;
|
|
110
62
|
const invalidKey = key.length > 512 || /\s/.test(key);
|
|
111
63
|
const dirty = Object.keys(patch).length > 0 || key.length > 0 || removeKey;
|
|
112
|
-
const setMain = (tier, effort) => setPatch(old => ({ ...old, mainEfforts: { ...old.mainEfforts, [provider]: { ...(old.mainEfforts?.[provider] === null ? Object.fromEntries(tiers.map(tier => [tier, null])) : old.mainEfforts?.[provider]), [tier]: effort } } }));
|
|
113
|
-
const setChild = (tier, route) => {
|
|
114
|
-
setChildDrafts(old => { const next = { ...old }; if (route)
|
|
115
|
-
next[`${provider}.${tier}`] = route;
|
|
116
|
-
else
|
|
117
|
-
delete next[`${provider}.${tier}`]; return next; });
|
|
118
|
-
setPatch(old => ({ ...old, childRoutes: { ...old.childRoutes, [provider]: { ...old.childRoutes?.[provider], [tier]: route === null ? null : { model: route.model, reasoningEffort: route.reasoningEffort } } } }));
|
|
119
|
-
};
|
|
120
64
|
const save = async () => {
|
|
121
65
|
if (saving)
|
|
122
66
|
return;
|
|
@@ -143,7 +87,6 @@ export function RoutingSettings({ rpc, ui }) {
|
|
|
143
87
|
try {
|
|
144
88
|
nextView = await request(rpc, 'saveSettings', patch);
|
|
145
89
|
setPatch({});
|
|
146
|
-
setChildDrafts({});
|
|
147
90
|
results.push('设置已保存');
|
|
148
91
|
}
|
|
149
92
|
catch (error) {
|
|
@@ -156,14 +99,12 @@ export function RoutingSettings({ rpc, ui }) {
|
|
|
156
99
|
setMessage(results.join(';'));
|
|
157
100
|
setSaving(false);
|
|
158
101
|
};
|
|
159
|
-
const issueText = (path) => [...conflicts, ...saveIssues, ...view.issues].filter(issue => issue.path === path).map(issue => issue.message).join(';');
|
|
160
|
-
const cell = (label, control, overridden, reset, issue) => h('div', { className: 'amr-settings-cell' }, h('span', { className: 'amr-settings-cell-label' }, label), control, overridden && h('button', { className: 'amr-settings-reset', type: 'button', disabled: saving, onClick: reset, 'aria-label': `${label}恢复默认` }, '恢复默认'), issue && h('p', { className: 'amr-settings-error', role: 'status' }, issue));
|
|
161
102
|
const keyState = !view.key.available ? '不可用' : !view.key.writable ? '来自启动环境' : view.key.configured ? '已配置' : '未配置';
|
|
162
|
-
const keyHint = !view.key.available ? 'DSH 凭据服务不可用。' : !view.key.writable ? '先自行保留现有 key,从启动环境(如 .env.local)删除 OPENROUTER_API_KEY 并重启,再在这里保存。' : view.key.configured ? '保存在 DSH 凭据存储,不写入设置文件;留空表示保持当前 key。' : '未配置时 AUTO
|
|
103
|
+
const keyHint = !view.key.available ? 'DSH 凭据服务不可用。' : !view.key.writable ? '先自行保留现有 key,从启动环境(如 .env.local)删除 OPENROUTER_API_KEY 并重启,再在这里保存。' : view.key.configured ? '保存在 DSH 凭据存储,不写入设置文件;留空表示保持当前 key。' : '未配置时 AUTO 判断会停用;保存 key 后不会自动重启已停用的会话。';
|
|
163
104
|
return h('div', { className: 'amr-settings' }, h('style', null, CSS), h(ui.SettingsForm, {
|
|
164
105
|
labels: { unavailable: '无法读取设置', readOnly: '设置只读', saveFailed: '部分更改未保存,请检查提示', save: '保存', saving: '保存中…' },
|
|
165
106
|
state: { available: true, writable: true, dirty, invalid: conflicts.length > 0 || invalidTimeout || invalidKey || invalidService, saving, failed }, onSave: () => { void save(); }, onDiscard: () => { },
|
|
166
|
-
children: h('fieldset', { disabled: saving || testing, style: { border: 0, padding: 0, margin: 0, minWidth: 0 } }, view.configStatus === 'invalid' && h('p', { role: 'status' }, '设置文件无法解析或含无效字段,已按可解析值与默认值显示;保存会覆盖损坏部分'), message && h('p', { role: 'status', className: failed ? 'amr-settings-error' : undefined }, message), h('section', { className: 'amr-settings-section' }, h('h3', null, '
|
|
107
|
+
children: h('fieldset', { disabled: saving || testing, style: { border: 0, padding: 0, margin: 0, minWidth: 0 } }, view.configStatus === 'invalid' && h('p', { role: 'status' }, '设置文件无法解析或含无效字段,已按可解析值与默认值显示;保存会覆盖损坏部分'), view.deprecated?.length ? h('p', { role: 'status', 'data-deprecated': 'true' }, `设置文件中的旧映射(${view.deprecated.join('、')})已不再生效,下次保存设置时移除。`) : null, message && h('p', { role: 'status', className: failed ? 'amr-settings-error' : undefined }, message), h('section', { className: 'amr-settings-section' }, h('h3', null, '决策服务'), h('p', null, 'AUTO 把脱敏任务发送给所选决策服务;不跨服务自动回退。'), h('div', { className: 'amr-settings-row' }, h('span', null, '决策服务'), h(ui.Menu, { open: serviceMenu, portal: true, side: 'bottom', selectedId: serviceProvider, onClose: () => setServiceMenu(false),
|
|
167
108
|
items: Object.entries(DECISION_SERVICES).map(([id, service]) => ({ id, label: service.label })),
|
|
168
109
|
onSelect: id => { if (!Object.hasOwn(DECISION_SERVICES, id))
|
|
169
110
|
return; setKey(''); setRemoveKey(false); setPatch(old => ({ ...old, decisionService: { provider: id } })); setServiceMenu(false); },
|
|
@@ -191,9 +132,6 @@ export function RoutingSettings({ rpc, ui }) {
|
|
|
191
132
|
finally {
|
|
192
133
|
setTesting(false);
|
|
193
134
|
}
|
|
194
|
-
} }, testing ? '测试中…' : '测试连接'), h('span', { role: 'status', 'aria-live': 'polite', className: testResult?.failed ? 'amr-settings-error' : undefined }, testing ? '正在请求所选决策服务…' : testResult?.text ?? (dirty ? '请先保存设置,再测试连接' : ''))), h('p', null, '测试会产生一次决策请求,不包含会话内容;请先保存设置。'), invalidService && h('p', { role: 'alert', className: 'amr-settings-error' }, '请填写有效模型和兼容端点'), h(ui.SettingsSecretField, { id: 'amr-key', label: `${DECISION_SERVICES[serviceProvider].label} API Key`, hint: changingService ? '请先保存服务选择' : keyHint, text: key, disabled: changingService || saving || !view.key.available || !view.key.writable, onEdit: text => { setKey(text); setRemoveKey(false); }, configured: !changingService && view.key.configured, stateLabel: changingService ? '保存后读取' : keyState }), !changingService && view.key.available && view.key.writable && view.key.configured && h('button', { type: 'button', className: 'amr-settings-reset', onClick: () => { setRemoveKey(!removeKey); setKey(''); } }, removeKey ? '取消删除 key' : '删除 key'), invalidKey && h('p', { className: 'amr-settings-error' }, 'API key 不能含空白,且不能超过 512 字符'), h('div', { className: 'amr-settings-row' }, h('div', null, h('label', { htmlFor: 'amr-timeout' }, '
|
|
195
|
-
const effort = mainValue(provider, tier);
|
|
196
|
-
return h('div', { key: tier, className: 'amr-settings-tier', 'data-tier': tier }, h('div', { className: 'amr-settings-tier-name' }, tier, h(ui.Tooltip, { label: view.criteria[tier], side: 'top', portal: true, children: h('button', { type: 'button', className: 'amr-settings-info', 'aria-label': `${tier} 判档标准`, onFocus: () => { } }, h(ui.IconInfoOutlineRegular, { size: 14 })) })), cell('主会话推理等级', h(RoutePicker, { ui, catalog: view, provider, cap, mode: 'effort', label: `${tier} 主会话推理等级`, value: { model: '', reasoningEffort: effort === undefined ? undefined : typeof effort === 'string' ? effort : '无效配置' }, invalid: familyUnresolved(provider), disabled: saving, onChange: value => setMain(tier, value.reasoningEffort) }), mainExplicit(provider, tier), () => setMain(tier, null), issueText(`mainEfforts.${provider}.${tier}`)), cell('子会话推荐', h(RoutePicker, { ui, catalog: view, provider, cap, label: `${tier} 子会话推荐`, value: childValue(provider, tier), disabled: saving || !selectableModels(view, provider, 'child').length, onChange: value => setChild(tier, value) }), childExplicit(provider, tier), () => setChild(tier, null), issueText(`childRoutes.${provider}.${tier}`)));
|
|
197
|
-
})), h('p', null, '官方允许列表是会话创建时的快照;已有会话仍可能拒绝新配置的推荐。'))), ...conflicts.filter(issue => !issue.path.startsWith(`childRoutes.${provider}.`) && !issue.path.startsWith(`mainEfforts.${provider}.`)).map(issue => h('p', { key: issue.path, role: 'status', className: 'amr-settings-error' }, `${issue.path}:${issue.message}`))),
|
|
135
|
+
} }, testing ? '测试中…' : '测试连接'), h('span', { role: 'status', 'aria-live': 'polite', className: testResult?.failed ? 'amr-settings-error' : undefined }, testing ? '正在请求所选决策服务…' : testResult?.text ?? (dirty ? '请先保存设置,再测试连接' : ''))), h('p', null, '测试会产生一次决策请求,不包含会话内容;请先保存设置。'), invalidService && h('p', { role: 'alert', className: 'amr-settings-error' }, '请填写有效模型和兼容端点'), h(ui.SettingsSecretField, { id: 'amr-key', label: `${DECISION_SERVICES[serviceProvider].label} API Key`, hint: changingService ? '请先保存服务选择' : keyHint, text: key, disabled: changingService || saving || !view.key.available || !view.key.writable, onEdit: text => { setKey(text); setRemoveKey(false); }, configured: !changingService && view.key.configured, stateLabel: changingService ? '保存后读取' : keyState }), !changingService && view.key.available && view.key.writable && view.key.configured && h('button', { type: 'button', className: 'amr-settings-reset', onClick: () => { setRemoveKey(!removeKey); setKey(''); } }, removeKey ? '取消删除 key' : '删除 key'), invalidKey && h('p', { className: 'amr-settings-error' }, 'API key 不能含空白,且不能超过 512 字符'), h('div', { className: 'amr-settings-row' }, h('div', null, h('label', { htmlFor: 'amr-timeout' }, '判断等待上限'), h('p', null, '1000–8000 毫秒,默认 5000;超时后该会话停用 AUTO。')), h('span', null, h('input', { id: 'amr-timeout', type: 'number', className: 'amr-settings-number', min: 1000, max: 8000, step: 1, value: Number.isNaN(settings.jevTimeoutMs) ? '' : settings.jevTimeoutMs, 'aria-invalid': invalidTimeout, onChange: e => setPatch(old => ({ ...old, jevTimeoutMs: e.currentTarget.value === '' ? NaN : Number(e.currentTarget.value) })) }), ' 毫秒'))), h('section', { className: 'amr-settings-section' }, h('h3', null, '会话'), h('p', null, '主模型不变;子任务独立选择模型能力与推理投入,并在创建时应用。MANUAL 不进行自动判断。'), h('div', { className: 'amr-settings-row' }, h('div', null, '新会话默认模式', h('p', null, settings.defaultMode === 'AUTO' ? '仅影响新会话;每个用户回合都会把脱敏任务发送给所选决策服务请求 Jev。' : '仅影响新会话,不改变已有会话。')), h(ui.SegmentedControl, { id: 'amr-default-mode', className: 'amr-settings-segments', label: '新会话默认模式', value: settings.defaultMode, options: [{ value: 'MANUAL', label: 'MANUAL' }, { value: 'AUTO', label: 'AUTO' }], onChange: value => setPatch(old => ({ ...old, defaultMode: value })) })), h('div', { className: 'amr-settings-row' }, h('span', null, '判断提示'), h(ui.Switch, { checked: settings.decisionToast, label: '判断提示', onChange: value => setPatch(old => ({ ...old, decisionToast: value })) })), h('div', { className: 'amr-settings-row' }, h('span', null, '推理强度上限'), h(ui.SegmentedControl, { id: 'amr-cap', className: 'amr-settings-segments', label: '推理强度上限', value: cap, options: AUTO_EFFORTS.map(value => ({ value, label: value })), onChange: value => setPatch(old => ({ ...old, effortCap: value })) }))), h('section', { className: 'amr-settings-section' }, h('h3', null, '自动选配'), h('p', null, '主会话仅调整推理投入;子任务按目标与范围选择合适的能力档,再确定投入。保留显式指定的模型和推理等级,不自动重试或升级。'), h('p', null, 'Magpie 使用完整模型 ID 与官方允许列表;目录不可用或目标不清楚时停止并提示。fresh 子任务受支持,fork 暂不支持。'), h(StrategyDetails)), ...[...conflicts, ...saveIssues].map(issue => h('p', { key: issue.path, role: 'status', className: 'amr-settings-error' }, `${issue.path}:${issue.message}`))),
|
|
198
136
|
}));
|
|
199
137
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { createElement as h } from 'react';
|
|
2
|
-
import questions from '../
|
|
3
|
-
import {
|
|
4
|
-
import { targetEffort } from '../effort.js';
|
|
2
|
+
import questions from '../rubric.json' with { type: 'json' };
|
|
3
|
+
import { registry } from '../strategies/index.js';
|
|
5
4
|
/** Read-only: criteria and slots are imported from the executable policy, never copied. */
|
|
6
5
|
export function StrategyDetails() {
|
|
7
|
-
return h('details', { className: 'amr-strategy' }, h('summary', { style: { cursor: 'pointer', padding: '8px 0' } }, '查看决策策略'), h('p', null, 'Agent 负责拆解、派发与验收;Jev 只做有限判断,AUTO 验证目录并应用资源配置。任务数量、风险和重要性不直接等于推理难度。以下为只读说明,不会修改设置。'), h('h4', null, '能力需求(仅子任务)'), h('p', null, 'bounded:方法明确、结果可验证的执行;analytical:需要调查、关联证据或选择方案;frontier:异常困难的原创推理或相互耦合的未知;insufficient_context:缺少目标或范围,停止并补充描述。'), h('h4', null, '推理投入(主会话与子任务分别判断)'), h('p', null, '主模型不变。以具体模型推荐投入为起点,lower / baseline / higher 分别在受支持序列中降低一档、保持、提高一档;受当前设置上限约束,自动不选 max。轻量模型不意味着自动降低投入。'), h('h4', null, '当前子模型能力映射'), ...
|
|
6
|
+
return h('details', { className: 'amr-strategy' }, h('summary', { style: { cursor: 'pointer', padding: '8px 0' } }, '查看决策策略'), h('p', null, 'Agent 负责拆解、派发与验收;Jev 只做有限判断,AUTO 验证目录并应用资源配置。任务数量、风险和重要性不直接等于推理难度。以下为只读说明,不会修改设置。'), h('h4', null, '能力需求(仅子任务)'), h('p', null, 'bounded:方法明确、结果可验证的执行;analytical:需要调查、关联证据或选择方案;frontier:异常困难的原创推理或相互耦合的未知;insufficient_context:缺少目标或范围,停止并补充描述。'), h('h4', null, '推理投入(主会话与子任务分别判断)'), h('p', null, '主模型不变。以具体模型推荐投入为起点,lower / baseline / higher 分别在受支持序列中降低一档、保持、提高一档;受当前设置上限约束,自动不选 max。轻量模型不意味着自动降低投入。'), h('h4', null, '当前子模型能力映射'), ...registry.strategies.map(family => h('div', { key: family.id }, h('strong', null, family.label), h('ul', null, ...Object.entries(family.childModels).map(([level, model]) => h('li', { key: level }, `${level} → ${model}(推荐起点 ${registry.effortStart(model) ?? '目录默认'})`))))), h('p', null, `Magpie 保留对应家族前缀与真实 provider,不跨入口回退;模型与 effort 必须同时通过当前目录及官方允许列表校验。其他家族需显式指定模型及可验证投入。`), h('h4', null, '约束、失败与恢复'), h('p', null, '显式指定的模型与投入优先;两者都指定时只校验、不请求 Jev。目标不足、目录失效或子判断失败时停止派发,不偷偷继承或换模。主判断失败则可见停用 AUTO。fresh 子任务创建时绑定精确身份;后续消息和恢复不重新选模。fork 暂不支持。MANUAL 停止新判断,已有绑定子任务仍保持初始路线。'), h('h4', null, 'Jev 实际判据原文'), h('p', null, '下面直接展示执行中使用的英文规则,避免翻译改变含义;两个问题相互独立,不能读取对方答案。'), ...Object.entries(questions).map(([dimension, question]) => h('section', { key: dimension, style: { overflowWrap: 'anywhere' } }, h('strong', null, dimension), h('p', null, question.instructions), h('dl', null, ...Object.entries(question.criteria).flatMap(([label, criterion]) => [h('dt', { key: `${label}-label` }, h('strong', null, label)), h('dd', { key: label, style: { margin: '4px 0 12px 12px' } }, criterion)])))));
|
|
8
7
|
}
|