@mokuyoaxis/dsh-iris 0.2.1 → 0.2.2
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 +23 -1
- package/README.en.md +25 -25
- package/README.md +25 -25
- package/docs/AGENT_QUICKSTART.md +7 -7
- package/docs/ARCHITECTURE.md +10 -2
- package/docs/ARTIFACT_LIBRARY.md +1 -1
- package/docs/CLI_MANAGEMENT.md +5 -3
- package/docs/COMPOSITE_VISION.md +1 -1
- package/docs/CORE_RUNTIME_CONTRACT.md +9 -1
- package/docs/DSH_CORE_MIGRATION.md +1 -1
- package/docs/FAULT_INJECTION.md +10 -1
- package/docs/HEADLESS_CLI.md +4 -2
- package/docs/HOST_ADAPTER_CONTRACT.md +1 -1
- package/docs/MODEL_PORT_CONTRACT.md +4 -2
- package/docs/OCR_MODEL.md +1 -1
- package/docs/PROMPT_OPTIMIZER.md +33 -3
- package/docs/PROVIDER_ADAPTER_CONTRACT.md +10 -2
- package/docs/PROVIDER_HEALTH.md +1 -1
- package/docs/ROADMAP.md +10 -2
- package/docs/SECURITY.md +1 -1
- package/docs/TASK_SEMANTICS.md +1 -1
- package/docs/VISION_CLI.md +1 -1
- package/docs/VISION_MODEL.md +1 -1
- package/docs/WORKBENCH_ARTIFACTS.md +1 -1
- package/docs/releases/0.2.2.md +31 -0
- package/lib/actions.js +12 -16
- package/lib/adapters.js +88 -24
- package/lib/capability.js +23 -16
- package/lib/client.js +268 -22
- package/lib/config.js +13 -4
- package/lib/core-lease-recovery.js +64 -92
- package/lib/core-runtime.js +45 -47
- package/lib/core-writer-lease.js +134 -0
- package/lib/models.js +19 -37
- package/lib/prompt-optimizer-config.js +19 -9
- package/lib/prompt-optimizer-core.js +133 -7
- package/lib/provider-adapters.js +2 -1
- package/lib/provider-config-service.js +3 -4
- package/lib/provider-protocol.js +61 -0
- package/lib/provider-task-runner.js +6 -4
- package/package.json +1 -1
- package/user_guide.md +30 -16
package/lib/client.js
CHANGED
|
@@ -21,7 +21,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
21
21
|
var exports = module.exports;
|
|
22
22
|
|
|
23
23
|
var React = require('react');
|
|
24
|
-
var IRIS_CLIENT_VERSION = '0.2.
|
|
24
|
+
var IRIS_CLIENT_VERSION = '0.2.2';
|
|
25
25
|
var HOST_CLIENT_PROTOCOL_VERSION = 0;
|
|
26
26
|
var reportedSeats = [];
|
|
27
27
|
|
|
@@ -299,9 +299,31 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
299
299
|
'.iris-po-trigger:hover { transform: translateY(-2px) scale(1.10); filter: drop-shadow(0 5px 10px rgba(112,137,255,.42)); }',
|
|
300
300
|
'.iris-po-trigger:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #7aa2f7); outline-offset: 2px; }',
|
|
301
301
|
'.iris-po-backdrop { position: fixed; inset: 0; z-index: 10019; background: rgba(8,10,18,.30); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); touch-action: none; animation: iris-po-fade .16s ease-out; }',
|
|
302
|
-
'.iris-po-panel
|
|
302
|
+
'.iris-po-panel:where(dialog) { left: auto; top: auto; margin: 0; max-width: none; }',
|
|
303
|
+
'dialog.iris-po-panel::backdrop { background: rgba(8,10,18,.30); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); }',
|
|
304
|
+
'.iris-po-panel { position: fixed; z-index: 10020; right: max(14px, env(safe-area-inset-right)); bottom: max(78px, calc(env(safe-area-inset-bottom) + 68px)); width: min(560px, calc(100vw - 28px)); max-height: min(780px, calc(100dvh - 104px)); overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; box-sizing: border-box; padding: 18px; display: grid; gap: 14px; background-color: rgba(35,36,47,.84); background: radial-gradient(circle at 10% -5%, rgba(132,160,255,.20), transparent 42%), color-mix(in srgb, var(--dsw-alias-bg-layer-1, #26262e) 92%, transparent); color: var(--dsw-alias-label-primary, #eeeeF2); border: 1px solid rgba(150,164,215,.25); border-radius: 22px; box-shadow: 0 24px 70px rgba(0,0,0,.36), inset 0 1px 0 rgba(255,255,255,.07); backdrop-filter: blur(28px) saturate(1.28); -webkit-backdrop-filter: blur(28px) saturate(1.28); animation: iris-po-rise .20s cubic-bezier(.2,.8,.2,1); scrollbar-width: thin; outline: none; }',
|
|
303
305
|
'.iris-po-head { position: sticky; top: -18px; z-index: 1; margin: -18px -18px 0; padding: 16px 18px 10px; display: flex; align-items: center; justify-content: space-between; gap: 10px; background: linear-gradient(to bottom, color-mix(in srgb, var(--dsw-alias-bg-layer-1, #26262e) 88%, transparent) 72%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }',
|
|
304
|
-
'.iris-po-title { display: inline-flex; align-items: center; gap: 9px; font-size:
|
|
306
|
+
'.iris-po-title { display: inline-flex; align-items: center; gap: 9px; font-size: 16px; letter-spacing: .01em; }',
|
|
307
|
+
'.iris-po-intro { display: grid; gap: 9px; }',
|
|
308
|
+
'.iris-po-principles { display: flex; flex-wrap: wrap; gap: 6px; }',
|
|
309
|
+
'.iris-po-principles span { padding: 3px 8px; border-radius: 999px; background: rgba(113,139,219,.09); color: var(--dsw-alias-label-secondary, #b4bdd5); font-size: 10px; }',
|
|
310
|
+
'.iris-po-section { min-width: 0; border: 1px solid rgba(137,155,218,.22); border-radius: 16px; background: rgba(128,150,221,.045); overflow: hidden; }',
|
|
311
|
+
'.iris-po-section > summary { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 14px; cursor: pointer; list-style: none; font-size: 13px; font-weight: 650; }',
|
|
312
|
+
'.iris-po-section > summary::-webkit-details-marker { display: none; }',
|
|
313
|
+
'.iris-po-section > summary::after { content: "⌄"; color: var(--dsw-alias-label-secondary, #a6a6b2); transition: transform .15s; }',
|
|
314
|
+
'.iris-po-section[open] > summary::after { transform: rotate(180deg); }',
|
|
315
|
+
'.iris-po-section-caption { margin-left: auto; font-size: 11px; font-weight: 400; color: var(--dsw-alias-label-secondary, #a6a6b2); }',
|
|
316
|
+
'.iris-po-section-body { min-width: 0; display: grid; gap: 13px; padding: 0 14px 14px; }',
|
|
317
|
+
'.iris-po-section:not([open]) > .iris-po-section-body { display: none; }',
|
|
318
|
+
'.iris-po-targets { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 5px; }',
|
|
319
|
+
'.iris-po-target { min-width: 0; min-height: 42px; padding: 7px 3px; border: 1px solid transparent; border-radius: 10px; font: inherit; font-size: 12px; cursor: pointer; color: var(--dsw-alias-label-secondary, #a6a6b2); background: rgba(128,150,221,.05); }',
|
|
320
|
+
'.iris-po-target[aria-pressed="true"] { color: var(--dsw-alias-label-primary, #eeeef2); border-color: rgba(124,147,225,.48); background: rgba(124,147,225,.18); box-shadow: inset 0 1px 0 rgba(255,255,255,.1); font-weight: 650; }',
|
|
321
|
+
'.iris-po-target:disabled { opacity: .45; cursor: not-allowed; }',
|
|
322
|
+
'.iris-po-target:focus-visible, .iris-po-section > summary:focus-visible, .iris-po-config > summary:focus-visible { outline: 2px solid #8d9fe6; outline-offset: 2px; }',
|
|
323
|
+
'.iris-po-source { min-width: 0; padding: 11px 12px; border-radius: 12px; border: 1px solid rgba(150,164,215,.14); background: rgba(128,150,221,.04); }',
|
|
324
|
+
'.iris-po-source-head { display: flex; justify-content: space-between; gap: 10px; font-size: 11px; color: var(--dsw-alias-label-secondary, #a6a6b2); }',
|
|
325
|
+
'.iris-po-source-text { margin: 8px 0 0; max-height: 112px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 12px; line-height: 1.65; }',
|
|
326
|
+
'.iris-po-template { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 200px; overflow: auto; font-size: 11px; line-height: 1.65; padding: 10px; border-radius: 10px; background: rgba(128,150,221,.06); }',
|
|
305
327
|
'.iris-po-mark, .iris-po-settings-mark { display: inline-flex; align-items: center; justify-content: center; width: 29px; height: 29px; border-radius: 999px; background: rgba(255,255,255,.055); box-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 5px 16px rgba(67,83,168,.16); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); filter: drop-shadow(0 2px 5px rgba(105,128,235,.16)); }',
|
|
306
328
|
'.iris-po-close { width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 999px; background: rgba(255,255,255,.05); color: var(--dsw-alias-label-secondary, #a6a6b2); font-size: 14px; cursor: pointer; transition: background .15s, color .15s; }',
|
|
307
329
|
'.iris-po-close:hover { background: rgba(255,255,255,.10); color: var(--dsw-alias-label-primary, #eeeef2); }',
|
|
@@ -313,6 +335,11 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
313
335
|
'.iris-po-field select:focus, .iris-po-field textarea:focus, .iris-po-result textarea:focus { border-color: rgba(122,162,247,.72); background: rgba(255,255,255,.075); box-shadow: 0 0 0 3px rgba(122,162,247,.11); }',
|
|
314
336
|
'.iris-po-field textarea { min-height: 60px; resize: vertical; line-height: 1.55; }',
|
|
315
337
|
'.iris-po-rules { display: grid; gap: 10px; }',
|
|
338
|
+
'.iris-po-editing { display: grid; gap: 8px; padding: 10px 12px; border: 1px solid rgba(150,164,215,.16); border-radius: 12px; }',
|
|
339
|
+
'.iris-po-editing label { display: flex; gap: 8px; align-items: center; font-size: 12px; cursor: pointer; }',
|
|
340
|
+
'.iris-po-suggestion { display: grid; gap: 7px; padding: 10px; margin-top: 8px; border-radius: 10px; background: rgba(128,150,221,.065); overflow-wrap: anywhere; }',
|
|
341
|
+
'.iris-po-suggestion label { display: flex; align-items: start; gap: 8px; font-size: 12px; }',
|
|
342
|
+
'.iris-po-suggestion p { margin: 0; white-space: pre-wrap; font-size: 12px; line-height: 1.6; }',
|
|
316
343
|
'.iris-po-diff { white-space: pre-wrap; overflow-wrap: anywhere; max-height: 28dvh; overflow-y: auto; font-size: 12px; line-height: 1.6; }',
|
|
317
344
|
'.iris-po-diff del, .iris-po-diff ins { display: block; padding: 6px 8px; border-radius: 7px; margin: 5px 0; }',
|
|
318
345
|
'.iris-po-diff del { background: rgba(226,113,126,.12); }',
|
|
@@ -324,7 +351,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
324
351
|
'.iris-po-primary { background: linear-gradient(135deg, #779cf3, #8c79de); color: #fff; border-color: rgba(174,187,255,.55); font-weight: 650; box-shadow: 0 6px 18px rgba(76,92,188,.22); }',
|
|
325
352
|
'.iris-po-primary:hover { transform: translateY(-1px); box-shadow: 0 8px 22px rgba(76,92,188,.32); }',
|
|
326
353
|
'.iris-po-primary:disabled, .iris-po-btn:disabled { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }',
|
|
327
|
-
'.iris-po-result { display: grid; gap: 8px; padding: 12px; border: 1px solid rgba(137,155,218,.18); border-radius: 15px; background: rgba(
|
|
354
|
+
'.iris-po-result { display: grid; gap: 8px; padding: 12px; border: 1px solid rgba(137,155,218,.18); border-radius: 15px; background: rgba(128,150,221,.045); }',
|
|
328
355
|
'.iris-po-result-label { font-size: 11px; font-weight: 650; letter-spacing: .02em; }',
|
|
329
356
|
'.iris-po-result textarea { min-height: 156px; max-height: 34dvh; resize: vertical; line-height: 1.58; }',
|
|
330
357
|
'.iris-po-config { padding: 11px 12px; border: 1px solid rgba(150,164,215,.14); border-radius: 13px; background: rgba(255,255,255,.025); font-size: 11px; }',
|
|
@@ -812,7 +839,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
812
839
|
var blocked = caps.map(function (c) { return m.health && m.health[c]; }).find(function (health) { return health && health.rateLimited; });
|
|
813
840
|
return React.createElement('div', { key: m.id, className: 'iris-pm-mrow' },
|
|
814
841
|
React.createElement('span', { className: 'iris-pm-mname' }, m.id, m.source === 'manual' ? React.createElement('span', { className: 'iris-pm-mtag' }, '手动') : null),
|
|
815
|
-
React.createElement('span', { className: 'iris-pm-mcaps' }, caps.map(function (c) { return verBadge(m, c); })),
|
|
842
|
+
React.createElement('span', { className: 'iris-pm-mcaps' }, caps.length ? caps.map(function (c) { return verBadge(m, c); }) : '未标注功能'),
|
|
843
|
+
React.createElement(ModelCapabilityEditor, { provider: p, model: m, onNote: props.onNote, onDone: props.onDone }),
|
|
816
844
|
caps.includes('image-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '图片协议 ',
|
|
817
845
|
React.createElement('select', { value: m.imageProtocol || 'auto', onChange: function (e) { setImageProtocol(m, e.target.value); } },
|
|
818
846
|
React.createElement('option', { value: 'auto' }, '继承账号默认(' + (p.mediaProtocol || '自动') + ')'),
|
|
@@ -834,6 +862,42 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
834
862
|
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { props.addModel(p, newId.trim()); setNewId(''); } }, '+ 添加')));
|
|
835
863
|
}
|
|
836
864
|
|
|
865
|
+
function ModelCapabilityEditor(props) {
|
|
866
|
+
var openPair = React.useState(false);
|
|
867
|
+
var capsPair = React.useState([]);
|
|
868
|
+
var savingPair = React.useState(false);
|
|
869
|
+
var selected = capsPair[0];
|
|
870
|
+
var saving = savingPair[0];
|
|
871
|
+
function save() {
|
|
872
|
+
if (saving) return;
|
|
873
|
+
savingPair[1](true);
|
|
874
|
+
postAction('providers_set_model_caps', { id: props.provider.id, model_id: props.model.id, capabilities: selected })
|
|
875
|
+
.then(function (r) {
|
|
876
|
+
if (r.ok) {
|
|
877
|
+
openPair[1](false);
|
|
878
|
+
props.onNote('模型功能标签已保存');
|
|
879
|
+
props.onDone();
|
|
880
|
+
} else props.onNote(r.d.error || '保存失败');
|
|
881
|
+
}).finally(function () { savingPair[1](false); });
|
|
882
|
+
}
|
|
883
|
+
if (!openPair[0]) return React.createElement('button', { className: 'iris-pm-btn', title: '设置此模型可用于哪些功能',
|
|
884
|
+
onClick: function () { capsPair[1]((props.model.capabilities || []).slice()); openPair[1](true); } }, '设置功能');
|
|
885
|
+
return React.createElement('div', { className: 'iris-wb-row' },
|
|
886
|
+
CAP_ROWS.map(function (row) {
|
|
887
|
+
return React.createElement('label', { key: row.cap, className: 'iris-pm-note' },
|
|
888
|
+
React.createElement('input', { type: 'checkbox', value: row.cap, checked: selected.includes(row.cap), disabled: saving,
|
|
889
|
+
onChange: function (e) {
|
|
890
|
+
var checked = e.target.checked;
|
|
891
|
+
capsPair[1](function (previous) {
|
|
892
|
+
return checked ? previous.filter(function (cap) { return cap !== row.cap; }).concat(row.cap)
|
|
893
|
+
: previous.filter(function (cap) { return cap !== row.cap; });
|
|
894
|
+
});
|
|
895
|
+
} }), row.label);
|
|
896
|
+
}),
|
|
897
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: saving, onClick: save }, saving ? '保存中…' : '保存功能'),
|
|
898
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: saving, onClick: function () { openPair[1](false); } }, '取消'));
|
|
899
|
+
}
|
|
900
|
+
|
|
837
901
|
function VisionInputEditor(props) {
|
|
838
902
|
var p = props.provider;
|
|
839
903
|
var m = props.model;
|
|
@@ -2674,6 +2738,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2674
2738
|
selectedTask ? React.createElement(TaskDetailDrawer, { taskId: selectedTask }) : null);
|
|
2675
2739
|
}
|
|
2676
2740
|
|
|
2741
|
+
var PROMPT_TARGET_OPTIONS = [
|
|
2742
|
+
{ value: 'general', label: '通用', description: '整理目标、背景、约束与输出要求。' },
|
|
2743
|
+
{ value: 'image', label: '画图', description: '整理已有主体、场景与构图要求,未指定细节不自动补齐。' },
|
|
2744
|
+
{ value: 'image-edit', label: '改图', description: '明确要修改的部分,保留原图其余特征。' },
|
|
2745
|
+
{ value: 'video', label: '视频', description: '组织动作、镜头与时间上的连续性。' },
|
|
2746
|
+
{ value: 's2v', label: '数字人', description: '围绕已有首帧与音频设计表情、动作和镜头。' }
|
|
2747
|
+
];
|
|
2748
|
+
|
|
2677
2749
|
// 折叠相同首尾,只显示中间替换区;按 Unicode code point 比较,不拆开 emoji。
|
|
2678
2750
|
function promptChanges(original, edited) {
|
|
2679
2751
|
var before = Array.from(original);
|
|
@@ -2695,6 +2767,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2695
2767
|
var openPair = React.useState(false);
|
|
2696
2768
|
var open = openPair[0];
|
|
2697
2769
|
var setOpen = openPair[1];
|
|
2770
|
+
var generalPair = React.useState(true);
|
|
2771
|
+
var generalOpen = generalPair[0];
|
|
2698
2772
|
var targetPair = React.useState('general');
|
|
2699
2773
|
var target = targetPair[0];
|
|
2700
2774
|
var setTarget = targetPair[1];
|
|
@@ -2722,14 +2796,37 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2722
2796
|
var editedPair = React.useState('');
|
|
2723
2797
|
var edited = editedPair[0];
|
|
2724
2798
|
var setEdited = editedPair[1];
|
|
2799
|
+
var editingPair = React.useState('faithful');
|
|
2800
|
+
var editing = editingPair[0];
|
|
2801
|
+
var suggestionsPair = React.useState(null);
|
|
2802
|
+
var suggestions = suggestionsPair[0];
|
|
2803
|
+
var setSuggestions = suggestionsPair[1];
|
|
2804
|
+
var selectedSuggestionsPair = React.useState([]);
|
|
2805
|
+
var selectedSuggestions = selectedSuggestionsPair[0];
|
|
2806
|
+
var setSelectedSuggestions = selectedSuggestionsPair[1];
|
|
2807
|
+
var undoPair = React.useState([]);
|
|
2808
|
+
var suggestionUndo = undoPair[0];
|
|
2809
|
+
var setSuggestionUndo = undoPair[1];
|
|
2810
|
+
var editedRef = React.useRef(edited);
|
|
2811
|
+
var resultRef = React.useRef(result);
|
|
2812
|
+
editedRef.current = edited;
|
|
2813
|
+
resultRef.current = result;
|
|
2725
2814
|
var abortRef = React.useRef(null);
|
|
2726
2815
|
var fileRef = React.useRef(null);
|
|
2816
|
+
var panelRef = React.useRef(null);
|
|
2727
2817
|
var lastWrittenRef = React.useRef(null);
|
|
2728
2818
|
var resultSessionRef = React.useRef(null);
|
|
2729
2819
|
var sessionRef = React.useRef(props.sessionId);
|
|
2730
2820
|
sessionRef.current = props.sessionId;
|
|
2731
2821
|
var supportsRules = !!(configState && configState.capabilities && configState.capabilities.rules === true);
|
|
2732
2822
|
var supportsAssembly = !!(configState && configState.capabilities && configState.capabilities.assemble === true);
|
|
2823
|
+
var editingCapabilities = configState && configState.capabilities && configState.capabilities.editing;
|
|
2824
|
+
var supportsFaithful = Array.isArray(editingCapabilities) && editingCapabilities.indexOf('faithful') >= 0 && editingCapabilities.indexOf('rewrite') >= 0;
|
|
2825
|
+
var supportsSuggestions = Array.isArray(editingCapabilities) && editingCapabilities.indexOf('suggestions') >= 0;
|
|
2826
|
+
function supportsTarget(value) {
|
|
2827
|
+
var available = configState && configState.capabilities && configState.capabilities.targets;
|
|
2828
|
+
return Array.isArray(available) ? available.indexOf(value) >= 0 : value !== 'image-edit';
|
|
2829
|
+
}
|
|
2733
2830
|
|
|
2734
2831
|
function promptRequest(operation, body, signal) {
|
|
2735
2832
|
return fetch('/iris/api/prompt-optimizer/' + operation, {
|
|
@@ -2769,10 +2866,35 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2769
2866
|
React.useEffect(function () {
|
|
2770
2867
|
cancel();
|
|
2771
2868
|
setResult(null);
|
|
2869
|
+
setSuggestions(null); setSelectedSuggestions([]); setSuggestionUndo([]);
|
|
2772
2870
|
lastWrittenRef.current = null;
|
|
2773
2871
|
resultSessionRef.current = null;
|
|
2774
2872
|
}, [props.sessionId]);
|
|
2775
2873
|
|
|
2874
|
+
React.useEffect(function () {
|
|
2875
|
+
if (!open) return;
|
|
2876
|
+
var previous = document.activeElement;
|
|
2877
|
+
var dialog = panelRef.current;
|
|
2878
|
+
if (dialog) { dialog.showModal(); dialog.focus(); }
|
|
2879
|
+
function keyboard(event) {
|
|
2880
|
+
if (event.key === 'Escape') { event.preventDefault(); closePanel(); }
|
|
2881
|
+
if (event.key !== 'Tab' || !panelRef.current) return;
|
|
2882
|
+
var items = Array.from(panelRef.current.querySelectorAll('button:not(:disabled), textarea:not(:disabled), summary, input:not(:disabled)'))
|
|
2883
|
+
.filter(function (item) { return item.getClientRects().length > 0; });
|
|
2884
|
+
if (!items.length) return;
|
|
2885
|
+
var first = items[0], last = items[items.length - 1];
|
|
2886
|
+
if (event.shiftKey && (document.activeElement === first || document.activeElement === panelRef.current)) {
|
|
2887
|
+
event.preventDefault(); last.focus();
|
|
2888
|
+
} else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
|
|
2889
|
+
}
|
|
2890
|
+
window.addEventListener('keydown', keyboard);
|
|
2891
|
+
return function () {
|
|
2892
|
+
window.removeEventListener('keydown', keyboard);
|
|
2893
|
+
if (dialog && dialog.open) dialog.close();
|
|
2894
|
+
if (previous && previous.isConnected) previous.focus();
|
|
2895
|
+
};
|
|
2896
|
+
}, [open]);
|
|
2897
|
+
|
|
2776
2898
|
function publishConfig(data) {
|
|
2777
2899
|
setConfigState(data);
|
|
2778
2900
|
try { window.dispatchEvent(new CustomEvent('iris-prompt-config-changed', { detail: data })); } catch (_) {}
|
|
@@ -2789,10 +2911,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2789
2911
|
|
|
2790
2912
|
function optimize(mode) {
|
|
2791
2913
|
mode = mode === 'assemble' ? 'assemble' : 'optimize';
|
|
2914
|
+
if (!supportsTarget(target)) { setNote('当前后端尚未支持此优化目标,请更新并重新载入'); return; }
|
|
2792
2915
|
if (mode === 'assemble' && !supportsAssembly) { setNote('当前后端尚未支持只组装,请更新并重新载入'); return; }
|
|
2793
2916
|
if (!draft || !draft.trim()) { setNote('请先在输入框中写入提示词'); return; }
|
|
2794
2917
|
if (refCount) { setNote('当前版本暂不改写含 @ 或 / 结构化引用的草稿,以免引用失效'); return; }
|
|
2795
2918
|
if (phase !== 'plain') { setNote('输入框正忙,请稍后再试'); return; }
|
|
2919
|
+
if (mode === 'optimize' && supportsFaithful && editing === 'faithful' && optimizationText.trim()) {
|
|
2920
|
+
setNote('忠实整理只调整分段;使用本次改写规则前,请选择“改写表达”。'); return;
|
|
2921
|
+
}
|
|
2796
2922
|
if (abortRef.current) abortRef.current.abort();
|
|
2797
2923
|
var controller = new AbortController();
|
|
2798
2924
|
var requestSessionId = props.sessionId;
|
|
@@ -2800,11 +2926,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2800
2926
|
setBusy(true);
|
|
2801
2927
|
setNote(mode === 'assemble' ? '正在组装,不调用模型…' : '正在调用模型优化…这可能产生模型费用');
|
|
2802
2928
|
setResult(null);
|
|
2929
|
+
setSuggestions(null); setSelectedSuggestions([]); setSuggestionUndo([]);
|
|
2803
2930
|
lastWrittenRef.current = null;
|
|
2804
2931
|
promptRequest('optimize', {
|
|
2805
2932
|
text: draft,
|
|
2806
2933
|
target: target,
|
|
2807
2934
|
mode: mode,
|
|
2935
|
+
editing: mode === 'optimize' && supportsFaithful ? editing : undefined,
|
|
2808
2936
|
rules: selectedRules(mode),
|
|
2809
2937
|
sessionId: props.sessionId,
|
|
2810
2938
|
route: selectedRoute || undefined
|
|
@@ -2812,6 +2940,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2812
2940
|
if (abortRef.current !== controller || controller.signal.aborted) return;
|
|
2813
2941
|
if (sessionRef.current !== requestSessionId) throw new Error('会话已切换,请在当前会话重新生成预览');
|
|
2814
2942
|
if (!data.ok || typeof data.optimized !== 'string' || typeof data.original !== 'string') throw new Error('未收到完整优化结果');
|
|
2943
|
+
if (data.original !== draft) throw new Error('结果原稿与本次请求不一致');
|
|
2944
|
+
if (mode === 'optimize' && supportsFaithful && editing === 'faithful'
|
|
2945
|
+
&& (data.editing?.strategy !== 'faithful' || data.editing.coverage !== 'exact' || data.editing.order !== 'preserved')) throw new Error('后端未确认完整的忠实整理结果');
|
|
2815
2946
|
setResult(data);
|
|
2816
2947
|
setEdited(data.optimized);
|
|
2817
2948
|
resultSessionRef.current = requestSessionId;
|
|
@@ -2825,6 +2956,69 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2825
2956
|
});
|
|
2826
2957
|
}
|
|
2827
2958
|
|
|
2959
|
+
function requestSuggestions() {
|
|
2960
|
+
if (!supportsSuggestions || !result || busy || !validEdited()) return;
|
|
2961
|
+
if (resultSessionRef.current !== props.sessionId) { setNote('会话已切换,请重新生成预览'); return; }
|
|
2962
|
+
var snapshot = edited;
|
|
2963
|
+
var previousResult = result;
|
|
2964
|
+
var requestSessionId = props.sessionId;
|
|
2965
|
+
var controller = new AbortController();
|
|
2966
|
+
abortRef.current = controller;
|
|
2967
|
+
setBusy(true); setNote('正在获取可选建议,正文保持原样;这会调用文本模型。');
|
|
2968
|
+
promptRequest('optimize', { text: snapshot, target: result.target, mode: 'optimize', editing: 'suggestions', rules: [],
|
|
2969
|
+
sessionId: props.sessionId, route: selectedRoute || undefined }, controller.signal).then(function (data) {
|
|
2970
|
+
if (abortRef.current !== controller || controller.signal.aborted) return;
|
|
2971
|
+
if (sessionRef.current !== requestSessionId || resultRef.current !== previousResult || editedRef.current !== snapshot) {
|
|
2972
|
+
setNote('预览已变化,本次建议未载入;请针对当前文字重新获取。'); return;
|
|
2973
|
+
}
|
|
2974
|
+
var ids = [];
|
|
2975
|
+
if (!data.ok || data.original !== snapshot || data.optimized !== snapshot || data.target !== previousResult.target || data.editing?.strategy !== 'suggestions'
|
|
2976
|
+
|| !Array.isArray(data.suggestions) || data.suggestions.length > 6 || !Array.isArray(data.questions) || data.questions.length > 6
|
|
2977
|
+
|| !data.suggestions.every(function (item) {
|
|
2978
|
+
if (!item || typeof item.id !== 'string' || !/^suggestion-[1-6]$/.test(item.id) || ids.indexOf(item.id) >= 0
|
|
2979
|
+
|| typeof item.label !== 'string' || !item.label.trim() || item.label.length > 80
|
|
2980
|
+
|| typeof item.text !== 'string' || !item.text.trim() || item.text.length > 600) return false;
|
|
2981
|
+
ids.push(item.id); return true;
|
|
2982
|
+
}) || !data.questions.every(function (text) { return typeof text === 'string' && text.trim() && text.length <= 600; })) throw new Error('未收到完整补充建议');
|
|
2983
|
+
setSuggestions({ source: snapshot, items: data.suggestions, questions: data.questions, adopted: [] });
|
|
2984
|
+
setSelectedSuggestions([]); setSuggestionUndo([]);
|
|
2985
|
+
setNote('建议尚未加入正文;只有采纳所选建议才会改变预览。');
|
|
2986
|
+
}).catch(function (err) {
|
|
2987
|
+
if (abortRef.current !== controller) return;
|
|
2988
|
+
setNote(controller.signal.aborted ? '已取消,原有预览保留' : String(err && err.message || err));
|
|
2989
|
+
}).finally(function () {
|
|
2990
|
+
if (abortRef.current === controller) { abortRef.current = null; setBusy(false); }
|
|
2991
|
+
});
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
function adoptSuggestions() {
|
|
2995
|
+
if (!suggestions || busy || !selectedSuggestions.length) return;
|
|
2996
|
+
var items = suggestions.items.filter(function (item) { return selectedSuggestions.indexOf(item.id) >= 0 && suggestions.adopted.indexOf(item.id) < 0; });
|
|
2997
|
+
if (!items.length) return;
|
|
2998
|
+
var next = edited + '\n\n' + items.map(function (item) { return item.text; }).join('\n\n');
|
|
2999
|
+
if (next.length > 16000) { setNote('采纳后将超过 16000 字符,请减少建议或手动编辑'); return; }
|
|
3000
|
+
var ids = items.map(function (item) { return item.id; });
|
|
3001
|
+
setSuggestionUndo(suggestionUndo.concat([{ before: edited, after: next, ids: ids }]));
|
|
3002
|
+
setSuggestions(Object.assign({}, suggestions, { adopted: suggestions.adopted.concat(ids) }));
|
|
3003
|
+
setEdited(next); editedRef.current = next; setSelectedSuggestions([]);
|
|
3004
|
+
setNote('已将所选建议加入预览,尚未写回;可以撤回上次采纳。');
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
function undoSuggestions() {
|
|
3008
|
+
var last = suggestionUndo[suggestionUndo.length - 1];
|
|
3009
|
+
if (!last || busy) return;
|
|
3010
|
+
if (edited !== last.after) { setNote('预览已被手动修改;请手动移除建议,撤回不会覆盖你的编辑。'); return; }
|
|
3011
|
+
setEdited(last.before); editedRef.current = last.before;
|
|
3012
|
+
setSuggestionUndo(suggestionUndo.slice(0, -1));
|
|
3013
|
+
if (suggestions) setSuggestions(Object.assign({}, suggestions, { adopted: suggestions.adopted.filter(function (id) { return last.ids.indexOf(id) < 0; }) }));
|
|
3014
|
+
setNote('已撤回上次采纳,尚未写回输入框。');
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
function editPreview(value) {
|
|
3018
|
+
setEdited(value); editedRef.current = value;
|
|
3019
|
+
setSuggestions(null); setSelectedSuggestions([]); setSuggestionUndo([]);
|
|
3020
|
+
}
|
|
3021
|
+
|
|
2828
3022
|
function cancel() {
|
|
2829
3023
|
if (abortRef.current) {
|
|
2830
3024
|
abortRef.current.abort();
|
|
@@ -2922,6 +3116,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2922
3116
|
var reasoningLabel = reasoningSetting === 'off-if-supported' ? '支持时关闭' : reasoningSetting === 'provider-default' ? '供应商默认' : reasoningSetting === 'inherit' ? '跟随会话' : reasoningSetting;
|
|
2923
3117
|
var outputBudget = configState && configState.config && configState.config.generation && configState.config.generation.maxOutputTokens || 1200;
|
|
2924
3118
|
var changes = result ? promptChanges(result.original, edited) : null;
|
|
3119
|
+
var targetInfo = PROMPT_TARGET_OPTIONS.find(function (item) { return item.value === target; }) || PROMPT_TARGET_OPTIONS[0];
|
|
3120
|
+
var resultTarget = result && PROMPT_TARGET_OPTIONS.find(function (item) { return item.value === result.target; });
|
|
3121
|
+
var config = configState && configState.config;
|
|
3122
|
+
var targetTemplate = config && config.targets && config.targets[target];
|
|
2925
3123
|
function ruleField(label, value, update, placeholder) {
|
|
2926
3124
|
return React.createElement('label', { className: 'iris-po-field' }, React.createElement('span', {}, label),
|
|
2927
3125
|
React.createElement('textarea', { value: value, disabled: busy || !supportsRules, placeholder: placeholder,
|
|
@@ -2931,20 +3129,43 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2931
3129
|
var panel = null;
|
|
2932
3130
|
if (open) {
|
|
2933
3131
|
panel = React.createElement(React.Fragment, {},
|
|
2934
|
-
React.createElement('
|
|
2935
|
-
|
|
3132
|
+
React.createElement('dialog', { ref: panelRef, tabIndex: -1, className: 'iris-po-panel', role: 'dialog', 'aria-modal': 'true', 'aria-label': 'Iris 提示词优化',
|
|
3133
|
+
onCancel: function (event) { event.preventDefault(); closePanel(); },
|
|
3134
|
+
onClick: function (event) {
|
|
3135
|
+
event.stopPropagation();
|
|
3136
|
+
if (event.target !== event.currentTarget) return;
|
|
3137
|
+
var bounds = event.currentTarget.getBoundingClientRect();
|
|
3138
|
+
if (event.clientX < bounds.left || event.clientX > bounds.right || event.clientY < bounds.top || event.clientY > bounds.bottom) closePanel();
|
|
3139
|
+
} },
|
|
2936
3140
|
React.createElement('div', { className: 'iris-po-head' },
|
|
2937
3141
|
React.createElement('strong', { className: 'iris-po-title' }, React.createElement('span', { className: 'iris-po-mark' }, '🫧'), 'Iris 提示词优化'),
|
|
2938
|
-
React.createElement('button', { className: 'iris-po-close', onClick: closePanel, title: '关闭' }, '✕')),
|
|
2939
|
-
React.createElement('div', { className: 'iris-po-
|
|
2940
|
-
|
|
2941
|
-
React.createElement('
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
React.createElement('
|
|
2947
|
-
|
|
3142
|
+
React.createElement('button', { className: 'iris-po-close', onClick: closePanel, title: '关闭', 'aria-label': '关闭提示词优化' }, '✕')),
|
|
3143
|
+
React.createElement('div', { className: 'iris-po-intro' },
|
|
3144
|
+
React.createElement('div', { className: 'iris-po-muted' }, '按用途整理草稿,先预览和编辑,再决定是否写回。'),
|
|
3145
|
+
React.createElement('div', { className: 'iris-po-principles' },
|
|
3146
|
+
React.createElement('span', {}, '保留原意'), React.createElement('span', {}, '可查看改动'), React.createElement('span', {}, '确认后写回'))),
|
|
3147
|
+
React.createElement('details', { className: 'iris-po-section', open: generalOpen,
|
|
3148
|
+
onToggle: function (event) { generalPair[1](event.currentTarget.open); } },
|
|
3149
|
+
React.createElement('summary', {}, '一般功能',
|
|
3150
|
+
React.createElement('span', { className: 'iris-po-section-caption' }, targetInfo.label + ' · 提示词优化' + (result ? ' · 有预览' : ''))),
|
|
3151
|
+
React.createElement('div', { className: 'iris-po-section-body' },
|
|
3152
|
+
React.createElement('div', { className: 'iris-po-targets', role: 'group', 'aria-label': '优化目标' },
|
|
3153
|
+
PROMPT_TARGET_OPTIONS.map(function (item) {
|
|
3154
|
+
return React.createElement('button', { key: item.value, type: 'button', className: 'iris-po-target',
|
|
3155
|
+
'aria-pressed': target === item.value, value: item.value,
|
|
3156
|
+
disabled: busy || !supportsTarget(item.value), title: supportsTarget(item.value) ? item.description : '当前后端未提供此目标',
|
|
3157
|
+
onClick: function () { setTarget(item.value); } }, item.label);
|
|
3158
|
+
})),
|
|
3159
|
+
React.createElement('div', { className: 'iris-po-muted' }, targetInfo.description),
|
|
3160
|
+
supportsFaithful ? React.createElement('div', { className: 'iris-po-editing', role: 'group', 'aria-label': '编辑方式' },
|
|
3161
|
+
['faithful', 'rewrite'].map(function (strategy) {
|
|
3162
|
+
return React.createElement('label', { key: strategy }, React.createElement('input', { type: 'radio', name: 'iris-prompt-editing',
|
|
3163
|
+
value: strategy, checked: editing === strategy, disabled: busy, onChange: function () { editingPair[1](strategy); } }), strategy === 'faithful' ? '忠实整理(默认)' : '改写表达');
|
|
3164
|
+
}),
|
|
3165
|
+
React.createElement('div', { className: 'iris-po-muted' }, editing === 'faithful' ? '保持原文和句序,只调整分组与分段;不补充细节。' : '按本次规则改写,结果需逐项核对;未授权内容保持原意。')) : null,
|
|
3166
|
+
React.createElement('div', { className: 'iris-po-source' },
|
|
3167
|
+
React.createElement('div', { className: 'iris-po-source-head' }, '当前草稿', React.createElement('span', {}, (draft || '').length + ' 字符')),
|
|
3168
|
+
React.createElement('p', { className: 'iris-po-source-text' }, draft || '先在对话输入框写下你的想法。')),
|
|
2948
3169
|
React.createElement('details', { className: 'iris-po-config' },
|
|
2949
3170
|
React.createElement('summary', {}, '本次规则(可选)'),
|
|
2950
3171
|
React.createElement('div', { className: 'iris-po-rules' },
|
|
@@ -2953,12 +3174,32 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2953
3174
|
ruleField('输出后缀 · 原样加入结果末尾', suffixText, setSuffixText, '例如:禁止添加文字和水印。')),
|
|
2954
3175
|
React.createElement('div', { className: 'iris-po-muted' }, supportsRules ? '非空内容表示本次选择;只组装保留原稿,只添加前后缀。刷新后重置,不修改 JSON 配置。' : '当前后端尚未支持规则,请更新后重新载入。')),
|
|
2955
3176
|
React.createElement('div', { className: 'iris-po-actions' },
|
|
2956
|
-
React.createElement('button', { className: 'iris-po-primary', disabled: busy || !draft || !draft.trim() || phase !== 'plain' || refCount > 0, onClick: function () { optimize('optimize'); } }, busy ? '处理中…' : '智能优化'),
|
|
3177
|
+
React.createElement('button', { className: 'iris-po-primary', disabled: busy || !draft || !draft.trim() || phase !== 'plain' || refCount > 0, onClick: function () { optimize('optimize'); } }, busy ? '处理中…' : supportsFaithful ? editing === 'faithful' ? '忠实整理' : '改写表达' : '智能优化'),
|
|
2957
3178
|
React.createElement('button', { className: 'iris-po-btn', disabled: busy || !supportsAssembly || !draft || !draft.trim() || phase !== 'plain' || refCount > 0, onClick: function () { optimize('assemble'); } }, '只组装(不调用模型)'),
|
|
2958
3179
|
busy ? React.createElement('button', { className: 'iris-po-btn', onClick: cancel }, '取消') : null),
|
|
3180
|
+
React.createElement('div', { className: 'iris-po-muted' }, '智能优化调用文本模型,可能产生费用;只组装原稿与前后缀。'),
|
|
2959
3181
|
result ? React.createElement('div', { className: 'iris-po-result' },
|
|
2960
|
-
React.createElement('div', { className: 'iris-po-result-label' }, '结果预览 · 可编辑'),
|
|
2961
|
-
React.createElement('textarea', { 'aria-label': '结果预览', value: edited, onChange: function (event) {
|
|
3182
|
+
React.createElement('div', { className: 'iris-po-result-label' }, '结果预览 · ' + (resultTarget ? resultTarget.label + ' · ' : '') + '可编辑 · ' + edited.length + ' 字符'),
|
|
3183
|
+
React.createElement('textarea', { 'aria-label': '结果预览', value: edited, onChange: function (event) { editPreview(event.target.value); } }),
|
|
3184
|
+
result.editing?.strategy === 'faithful' ? React.createElement('div', { className: 'iris-po-muted' }, '本次整理保留了全部原文片段与句序;手动编辑或采纳建议后,请重新核对内容。') : null,
|
|
3185
|
+
supportsSuggestions ? React.createElement('details', { className: 'iris-po-config' },
|
|
3186
|
+
React.createElement('summary', {}, '可选补充与待确认'),
|
|
3187
|
+
React.createElement('div', { className: 'iris-po-muted' }, '建议独立生成,默认未采纳;不会自动加入正文或写回。每次获取会调用文本模型。'),
|
|
3188
|
+
React.createElement('button', { className: 'iris-po-btn', disabled: busy || !edited.trim(), onClick: requestSuggestions }, '获取补充建议'),
|
|
3189
|
+
suggestions ? React.createElement('div', {},
|
|
3190
|
+
suggestions.items.map(function (item) {
|
|
3191
|
+
var adopted = suggestions.adopted.indexOf(item.id) >= 0;
|
|
3192
|
+
return React.createElement('div', { className: 'iris-po-suggestion', key: item.id },
|
|
3193
|
+
React.createElement('label', {}, React.createElement('input', { type: 'checkbox', checked: adopted || selectedSuggestions.indexOf(item.id) >= 0, disabled: busy || adopted,
|
|
3194
|
+
onChange: function (event) { setSelectedSuggestions(event.target.checked ? selectedSuggestions.concat([item.id]) : selectedSuggestions.filter(function (id) { return id !== item.id; })); } }), item.label + (adopted ? ' · 已加入预览' : '')),
|
|
3195
|
+
React.createElement('p', {}, item.text));
|
|
3196
|
+
}),
|
|
3197
|
+
!suggestions.items.length ? React.createElement('div', { className: 'iris-po-muted' }, '没有额外补充建议。') : null,
|
|
3198
|
+
suggestions.questions.length ? React.createElement('div', { className: 'iris-po-suggestion' }, React.createElement('strong', {}, '待确认 · 尚未作为要求'),
|
|
3199
|
+
suggestions.questions.map(function (question, index) { return React.createElement('p', { key: index }, question); })) : null,
|
|
3200
|
+
React.createElement('div', { className: 'iris-po-actions' },
|
|
3201
|
+
React.createElement('button', { className: 'iris-po-btn', disabled: busy || !selectedSuggestions.length, onClick: adoptSuggestions }, '采纳所选建议'),
|
|
3202
|
+
React.createElement('button', { className: 'iris-po-btn', disabled: busy || !suggestionUndo.length, onClick: undoSuggestions }, '撤回上次采纳'))) : null) : null,
|
|
2962
3203
|
React.createElement('details', { className: 'iris-po-config' }, React.createElement('summary', {}, '原文与改动'),
|
|
2963
3204
|
React.createElement('textarea', { 'aria-label': '优化前原文', readOnly: true, value: result.original }),
|
|
2964
3205
|
React.createElement('div', { className: 'iris-po-muted' }, '以下折叠相同首尾,显示中间替换区:'),
|
|
@@ -2977,9 +3218,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2977
3218
|
React.createElement('button', { className: 'iris-po-btn', onClick: copyResult }, '复制'),
|
|
2978
3219
|
React.createElement('button', { className: 'iris-po-btn', disabled: busy, onClick: function () { optimize(result.mode); } }, '重新生成预览'),
|
|
2979
3220
|
React.createElement('button', { className: 'iris-po-btn', onClick: function () { replaceDraft(result.original); } }, '恢复原文'))) : null,
|
|
2980
|
-
note ? React.createElement('div', { className: 'iris-po-note' }, note) : null,
|
|
3221
|
+
note ? React.createElement('div', { className: 'iris-po-note', role: 'status' }, note) : null)),
|
|
2981
3222
|
React.createElement('details', { className: 'iris-po-config' },
|
|
2982
|
-
React.createElement('summary', {}, '
|
|
3223
|
+
React.createElement('summary', {}, '指令与模型设置'),
|
|
3224
|
+
React.createElement('div', { className: 'iris-po-route', title: routeLabel }, '模型:' + routeLabel + ' · 思考:' + reasoningLabel + ' · 输出:' + outputBudget + ' tokens'),
|
|
3225
|
+
React.createElement('details', {}, React.createElement('summary', {}, '查看当前功能的优化指令'),
|
|
3226
|
+
React.createElement('div', { className: 'iris-po-template' }, config && config.systemPrompt || '配置尚未载入'),
|
|
3227
|
+
React.createElement('div', { className: 'iris-po-template' }, targetTemplate || '当前后端未提供此目标模板')),
|
|
3228
|
+
React.createElement('div', { className: 'iris-po-result-label' }, 'JSON 配置'),
|
|
2983
3229
|
React.createElement('div', { className: 'iris-po-muted' }, '可导出后修改 systemPrompt、targets、route 和 generation,再重新导入。'),
|
|
2984
3230
|
React.createElement('div', { className: 'iris-po-actions' },
|
|
2985
3231
|
React.createElement('button', { className: 'iris-po-btn', onClick: exportConfig }, '导出'),
|
package/lib/config.js
CHANGED
|
@@ -66,7 +66,7 @@ function normalizeStoredProvider(provider) {
|
|
|
66
66
|
const changed = provider.mediaProtocol !== selected.mediaProtocol
|
|
67
67
|
|| (provider.protocolInferred === true) !== selected.protocolInferred;
|
|
68
68
|
Object.assign(provider, selected);
|
|
69
|
-
return
|
|
69
|
+
return changed;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
export function irisHome() {
|
|
@@ -565,10 +565,19 @@ export function setProviderModels(id, modelEntries) {
|
|
|
565
565
|
export function mergeDiscoveredModels(id, modelEntries) {
|
|
566
566
|
const provider = load().providers.find((p) => p.id === id);
|
|
567
567
|
if (!provider) return null;
|
|
568
|
-
const merged = new Map(models.providerModels(provider).map((m) =>
|
|
568
|
+
const merged = new Map(models.providerModels(provider).map((m) => {
|
|
569
|
+
const raw = provider.models?.find((entry) => String(typeof entry === 'string' ? entry : entry?.id || '').trim() === m.id);
|
|
570
|
+
// 读取投影的 [] 不能伪造成用户明确清空;未标注条目以后仍可接收可信元数据。
|
|
571
|
+
if (!Array.isArray(raw?.capabilities) && !models.hasConfiguredModelCapabilities(provider, m.id)) {
|
|
572
|
+
const entry = { ...m };
|
|
573
|
+
delete entry.capabilities;
|
|
574
|
+
return [m.id, entry];
|
|
575
|
+
}
|
|
576
|
+
return [m.id, m];
|
|
577
|
+
}));
|
|
569
578
|
for (const model of modelEntries) {
|
|
570
579
|
const previous = merged.get(model.id);
|
|
571
|
-
if (previous
|
|
580
|
+
if (previous && models.hasConfiguredModelCapabilities(provider, model.id)) continue;
|
|
572
581
|
merged.set(model.id, { ...previous, ...model, source: 'discovered' });
|
|
573
582
|
}
|
|
574
583
|
return setProviderModels(id, [...merged.values()]);
|
|
@@ -631,7 +640,7 @@ export function addProviderModel(id, modelId, capabilities) {
|
|
|
631
640
|
if (!Array.isArray(p.models)) p.models = [];
|
|
632
641
|
let entry = findModelEntry(p, name);
|
|
633
642
|
if (!entry) {
|
|
634
|
-
entry = { id: name, capabilities: Array.isArray(capabilities) ? capabilities :
|
|
643
|
+
entry = { id: name, capabilities: Array.isArray(capabilities) ? capabilities : undefined, source: 'manual' };
|
|
635
644
|
p.models.push(entry);
|
|
636
645
|
} else if (Array.isArray(capabilities)) {
|
|
637
646
|
entry.capabilities = capabilities;
|