@wendongfly/myhi 1.3.205 → 1.3.208
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/dist/chat.html +305 -26
- package/dist/chat.html.gz +0 -0
- package/dist/index.html +80 -4
- package/dist/index.html.gz +0 -0
- package/dist/index.js +1 -1
- package/dist/index.min.js +180 -169
- package/package.json +1 -1
package/dist/index.html
CHANGED
|
@@ -181,6 +181,7 @@
|
|
|
181
181
|
@media (max-width: 540px) {
|
|
182
182
|
.set-item.inp { flex-direction: column; align-items: stretch; gap: 0.4rem; }
|
|
183
183
|
.set-item.inp .set-ctl { flex: none; }
|
|
184
|
+
.set-item.inp .set-seg { flex: none; } /* 访问协议 / 升级下载源:说明长,名称在上、分段条整行(以前被挤成一字一行) */
|
|
184
185
|
.set-lbl small { display: inline; margin-left: 0.4rem; }
|
|
185
186
|
.set-seg { flex: 1; }
|
|
186
187
|
}
|
|
@@ -955,6 +956,19 @@
|
|
|
955
956
|
<button class="set-link" onclick="closeSettings();showUsage()">📊 用量 <small>费用 / token 统计</small></button>
|
|
956
957
|
</div>
|
|
957
958
|
|
|
959
|
+
<!-- 场景模型:同一个网关 + token,不同场景用不同模型(仅管理员)-->
|
|
960
|
+
<div id="set-models" class="admin-only">
|
|
961
|
+
<div class="set-group-t">系统设置 · 模型(所有任务生效)</div>
|
|
962
|
+
<div class="set-card">
|
|
963
|
+
<div class="set-item" style="display:block;font-size:0.74rem;color:var(--muted);line-height:1.6" id="set-models-src">读取网关模型列表…</div>
|
|
964
|
+
<div id="set-models-rows"></div>
|
|
965
|
+
<div class="set-item" style="justify-content:flex-end"><button class="set-btn" id="set-models-save" onclick="saveSceneModels()">保存</button></div>
|
|
966
|
+
</div>
|
|
967
|
+
<div id="set-models-status" class="set-status"></div>
|
|
968
|
+
<datalist id="set-models-dl-tools"></datalist><datalist id="set-models-dl-vision"></datalist>
|
|
969
|
+
<datalist id="set-models-dl-all"></datalist>
|
|
970
|
+
</div>
|
|
971
|
+
|
|
958
972
|
<div id="set-svc">
|
|
959
973
|
<div class="set-group-t" id="set-svc-t">服务</div>
|
|
960
974
|
<div class="set-card">
|
|
@@ -963,12 +977,12 @@
|
|
|
963
977
|
<button id="set-up-btn" class="set-btn dirty" style="display:none" onclick="homeUpgrade()">升级</button>
|
|
964
978
|
</div>
|
|
965
979
|
<div id="set-up-status" class="set-status"></div>
|
|
966
|
-
<div class="set-item admin-only" id="set-proto-row">
|
|
980
|
+
<div class="set-item inp admin-only" id="set-proto-row">
|
|
967
981
|
<div class="set-lbl">访问协议<small id="set-proto-sub">HTTPS 才能在手机上用麦克风 / 摄像头;内网只想少点「不安全」提示可用 HTTP</small></div>
|
|
968
982
|
<div class="set-seg" id="set-proto"><button data-p="https">HTTPS</button><button data-p="http">HTTP</button></div>
|
|
969
983
|
</div>
|
|
970
984
|
<div id="set-proto-status" class="set-status"></div>
|
|
971
|
-
<div class="set-item admin-only">
|
|
985
|
+
<div class="set-item inp admin-only">
|
|
972
986
|
<div class="set-lbl">升级下载源<small>myhi 和 Claude 升级从哪下载。自动:先官方源,连不上或装失败自动换国内镜像</small></div>
|
|
973
987
|
<div class="set-seg" id="set-reg"><button data-r="auto">自动</button><button data-r="official">官方</button><button data-r="mirror">国内镜像</button></div>
|
|
974
988
|
</div>
|
|
@@ -1122,7 +1136,7 @@
|
|
|
1122
1136
|
document.getElementById('set-mode').innerHTML = !data.multiUser ? '👤 单用户模式 · 所有功能都可用'
|
|
1123
1137
|
: _isAdmin ? '👥 多用户模式 · 你是管理员 · <a href="/admin#mode">管理成员 →</a>'
|
|
1124
1138
|
: `👥 多用户模式 · 你是成员「${esc(data.name || '')}」· 本机显示名、语音等全局设置需管理员修改`;
|
|
1125
|
-
if (!_isAdmin) document.querySelectorAll('#set-
|
|
1139
|
+
if (!_isAdmin) document.querySelectorAll('#set-sheet .admin-only').forEach(el => el.remove());
|
|
1126
1140
|
checkAiEngine();
|
|
1127
1141
|
checkHomeVersion();
|
|
1128
1142
|
if (data.hasUsers && data.member) { document.getElementById('passwd-btn').style.display = ''; document.getElementById('ws-menu-pw').style.display = ''; }
|
|
@@ -1315,7 +1329,7 @@
|
|
|
1315
1329
|
window.openSettings = function() {
|
|
1316
1330
|
renderAppearance();
|
|
1317
1331
|
['set-machine-status', 'set-asr-status', 'set-restart-status', 'set-up-status', 'set-proto-status'].filter(id => document.getElementById(id)).forEach(id => setStatus(id, ''));
|
|
1318
|
-
if (_isAdmin) { loadProto(); loadRegistry(); }
|
|
1332
|
+
if (_isAdmin) { loadProto(); loadRegistry(); loadSceneModels(); }
|
|
1319
1333
|
document.querySelectorAll('#set-sheet .set-btn').forEach(b => b.classList.remove('dirty'));
|
|
1320
1334
|
document.getElementById('set-backdrop').classList.add('open');
|
|
1321
1335
|
document.getElementById('set-sheet').classList.add('open');
|
|
@@ -1480,6 +1494,68 @@
|
|
|
1480
1494
|
});
|
|
1481
1495
|
if (d.fromEnv) setStatus('set-proto-status', '启动时设了环境变量 MYHI_NO_HTTPS,固定为 HTTP');
|
|
1482
1496
|
}
|
|
1497
|
+
// ── 场景模型 ────────────────────────────────────────────
|
|
1498
|
+
// 每个场景一个输入框:可从网关模型列表里选(datalist),也可手填任意模型名 / 别名;留空 = 用默认
|
|
1499
|
+
const SCENE_HINT = {
|
|
1500
|
+
main: '任务对话默认用它(聊天框里没单独选模型时);留空 = Claude Code 默认',
|
|
1501
|
+
plan: '计划模式(只调研、出方案)用,可选更强的;批准执行后回到主任务模型;留空 = 跟主任务一样',
|
|
1502
|
+
subagent: 'AI 把任务拆成子任务时用;留空 = 跟主任务一样',
|
|
1503
|
+
background: '生成标题、摘要等后台小活,建议选便宜的',
|
|
1504
|
+
ask: '「问一问」和解释,建议选便宜快速的;留空 = sonnet',
|
|
1505
|
+
voice: '语音转成书面文字,建议选便宜的;留空 = 同问一问',
|
|
1506
|
+
vision: '主任务模型看不了图时,先用它把聊天框发的图片转成文字再交给主模型(多花几秒,可在聊天里点开核对);留空 = 不转',
|
|
1507
|
+
};
|
|
1508
|
+
async function loadSceneModels() {
|
|
1509
|
+
let d; try { d = await (await fetch('/api/models')).json(); } catch { return; }
|
|
1510
|
+
if (!d.ok) return;
|
|
1511
|
+
const src = document.getElementById('set-models-src');
|
|
1512
|
+
const ms = (d.models || []).filter(m => !m.id.includes('*'));
|
|
1513
|
+
const t = d.token, quota = t && (t.dailyLimit != null || t.monthlyLimit != null)
|
|
1514
|
+
? ` 积分:${t.dailyLimit != null ? `今日已用 ${t.dailyUsed ?? '?'} / ${t.dailyLimit}` : ''}${t.monthlyLimit != null ? ` 本月已用 ${t.monthlyUsed ?? '?'} / ${t.monthlyLimit}` : ''}。` : '';
|
|
1515
|
+
// 没配自定义网关:不提供模型选择(全部跟随 Claude Code 默认),整组只留一句说明
|
|
1516
|
+
const noGw = !d.gateway;
|
|
1517
|
+
document.getElementById('set-models-rows').style.display = noGw ? 'none' : '';
|
|
1518
|
+
document.getElementById('set-models-save').parentElement.style.display = noGw ? 'none' : '';
|
|
1519
|
+
if (noGw) { src.textContent = '没有配置自定义网关:所有任务使用 Claude Code 默认模型,不需要选模型。配置网关后,可在这里按场景(主任务 / 计划 / 问一问 / 看图…)从网关模型里选。'; return; }
|
|
1520
|
+
src.textContent = d.error ? '网关模型列表读取失败(' + d.error + '),可以手填模型名。'
|
|
1521
|
+
: `网关上有 ${ms.length} 个模型${d.scoped ? '(这个 token 可用的)' : '(网关是旧版,未按 token 过滤,个别模型可能用不了)'},点输入框可选,也可以手填。` + quota;
|
|
1522
|
+
// 两份候选:主会话 / 子代理只列能用工具干活的;问一问、语音整理、后台小任务全列,便宜的排前面
|
|
1523
|
+
const opt = (m) => `<option value="${esc(m.id)}">${esc([m.name !== m.id ? m.name : '', m.kind === 'alias' && m.target ? '→ ' + m.target : '', (m.tags || []).includes('cheap') ? '便宜' : '', m.multiplier != null ? m.multiplier + 'x' : '', (m.caps || []).includes('vision') ? '能看图' : ''].filter(Boolean).join(' · '))}</option>`;
|
|
1524
|
+
const aliases = ''; // 模型只从网关目录选(不再列 Claude 别名)
|
|
1525
|
+
const cheapFirst = [...ms].sort((a, b) => ((b.tags || []).includes('cheap') - (a.tags || []).includes('cheap')));
|
|
1526
|
+
document.getElementById('set-models-dl-tools').innerHTML = ms.filter(m => !m.fit?.main).map(opt).join('') + aliases;
|
|
1527
|
+
document.getElementById('set-models-dl-all').innerHTML = cheapFirst.map(opt).join('') + aliases;
|
|
1528
|
+
document.getElementById('set-models-dl-vision').innerHTML = cheapFirst.filter(m => (m.caps || []).includes('vision')).map(opt).join('') + aliases;
|
|
1529
|
+
const TOOL_SCENES = { main: 1, plan: 1, subagent: 1, vision: 1 };
|
|
1530
|
+
const DL = (k) => k === 'vision' ? 'set-models-dl-vision' : TOOL_SCENES[k] ? 'set-models-dl-tools' : 'set-models-dl-all';
|
|
1531
|
+
document.getElementById('set-models-rows').innerHTML = Object.entries(d.sceneLabels || {}).map(([k, label]) =>
|
|
1532
|
+
`<div class="set-item inp"><div class="set-lbl">${esc(label)}<small>${esc(SCENE_HINT[k] || '')}</small></div>
|
|
1533
|
+
<div class="set-ctl"><input class="set-inp" data-scene="${esc(k)}" list="${DL(k)}" value="${esc(d.scenes?.[k] || '')}" placeholder="默认" autocomplete="off" spellcheck="false"></div>
|
|
1534
|
+
<div class="set-warn" data-warn="${esc(k)}" style="display:none;font-size:0.72rem;color:var(--yellow,#d29922)"></div></div>`).join('');
|
|
1535
|
+
// 填了不适合这个场景的模型(不支持工具等)→ 输入框下提示原因(不硬拦:网关元数据可能没填全)
|
|
1536
|
+
const byId = Object.fromEntries(ms.map(m => [m.id, m]));
|
|
1537
|
+
const check = (inp) => {
|
|
1538
|
+
const k = inp.dataset.scene, w = document.querySelector(`[data-warn="${k}"]`);
|
|
1539
|
+
const why = TOOL_SCENES[k] && byId[inp.value.trim()]?.fit?.[k];
|
|
1540
|
+
// 看图:主任务模型本身看不了图却没配看图模型 → 提醒(图片发过去 AI 会读不出来)
|
|
1541
|
+
const mainM = byId[(document.querySelector('[data-scene="main"]')?.value || '').trim()];
|
|
1542
|
+
const hint = k === 'vision' && !inp.value.trim() && mainM?.caps?.length && !mainM.caps.includes('vision') ? `主任务模型 ${mainM.name} 看不了图,建议配一个,否则发图片 AI 会读不出来` : '';
|
|
1543
|
+
w.textContent = why ? '⚠ ' + why : hint ? '⚠ ' + hint : ''; w.style.display = why || hint ? '' : 'none';
|
|
1544
|
+
};
|
|
1545
|
+
document.querySelectorAll('#set-models-rows [data-scene]').forEach(i => { check(i); i.addEventListener('input', () => check(i)); });
|
|
1546
|
+
// 和其他设置项一样:改了才出现「保存」
|
|
1547
|
+
const btn = document.getElementById('set-models-save');
|
|
1548
|
+
btn.classList.remove('dirty');
|
|
1549
|
+
document.querySelectorAll('#set-models-rows [data-scene]').forEach(i => i.addEventListener('input', () => btn.classList.add('dirty')));
|
|
1550
|
+
}
|
|
1551
|
+
window.saveSceneModels = async function() {
|
|
1552
|
+
const body = {};
|
|
1553
|
+
document.querySelectorAll('#set-models-rows [data-scene]').forEach(i => { body[i.dataset.scene] = i.value.trim(); });
|
|
1554
|
+
let r; try { r = await (await fetch('/api/model-scenes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })).json(); } catch (e) { r = { ok: false, error: e.message }; }
|
|
1555
|
+
if (r.ok) document.getElementById('set-models-save').classList.remove('dirty');
|
|
1556
|
+
setStatus('set-models-status', r.ok ? '已保存。新开的任务和下一次重启的 AI 进程生效;正在跑的任务在下次重启 AI 后生效' : '保存失败:' + (r.error || ''));
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1483
1559
|
async function loadRegistry() {
|
|
1484
1560
|
let d; try { d = await (await fetch('/api/npm-registry')).json(); } catch { return; }
|
|
1485
1561
|
const seg = document.getElementById('set-reg'); if (!seg || !d.ok) return;
|
package/dist/index.html.gz
CHANGED
|
Binary file
|