@mokuyoaxis/dsh-iris 0.1.1 → 0.1.3
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 +75 -1
- package/README.en.md +259 -0
- package/README.md +70 -7
- package/bin/dsh-iris.js +23 -0
- package/cordis.patch.yml +1 -1
- package/docs/ARCHITECTURE.md +81 -0
- package/docs/FAULT_INJECTION.md +26 -0
- package/docs/PROVIDER_SUBMISSION_CONTRACT.md +60 -0
- package/docs/ROADMAP.md +25 -8
- package/docs/SECURITY.md +53 -0
- package/docs/TASK_SEMANTICS.md +147 -0
- package/docs/assets/diagrams/iris-architecture.drawio +1 -0
- package/docs/assets/diagrams/iris-architecture.png +0 -0
- package/docs/assets/diagrams/iris-architecture.svg +63 -0
- package/docs/assets/diagrams/iris-task-lifecycle.drawio +1 -0
- package/docs/assets/diagrams/iris-task-lifecycle.png +0 -0
- package/docs/assets/diagrams/iris-task-lifecycle.svg +64 -0
- package/docs/assets/diagrams/iris-workflow-compose.drawio +1 -0
- package/docs/assets/diagrams/iris-workflow-compose.png +0 -0
- package/docs/assets/diagrams/iris-workflow-compose.svg +41 -0
- package/docs/assets/examples/iris-flower-generated.webp +0 -0
- package/docs/assets/screenshots/image-task-succeeded.webp +0 -0
- package/docs/assets/screenshots/prompt-general-after.webp +0 -0
- package/docs/assets/screenshots/prompt-general-before.webp +0 -0
- package/docs/assets/screenshots/prompt-general-preview.webp +0 -0
- package/docs/assets/screenshots/prompt-image-before.webp +0 -0
- package/docs/assets/screenshots/prompt-image-preview.webp +0 -0
- package/docs/releases/0.1.2.md +26 -0
- package/docs/releases/0.1.3.md +30 -0
- package/docs/screenshots.md +34 -0
- package/lib/actions.js +609 -175
- package/lib/api.js +122 -18
- package/lib/bundled-skills.js +62 -0
- package/lib/client.js +460 -22
- package/lib/doctor.js +243 -0
- package/lib/index.js +57 -46
- package/lib/prompt-optimizer-config.js +170 -0
- package/lib/prompt-optimizer.js +244 -0
- package/lib/provider-contract.js +234 -0
- package/lib/task-semantics.js +209 -0
- package/lib/tasks.js +473 -29
- package/package.json +13 -4
- package/screenshots.json +6 -0
- package/user_guide.md +84 -3
package/lib/client.js
CHANGED
|
@@ -37,6 +37,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
37
37
|
'.iris-wb-badge { font-size: 11px; padding: 1px 6px; border-radius: 4px; background: var(--dsw-alias-bg-layer-2, #32323c); }',
|
|
38
38
|
'.iris-wb-badge.ok { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
39
39
|
'.iris-wb-badge.err { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
40
|
+
'.iris-wb-badge.warn { color: var(--dsw-alias-state-warn-primary, #d9a941); }',
|
|
40
41
|
'.iris-wb-progress { margin-top: 6px; }',
|
|
41
42
|
'.iris-wb-bar { height: 5px; border-radius: 3px; background: var(--dsw-alias-bg-layer-2, #32323c); overflow: hidden; }',
|
|
42
43
|
'.iris-wb-bar i { display: block; height: 100%; background: var(--dsw-alias-brand-primary, #7aa2f7); }',
|
|
@@ -111,7 +112,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
111
112
|
'.iris-bubble.lit .iris-bubble-btn { border-color: var(--dsw-alias-state-success-primary, #6fcf6f); box-shadow: 0 0 14px var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
112
113
|
'.iris-bubble.dim .iris-bubble-btn { opacity: 0.55; border-color: var(--dsw-alias-border-l1, #3a3a44); }',
|
|
113
114
|
'.iris-bubble-badge { position: absolute; top: -2px; right: -2px; min-width: 16px; height: 16px; border-radius: 8px; background: var(--dsw-alias-state-warn-primary, #d9a941); color: #1a1a1e; font-size: 10px; font-weight: 700; display: flex; align-items: center; justify-content: center; padding: 0 3px; }',
|
|
114
|
-
'.iris-bubble-panel { position: fixed; z-index: 9998; width: 360px; max-height: min(520px, 70vh); overflow: auto; background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 12px; padding: 12px 14px; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45); }',
|
|
115
|
+
'.iris-bubble-panel { position: fixed; z-index: 9998; width: 360px; max-height: min(520px, 70vh); overflow: auto; background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 12px; padding: 12px 14px; font-size: 12px; line-height: 1.45; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45); }',
|
|
115
116
|
'.iris-bubble-panel-head { position: absolute; top: 6px; right: 6px; z-index: 2; }',
|
|
116
117
|
'.iris-bubble-close { border: none; background: transparent; color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 14px; cursor: pointer; padding: 4px 6px; }',
|
|
117
118
|
'.iris-bubble-close:hover { color: var(--dsw-alias-label-primary, #e6e6e6); }',
|
|
@@ -160,7 +161,48 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
160
161
|
'.iris-ff-list { display: grid; gap: 2px; max-height: 160px; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 6px; padding: 4px; background: var(--dsw-alias-bg-layer-2, #32323c); }',
|
|
161
162
|
'.iris-ff-item { display: flex; align-items: center; gap: 6px; font-size: 11px; cursor: pointer; padding: 2px 4px; border-radius: 4px; }',
|
|
162
163
|
'.iris-ff-item:hover { background: var(--dsw-alias-bg-layer-1, #26262e); }',
|
|
163
|
-
'.iris-ff-item .iris-ff-src { color: var(--dsw-alias-brand-primary, #7aa2f7); font-size: 10px; }'
|
|
164
|
+
'.iris-ff-item .iris-ff-src { color: var(--dsw-alias-brand-primary, #7aa2f7); font-size: 10px; }',
|
|
165
|
+
/* 对话框级提示词优化器:桌面浮层 + 窄屏底部 Sheet */
|
|
166
|
+
'.iris-po-wrap { position: relative; display: inline-flex; flex: 0 0 auto; }',
|
|
167
|
+
'.iris-po-trigger { appearance: none; -webkit-appearance: none; width: 30px; height: 30px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: 0; outline: 0; background: transparent; color: inherit; font-size: 19px; line-height: 1; cursor: pointer; box-shadow: none; filter: drop-shadow(0 3px 7px rgba(91,113,220,.22)); transition: transform .18s ease, filter .18s ease, opacity .18s ease; }',
|
|
168
|
+
'.iris-po-trigger:hover { transform: translateY(-2px) scale(1.10); filter: drop-shadow(0 5px 10px rgba(112,137,255,.42)); }',
|
|
169
|
+
'.iris-po-trigger:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #7aa2f7); outline-offset: 2px; }',
|
|
170
|
+
'.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; }',
|
|
171
|
+
'.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(440px, calc(100vw - 28px)); max-height: min(680px, 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: 13px; background-color: rgba(35,36,47,.84); background: radial-gradient(circle at 10% -5%, rgba(132,160,255,.24), transparent 42%), color-mix(in srgb, var(--dsw-alias-bg-layer-1, #26262e) 82%, transparent); color: var(--dsw-alias-label-primary, #eeeeF2); border: 1px solid rgba(150,164,215,.25); border-radius: 20px; box-shadow: 0 24px 70px rgba(0,0,0,.48), 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; }',
|
|
172
|
+
'.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); }',
|
|
173
|
+
'.iris-po-title { display: inline-flex; align-items: center; gap: 9px; font-size: 14px; letter-spacing: .01em; }',
|
|
174
|
+
'.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)); }',
|
|
175
|
+
'.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; }',
|
|
176
|
+
'.iris-po-close:hover { background: rgba(255,255,255,.10); color: var(--dsw-alias-label-primary, #eeeef2); }',
|
|
177
|
+
'.iris-po-muted, .iris-po-route, .iris-po-note { font-size: 11px; line-height: 1.55; color: var(--dsw-alias-label-secondary, #a6a6b2); word-break: break-word; }',
|
|
178
|
+
'.iris-po-route { padding: 7px 10px; border-radius: 10px; background: rgba(255,255,255,.045); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
|
179
|
+
'.iris-po-note { padding: 8px 10px; border-radius: 10px; color: var(--dsw-alias-state-warn-primary, #dfbd69); background: rgba(217,169,65,.08); }',
|
|
180
|
+
'.iris-po-field { display: grid; gap: 6px; font-size: 11px; color: var(--dsw-alias-label-secondary, #a6a6b2); }',
|
|
181
|
+
'.iris-po-field select, .iris-po-result textarea { width: 100%; box-sizing: border-box; background: rgba(255,255,255,.055); color: var(--dsw-alias-label-primary, #eeeef2); border: 1px solid rgba(150,164,215,.20); border-radius: 11px; padding: 9px 11px; font: inherit; font-size: 12px; outline: none; transition: border-color .15s, background .15s, box-shadow .15s; }',
|
|
182
|
+
'.iris-po-field select: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); }',
|
|
183
|
+
'.iris-po-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }',
|
|
184
|
+
'.iris-po-btn, .iris-po-primary { min-height: 34px; border: 1px solid rgba(150,164,215,.20); border-radius: 10px; padding: 6px 11px; font: inherit; font-size: 11px; cursor: pointer; transition: transform .15s, background .15s, border-color .15s; }',
|
|
185
|
+
'.iris-po-btn { background: rgba(255,255,255,.045); color: var(--dsw-alias-label-secondary, #b0b0bb); }',
|
|
186
|
+
'.iris-po-btn:hover { background: rgba(255,255,255,.085); color: var(--dsw-alias-label-primary, #eeeef2); border-color: rgba(150,174,255,.50); }',
|
|
187
|
+
'.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); }',
|
|
188
|
+
'.iris-po-primary:hover { transform: translateY(-1px); box-shadow: 0 8px 22px rgba(76,92,188,.32); }',
|
|
189
|
+
'.iris-po-primary:disabled, .iris-po-btn:disabled { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }',
|
|
190
|
+
'.iris-po-result { display: grid; gap: 8px; padding: 12px; border: 1px solid rgba(137,155,218,.18); border-radius: 15px; background: rgba(10,12,20,.16); }',
|
|
191
|
+
'.iris-po-result-label { font-size: 11px; font-weight: 650; letter-spacing: .02em; }',
|
|
192
|
+
'.iris-po-result textarea { min-height: 156px; max-height: 34dvh; resize: vertical; line-height: 1.58; }',
|
|
193
|
+
'.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; }',
|
|
194
|
+
'.iris-po-config summary { cursor: pointer; color: var(--dsw-alias-label-secondary, #a6a6b2); }',
|
|
195
|
+
'.iris-po-config[open] summary { margin-bottom: 9px; color: var(--dsw-alias-label-primary, #eeeef2); }',
|
|
196
|
+
'.iris-po-config .iris-po-actions { margin: 9px 0; }',
|
|
197
|
+
'.iris-po-btn.danger:hover { color: var(--dsw-alias-state-error-primary, #eb7d86); border-color: rgba(235,125,134,.55); background: rgba(235,125,134,.08); }',
|
|
198
|
+
'.iris-po-file { display: none; }',
|
|
199
|
+
'.iris-po-disable { justify-self: start; border: 0; padding: 2px 0; background: transparent; color: var(--dsw-alias-label-tertiary, #777986); font: inherit; font-size: 10px; cursor: pointer; }',
|
|
200
|
+
'.iris-po-disable:hover { color: var(--dsw-alias-state-error-primary, #eb7d86); }',
|
|
201
|
+
'@keyframes iris-po-fade { from { opacity: 0; } to { opacity: 1; } }',
|
|
202
|
+
'@keyframes iris-po-rise { from { opacity: 0; transform: translateY(12px) scale(.985); } to { opacity: 1; transform: translateY(0) scale(1); } }',
|
|
203
|
+
'@media (max-width: 640px) { .iris-po-backdrop { background: rgba(6,8,14,.48); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); } .iris-po-panel { left: max(8px, env(safe-area-inset-left)); right: max(8px, env(safe-area-inset-right)); bottom: max(8px, env(safe-area-inset-bottom)); width: auto; max-height: calc(100dvh - max(16px, env(safe-area-inset-top))); padding: 16px; gap: 11px; border-radius: 22px; } .iris-po-head { top: -16px; margin: -16px -16px 0; padding: 14px 16px 10px; } .iris-po-btn, .iris-po-primary { min-height: 42px; flex: 1 1 auto; } .iris-po-actions .iris-po-primary:first-child { flex-basis: 100%; } .iris-po-result textarea { min-height: 122px; max-height: 28dvh; resize: none; } .iris-po-route { white-space: normal; } }',
|
|
204
|
+
'@media (max-width: 360px) { .iris-po-panel { left: 4px; right: 4px; bottom: max(4px, env(safe-area-inset-bottom)); padding: 13px; border-radius: 18px; } .iris-po-head { top: -13px; margin: -13px -13px 0; padding: 12px 13px 8px; } .iris-po-actions { gap: 6px; } }',
|
|
205
|
+
'@media (prefers-reduced-motion: reduce) { .iris-po-panel, .iris-po-backdrop { animation: none; } .iris-po-trigger, .iris-po-btn, .iris-po-primary { transition: none; } }',
|
|
164
206
|
].join('\n');
|
|
165
207
|
|
|
166
208
|
function adoptStyles() {
|
|
@@ -211,6 +253,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
211
253
|
if (sharedState) setState(sharedState);
|
|
212
254
|
if (!sharedTimer && !sharedSource) {
|
|
213
255
|
function apply(data) {
|
|
256
|
+
if (sharedState && data && sharedState.stateEpoch && data.stateEpoch === sharedState.stateEpoch
|
|
257
|
+
&& Number(data.stateRevision || 0) <= Number(sharedState.stateRevision || 0)) return;
|
|
214
258
|
sharedState = data;
|
|
215
259
|
for (var i = 0; i < sharedListeners.length; i++) {
|
|
216
260
|
sharedListeners[i](data);
|
|
@@ -250,6 +294,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
250
294
|
return sharedState;
|
|
251
295
|
}
|
|
252
296
|
|
|
297
|
+
/** 功能卡就绪态直接来自共享 Iris 状态,不再等用户点开卡片后才加载。 */
|
|
298
|
+
function capabilityReady(state, capability) {
|
|
299
|
+
if (!capability) return true;
|
|
300
|
+
var providers = (state && state.providers) || [];
|
|
301
|
+
return providers.some(function (p) {
|
|
302
|
+
return p.enabled && p.apiKeyHint && Array.isArray(p.capabilities) && p.capabilities.indexOf(capability) >= 0;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
253
306
|
/* ---- 任务详情抽屉:按需拉取 /iris/api/task/:id ---- */
|
|
254
307
|
function useTaskDetail(taskId) {
|
|
255
308
|
var pair = React.useState(null);
|
|
@@ -297,10 +350,38 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
297
350
|
return '更早';
|
|
298
351
|
}
|
|
299
352
|
|
|
353
|
+
var TASK_STATE_LABELS = {
|
|
354
|
+
queued: '排队中', running: '运行中', watching_paused: '观察暂停', needs_attention: '需要确认',
|
|
355
|
+
artifact_unavailable: '结果待取回', succeeded: '成功', failed: '失败', canceled: '已取消'
|
|
356
|
+
};
|
|
357
|
+
function taskState(task) {
|
|
358
|
+
if (task && task.userState) return task.userState;
|
|
359
|
+
if (task && task.status === 'succeeded') return 'succeeded';
|
|
360
|
+
if (task && task.status === 'failed') return 'failed';
|
|
361
|
+
if (task && task.status === 'canceled') return 'canceled';
|
|
362
|
+
return 'running';
|
|
363
|
+
}
|
|
364
|
+
function taskStateLabel(task) {
|
|
365
|
+
var state = taskState(task);
|
|
366
|
+
return TASK_STATE_LABELS[state] || state;
|
|
367
|
+
}
|
|
368
|
+
function taskStateClass(task) {
|
|
369
|
+
var state = taskState(task);
|
|
370
|
+
if (state === 'succeeded') return 'ok';
|
|
371
|
+
if (state === 'failed') return 'err';
|
|
372
|
+
if (state === 'watching_paused' || state === 'needs_attention' || state === 'artifact_unavailable') return 'warn';
|
|
373
|
+
return '';
|
|
374
|
+
}
|
|
375
|
+
function attentionDispositionLabel(task) {
|
|
376
|
+
var disposition = task && task.attentionDisposition;
|
|
377
|
+
if (!disposition || disposition.status !== 'acknowledged') return '';
|
|
378
|
+
return disposition.reason === 'retried' ? '已通过重试处理' : '提醒已读';
|
|
379
|
+
}
|
|
380
|
+
|
|
300
381
|
/* 紧凑任务行:泡泡「任务」标签用,单行摘要 */
|
|
301
382
|
function taskRowMini(task, selected, onSelect) {
|
|
302
|
-
var st = task
|
|
303
|
-
var label =
|
|
383
|
+
var st = taskStateClass(task);
|
|
384
|
+
var label = taskStateLabel(task);
|
|
304
385
|
return React.createElement('div', {
|
|
305
386
|
key: task.id, className: 'iris-task-mini' + (selected ? ' selected' : ''),
|
|
306
387
|
onClick: function () { onSelect(selected ? null : task.id); }, title: '点击展开详情'
|
|
@@ -311,6 +392,47 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
311
392
|
React.createElement('span', { className: 'iris-wb-muted' }, task.createdAt ? new Date(task.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : ''));
|
|
312
393
|
}
|
|
313
394
|
|
|
395
|
+
function TaskRecoveryActions({ detail }) {
|
|
396
|
+
var pair = React.useState('');
|
|
397
|
+
var note = pair[0];
|
|
398
|
+
var setNote = pair[1];
|
|
399
|
+
var busyPair = React.useState(false);
|
|
400
|
+
var busy = busyPair[0];
|
|
401
|
+
var setBusy = busyPair[1];
|
|
402
|
+
var state = taskState(detail);
|
|
403
|
+
var attentionState = ['watching_paused', 'needs_attention', 'artifact_unavailable'].indexOf(state) >= 0;
|
|
404
|
+
var acknowledged = detail.attentionDisposition && detail.attentionDisposition.status === 'acknowledged';
|
|
405
|
+
var canAcknowledge = detail.schemaVersion === 2 && attentionState && !acknowledged;
|
|
406
|
+
var canRestore = detail.schemaVersion === 2 && attentionState && acknowledged;
|
|
407
|
+
var canReobserve = detail.schemaVersion === 2 && !acknowledged && detail.acceptance === 'accepted' && detail.remoteTaskId
|
|
408
|
+
&& detail.outcome !== 'succeeded' && detail.outcome !== 'failed' && detail.outcome !== 'canceled';
|
|
409
|
+
var canRedeliver = detail.schemaVersion === 2 && !acknowledged && state === 'artifact_unavailable' && detail.remoteTaskId;
|
|
410
|
+
var canRetry = detail.schemaVersion === 2 && !acknowledged && ['needs_attention', 'failed', 'canceled'].indexOf(state) >= 0
|
|
411
|
+
&& (detail.cap === 'image' || (detail.cap === 'video' && detail.mode === 't2v'));
|
|
412
|
+
if (!canAcknowledge && !canRestore && !canReobserve && !canRedeliver && !canRetry) return null;
|
|
413
|
+
function run(action, body, confirmMessage) {
|
|
414
|
+
if (confirmMessage && !window.confirm(confirmMessage)) return;
|
|
415
|
+
setBusy(true);
|
|
416
|
+
setNote('处理中…');
|
|
417
|
+
postAction(action, body).then(function (r) {
|
|
418
|
+
setNote(r.ok ? ((r.d && r.d.text) || '操作已接受') : ('失败:' + ((r.d && r.d.error) || '未知错误')));
|
|
419
|
+
}).catch(function (error) { setNote('失败:' + String((error && error.message) || error)); })
|
|
420
|
+
.finally(function () { setBusy(false); });
|
|
421
|
+
}
|
|
422
|
+
return React.createElement('div', { className: 'iris-wb-row', onClick: function (e) { e.stopPropagation(); } },
|
|
423
|
+
canReobserve ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy, onClick: function () { run('task_reobserve', { task_id: detail.id }); } }, '重新观察') : null,
|
|
424
|
+
canRedeliver ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy, onClick: function () { run('task_redeliver', { task_id: detail.id }); } }, '重新交付') : null,
|
|
425
|
+
canRetry ? React.createElement('button', { className: 'iris-pm-btn danger', disabled: busy, onClick: function () {
|
|
426
|
+
run('task_manual_retry', { task_id: detail.id, confirm_duplicate_charge: true },
|
|
427
|
+
'远端任务可能已经产生,本次会创建新的生成任务,可能重复计费。确认知情重试?');
|
|
428
|
+
} }, '知情重试') : null,
|
|
429
|
+
canAcknowledge ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy,
|
|
430
|
+
onClick: function () { run('task_ack_attention', { task_id: detail.id }); } }, '标为已读') : null,
|
|
431
|
+
canRestore ? React.createElement('button', { className: 'iris-pm-btn', disabled: busy,
|
|
432
|
+
onClick: function () { run('task_restore_attention', { task_id: detail.id }); } }, '恢复提醒') : null,
|
|
433
|
+
note ? React.createElement('span', { className: 'iris-clean-note' }, note) : null);
|
|
434
|
+
}
|
|
435
|
+
|
|
314
436
|
/* ---- 任务详情抽屉:点击任务卡片行展开 ---- */
|
|
315
437
|
function TaskDetailDrawer({ taskId }) {
|
|
316
438
|
var detail = useTaskDetail(taskId);
|
|
@@ -320,6 +442,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
320
442
|
var el = [];
|
|
321
443
|
el.push(React.createElement('div', { key: 'k', className: 'iris-wb-k' }, '提示词'));
|
|
322
444
|
el.push(React.createElement('div', { key: 'p', className: 'iris-wb-prompt' }, detail.prompt || '(空)'));
|
|
445
|
+
if (Number(detail.schemaVersion) === 1) {
|
|
446
|
+
el.push(React.createElement('div', { key: 'legacy', className: 'iris-clean-note' },
|
|
447
|
+
'旧版任务记录 · 仅支持查看。由于缺少可靠的远端受理与交付事实,不提供重新观察、重新交付或知情重试;0.1.3 新任务将使用可信任务语义。'));
|
|
448
|
+
}
|
|
449
|
+
var dispositionLabel = attentionDispositionLabel(detail);
|
|
450
|
+
if (dispositionLabel) {
|
|
451
|
+
var related = detail.attentionDisposition.relatedTaskId ? ' · 关联任务 ' + detail.attentionDisposition.relatedTaskId : '';
|
|
452
|
+
el.push(React.createElement('div', { key: 'attention-disposition', className: 'iris-clean-note' }, dispositionLabel + related));
|
|
453
|
+
}
|
|
323
454
|
if (detail.error) {
|
|
324
455
|
el.push(React.createElement('div', { key: 'ek', className: 'iris-wb-k' }, '错误'));
|
|
325
456
|
el.push(React.createElement('div', { key: 'e', className: 'iris-wb-err' }, detail.error));
|
|
@@ -327,11 +458,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
327
458
|
var kv = [
|
|
328
459
|
['ID', detail.id],
|
|
329
460
|
['能力', detail.cap],
|
|
330
|
-
['状态', detail
|
|
461
|
+
['状态', taskStateLabel(detail)],
|
|
331
462
|
['模型', detail.model],
|
|
332
463
|
['供应商', detail.providerName],
|
|
333
464
|
['模式', detail.mode || '—'],
|
|
334
465
|
['远端任务', detail.remoteTaskId || '—'],
|
|
466
|
+
['来源任务', detail.retryOf || '—'],
|
|
467
|
+
['后续重试', (detail.manualRetries || []).map(function (item) { return item.taskId; }).join('、') || '—'],
|
|
335
468
|
['发起', detail.createdAt ? fmtTime(detail.createdAt) : '—'],
|
|
336
469
|
['完成', detail.finishedAt ? fmtTime(detail.finishedAt) : '—'],
|
|
337
470
|
['耗时', detail.elapsedMs ? fmtElapsed(detail.elapsedMs) : '—']
|
|
@@ -368,24 +501,25 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
368
501
|
return React.createElement('span', { key: a.attachmentId, className: 'iris-wb-kv' }, a.attachmentId + (a.file ? ' (' + a.file + ')' : ''));
|
|
369
502
|
})));
|
|
370
503
|
}
|
|
504
|
+
el.push(React.createElement(TaskRecoveryActions, { key: 'recovery', detail: detail }));
|
|
371
505
|
return React.createElement('div', { className: 'iris-wb-drawer' }, ...el);
|
|
372
506
|
}
|
|
373
507
|
|
|
374
508
|
function taskRow(task, isRunning, selected, onSelect) {
|
|
375
|
-
var
|
|
376
|
-
|
|
377
|
-
badge = React.createElement('span', { className: 'iris-wb-badge ok' }, '成功');
|
|
378
|
-
} else if (task.status === 'failed') {
|
|
379
|
-
badge = React.createElement('span', { className: 'iris-wb-badge err' }, '失败');
|
|
380
|
-
} else {
|
|
381
|
-
badge = React.createElement('span', { className: 'iris-wb-badge' }, task.status);
|
|
382
|
-
}
|
|
509
|
+
var stateClass = taskStateClass(task);
|
|
510
|
+
var badge = React.createElement('span', { className: 'iris-wb-badge' + (stateClass ? ' ' + stateClass : '') }, taskStateLabel(task));
|
|
383
511
|
var cells = [
|
|
384
512
|
React.createElement('span', { className: 'iris-wb-kv' }, task.cap),
|
|
385
513
|
badge,
|
|
386
514
|
React.createElement('span', { className: 'iris-wb-kv' }, task.model || ''),
|
|
387
515
|
React.createElement('span', { className: 'iris-wb-muted' }, String(task.prompt || '').slice(0, 60))
|
|
388
516
|
];
|
|
517
|
+
if (Number(task.schemaVersion) === 1) {
|
|
518
|
+
cells.push(React.createElement('span', { className: 'iris-wb-badge', title: '旧版记录缺少可靠的远端受理与交付事实,仅支持查看' }, '旧任务 · 只读'));
|
|
519
|
+
}
|
|
520
|
+
var dispositionLabel = attentionDispositionLabel(task);
|
|
521
|
+
if (dispositionLabel) cells.push(React.createElement('span', { className: 'iris-wb-badge ok' }, dispositionLabel));
|
|
522
|
+
if (task.retryOf) cells.push(React.createElement('span', { className: 'iris-wb-badge' }, '重试任务'));
|
|
389
523
|
if (task.error) cells.push(React.createElement('span', { className: 'iris-wb-badge err' }, String(task.error).slice(0, 40)));
|
|
390
524
|
var links = (task.media || []).map(function (m) {
|
|
391
525
|
return React.createElement('a', {
|
|
@@ -650,6 +784,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
650
784
|
var modelPair = React.useState(null); // { assigned, options:[{id,providerId}], tested }
|
|
651
785
|
var modelInfo = modelPair[0];
|
|
652
786
|
var setModelInfo = modelPair[1];
|
|
787
|
+
var irisState = useIrisState(5000);
|
|
653
788
|
function setVal(key, value) { var nv = {}; Object.keys(vals).forEach(function (k) { nv[k] = vals[k]; }); nv[key] = value; setVals(nv); }
|
|
654
789
|
function loadModels() {
|
|
655
790
|
if (!capability) return;
|
|
@@ -729,11 +864,13 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
729
864
|
run === 'running' ? '执行中…' : '执行'),
|
|
730
865
|
result);
|
|
731
866
|
}
|
|
732
|
-
var
|
|
867
|
+
var headError = !!(run && run !== 'running' && run.ok === false);
|
|
868
|
+
var headLit = !capability || (capabilityReady(irisState, capability) && !headError);
|
|
869
|
+
var headTitle = headError ? '最近一次执行出错' : (headLit ? 'API 与能力已配置' : '缺少可用 API 或能力模型');
|
|
733
870
|
return React.createElement('div', { className: 'iris-act-card' + (open ? ' open' : '') + (headLit ? ' lit' : ' dim') },
|
|
734
871
|
React.createElement('div', { className: 'iris-act-head', onClick: function () { setOpen(!open); } },
|
|
735
872
|
React.createElement('span', { className: 'iris-act-title' }, title),
|
|
736
|
-
capability ? React.createElement('span', { className: headLit ? 'iris-pm-dot lit' : 'iris-pm-dot', title:
|
|
873
|
+
capability ? React.createElement('span', { className: headLit ? 'iris-pm-dot lit' : 'iris-pm-dot', title: headTitle }, '●') : null,
|
|
737
874
|
React.createElement('span', { className: 'iris-act-arrow' }, '▶')),
|
|
738
875
|
body);
|
|
739
876
|
}
|
|
@@ -957,10 +1094,58 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
957
1094
|
note ? React.createElement('div', { className: 'iris-clean-note', style: { width: '100%' } }, note) : null);
|
|
958
1095
|
}
|
|
959
1096
|
|
|
1097
|
+
function PromptOptimizerSettings() {
|
|
1098
|
+
var pair = React.useState(null);
|
|
1099
|
+
var state = pair[0];
|
|
1100
|
+
var setState = pair[1];
|
|
1101
|
+
var notePair = React.useState('');
|
|
1102
|
+
var note = notePair[0];
|
|
1103
|
+
var setNote = notePair[1];
|
|
1104
|
+
|
|
1105
|
+
function load() {
|
|
1106
|
+
fetch('/iris/api/prompt-optimizer/config')
|
|
1107
|
+
.then(function (res) { return res.ok ? res.json() : null; })
|
|
1108
|
+
.then(function (data) { if (data && data.config) setState(data); })
|
|
1109
|
+
.catch(function () { setNote('读取提示词优化配置失败'); });
|
|
1110
|
+
}
|
|
1111
|
+
React.useEffect(function () {
|
|
1112
|
+
function changed(event) { if (event.detail && event.detail.config) setState(event.detail); }
|
|
1113
|
+
window.addEventListener('iris-prompt-config-changed', changed);
|
|
1114
|
+
load();
|
|
1115
|
+
return function () { window.removeEventListener('iris-prompt-config-changed', changed); };
|
|
1116
|
+
}, []);
|
|
1117
|
+
|
|
1118
|
+
function toggle() {
|
|
1119
|
+
var enabled = !(state && state.config && state.config.enabled !== false);
|
|
1120
|
+
fetch('/iris/api/prompt-optimizer/enabled', {
|
|
1121
|
+
method: 'POST',
|
|
1122
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1123
|
+
body: JSON.stringify({ enabled: enabled })
|
|
1124
|
+
}).then(function (res) {
|
|
1125
|
+
return res.json().then(function (data) {
|
|
1126
|
+
if (!res.ok) throw new Error(data.error || '更新失败');
|
|
1127
|
+
setState(data);
|
|
1128
|
+
window.dispatchEvent(new CustomEvent('iris-prompt-config-changed', { detail: data }));
|
|
1129
|
+
setNote(enabled ? '对话框 🫧 入口已启用' : '对话框入口已关闭;Iris 工作台与后台能力不受影响');
|
|
1130
|
+
});
|
|
1131
|
+
}).catch(function (err) { setNote(String(err && err.message || err)); });
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
var enabled = !state || !state.config || state.config.enabled !== false;
|
|
1135
|
+
return React.createElement('div', { className: 'iris-wb-card' },
|
|
1136
|
+
React.createElement('div', { className: 'iris-wb-row' },
|
|
1137
|
+
React.createElement('span', { className: 'iris-po-settings-mark' }, '🫧'),
|
|
1138
|
+
React.createElement('span', { style: { flex: 1 } }, enabled ? '对话框入口已启用' : '对话框入口已关闭'),
|
|
1139
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: toggle }, enabled ? '关闭入口' : '重新启用')),
|
|
1140
|
+
React.createElement('div', { className: 'iris-wb-muted' }, '这里只控制对话框提示词优化入口;Iris 工作台、Agent 工具和任务后台始终保持运行。'),
|
|
1141
|
+
note ? React.createElement('div', { className: 'iris-clean-note' }, note) : null);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
960
1144
|
function WorkbenchPanel() {
|
|
961
1145
|
var state = useIrisState(5000);
|
|
962
1146
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
963
1147
|
var recent = (state && state.tasks && state.tasks.recent) || [];
|
|
1148
|
+
var attention = (state && state.tasks && state.tasks.attention) || [];
|
|
964
1149
|
var selPair = React.useState(null);
|
|
965
1150
|
var selectedTask = selPair[0];
|
|
966
1151
|
var setSelectedTask = selPair[1];
|
|
@@ -975,10 +1160,16 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
975
1160
|
React.createElement(CapabilityAssigner, {}),
|
|
976
1161
|
React.createElement('div', { className: 'iris-wb-sec' }, 'Iris 泡泡快捷卡片(勾选后显示在「⚡ 常用」标签)'),
|
|
977
1162
|
React.createElement(BubbleCardPicker, {}),
|
|
1163
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '对话框提示词优化'),
|
|
1164
|
+
React.createElement(PromptOptimizerSettings, {}),
|
|
978
1165
|
React.createElement('div', { className: 'iris-wb-sec' }, '运行中任务'),
|
|
979
1166
|
React.createElement('div', { className: 'iris-wb-box' },
|
|
980
1167
|
running.length ? running.map(function (t) { return taskRow(t, true, selectedTask === t.id, onSelect); })
|
|
981
1168
|
: React.createElement('div', { className: 'iris-wb-empty' }, '暂无进行中任务')),
|
|
1169
|
+
attention.length ? React.createElement('div', {},
|
|
1170
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '需要处理'),
|
|
1171
|
+
React.createElement('div', { className: 'iris-wb-box' },
|
|
1172
|
+
attention.map(function (t) { return taskRow(t, false, selectedTask === t.id, onSelect); }))) : null,
|
|
982
1173
|
React.createElement('div', { className: 'iris-wb-sec' }, '历史任务'),
|
|
983
1174
|
React.createElement(CleanupBar, {}),
|
|
984
1175
|
React.createElement(HistoryBrowser, { recent: recent, selected: selectedTask, onSelect: onSelect }),
|
|
@@ -991,6 +1182,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
991
1182
|
var state = useIrisState(5000);
|
|
992
1183
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
993
1184
|
var recent = (state && state.tasks && state.tasks.recent) || [];
|
|
1185
|
+
var attention = (state && state.tasks && state.tasks.attention) || [];
|
|
994
1186
|
var recentTotal = (state && state.tasks && state.tasks.recentTotal) || recent.length;
|
|
995
1187
|
var cards = useBubbleCards()[0];
|
|
996
1188
|
var tabPair = React.useState('tasks');
|
|
@@ -1002,17 +1194,24 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1002
1194
|
function onSelect(id) { setSelectedTask(id); }
|
|
1003
1195
|
var body;
|
|
1004
1196
|
if (tab === 'tasks') {
|
|
1005
|
-
|
|
1197
|
+
// 快捷面板承载当前任务、需要处理的异常事实与可直接查看的成功产物。
|
|
1198
|
+
var succeeded = recent.filter(function (t) {
|
|
1199
|
+
return t.status === 'succeeded' && Array.isArray(t.media) && t.media.length > 0;
|
|
1200
|
+
});
|
|
1201
|
+
var shown = succeeded.slice(0, 6);
|
|
1006
1202
|
body = React.createElement('div', { className: 'iris-wb-box' },
|
|
1007
1203
|
running.length ? React.createElement('div', {},
|
|
1008
1204
|
React.createElement('div', { className: 'iris-wb-sec' }, '运行中'),
|
|
1009
1205
|
running.map(function (t) { return taskRow(t, true, selectedTask === t.id, onSelect); })) : null,
|
|
1010
|
-
React.createElement('div', {
|
|
1206
|
+
attention.length ? React.createElement('div', {},
|
|
1207
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '需要处理'),
|
|
1208
|
+
attention.slice(0, 4).map(function (t) { return taskRowMini(t, selectedTask === t.id, onSelect); })) : null,
|
|
1209
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '最近完成'),
|
|
1011
1210
|
shown.length ? shown.map(function (t) { return taskRowMini(t, selectedTask === t.id, onSelect); })
|
|
1012
|
-
: React.createElement('div', { className: 'iris-wb-empty' }, '
|
|
1013
|
-
|
|
1211
|
+
: React.createElement('div', { className: 'iris-wb-empty' }, '尚无成功产物'),
|
|
1212
|
+
succeeded.length > 6 || recentTotal > succeeded.length
|
|
1014
1213
|
? React.createElement('div', { className: 'iris-bubble-more', style: { cursor: 'default' } },
|
|
1015
|
-
'
|
|
1214
|
+
'失败、取消与完整历史请到 Iris 工作台查看') : null);
|
|
1016
1215
|
} else {
|
|
1017
1216
|
var defs = CARD_DEFS.filter(function (d) { return cards.indexOf(d.action) >= 0; });
|
|
1018
1217
|
body = defs.length
|
|
@@ -1027,6 +1226,234 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1027
1226
|
selectedTask ? React.createElement(TaskDetailDrawer, { taskId: selectedTask }) : null);
|
|
1028
1227
|
}
|
|
1029
1228
|
|
|
1229
|
+
function PromptOptimizerControl(props) {
|
|
1230
|
+
var draft = props.useInput(function (value) { return value.draft; });
|
|
1231
|
+
var phase = props.useInput(function (value) { return value.phase; });
|
|
1232
|
+
var refCount = props.useInput(function (value) { return (value.occurrences || []).length; });
|
|
1233
|
+
var projection = props.useProjection('modelSelection');
|
|
1234
|
+
var selectedRoute = projection && (projection.current || projection.selection || projection.next || projection.lastUsed || (projection.provider && projection.model ? projection : null));
|
|
1235
|
+
|
|
1236
|
+
var openPair = React.useState(false);
|
|
1237
|
+
var open = openPair[0];
|
|
1238
|
+
var setOpen = openPair[1];
|
|
1239
|
+
var targetPair = React.useState('general');
|
|
1240
|
+
var target = targetPair[0];
|
|
1241
|
+
var setTarget = targetPair[1];
|
|
1242
|
+
var busyPair = React.useState(false);
|
|
1243
|
+
var busy = busyPair[0];
|
|
1244
|
+
var setBusy = busyPair[1];
|
|
1245
|
+
var resultPair = React.useState(null);
|
|
1246
|
+
var result = resultPair[0];
|
|
1247
|
+
var setResult = resultPair[1];
|
|
1248
|
+
var notePair = React.useState('');
|
|
1249
|
+
var note = notePair[0];
|
|
1250
|
+
var setNote = notePair[1];
|
|
1251
|
+
var configPair = React.useState(null);
|
|
1252
|
+
var configState = configPair[0];
|
|
1253
|
+
var setConfigState = configPair[1];
|
|
1254
|
+
var abortRef = React.useRef(null);
|
|
1255
|
+
var fileRef = React.useRef(null);
|
|
1256
|
+
|
|
1257
|
+
function promptRequest(operation, body, signal) {
|
|
1258
|
+
return fetch('/iris/api/prompt-optimizer/' + operation, {
|
|
1259
|
+
method: 'POST',
|
|
1260
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1261
|
+
body: JSON.stringify(body || {}),
|
|
1262
|
+
signal: signal
|
|
1263
|
+
}).then(function (res) {
|
|
1264
|
+
return res.json().catch(function () { return {}; }).then(function (data) {
|
|
1265
|
+
if (!res.ok) throw new Error(data.error || ('请求失败 (' + res.status + ')'));
|
|
1266
|
+
return data;
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
function loadConfig() {
|
|
1272
|
+
fetch('/iris/api/prompt-optimizer/config')
|
|
1273
|
+
.then(function (res) { return res.ok ? res.json() : null; })
|
|
1274
|
+
.then(function (data) { if (data && data.config) setConfigState(data); })
|
|
1275
|
+
.catch(function () { setNote('无法读取提示词优化配置'); });
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
React.useEffect(function () {
|
|
1279
|
+
return function () {
|
|
1280
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1281
|
+
};
|
|
1282
|
+
}, []);
|
|
1283
|
+
|
|
1284
|
+
React.useEffect(function () {
|
|
1285
|
+
function changed(event) { if (event.detail && event.detail.config) setConfigState(event.detail); }
|
|
1286
|
+
window.addEventListener('iris-prompt-config-changed', changed);
|
|
1287
|
+
loadConfig();
|
|
1288
|
+
return function () { window.removeEventListener('iris-prompt-config-changed', changed); };
|
|
1289
|
+
}, []);
|
|
1290
|
+
|
|
1291
|
+
function publishConfig(data) {
|
|
1292
|
+
setConfigState(data);
|
|
1293
|
+
try { window.dispatchEvent(new CustomEvent('iris-prompt-config-changed', { detail: data })); } catch (_) {}
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
function optimize() {
|
|
1297
|
+
if (!draft || !draft.trim()) { setNote('请先在输入框中写入提示词'); return; }
|
|
1298
|
+
if (refCount) { setNote('当前版本暂不改写含 @ 或 / 结构化引用的草稿,以免引用失效'); return; }
|
|
1299
|
+
if (phase !== 'plain') { setNote('输入框正忙,请稍后再试'); return; }
|
|
1300
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1301
|
+
var controller = new AbortController();
|
|
1302
|
+
abortRef.current = controller;
|
|
1303
|
+
setBusy(true);
|
|
1304
|
+
setNote('正在调用模型优化…这可能产生模型费用');
|
|
1305
|
+
setResult(null);
|
|
1306
|
+
promptRequest('optimize', {
|
|
1307
|
+
text: draft,
|
|
1308
|
+
target: target,
|
|
1309
|
+
sessionId: props.sessionId,
|
|
1310
|
+
route: selectedRoute || undefined
|
|
1311
|
+
}, controller.signal).then(function (data) {
|
|
1312
|
+
setResult(data);
|
|
1313
|
+
setNote('已生成预览,尚未写回输入框');
|
|
1314
|
+
}).catch(function (err) {
|
|
1315
|
+
if (controller.signal.aborted) setNote('已取消');
|
|
1316
|
+
else setNote(String(err && err.message || err));
|
|
1317
|
+
}).finally(function () {
|
|
1318
|
+
if (abortRef.current === controller) abortRef.current = null;
|
|
1319
|
+
setBusy(false);
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
function cancel() {
|
|
1324
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
function replaceDraft(text) {
|
|
1328
|
+
if (!result) return;
|
|
1329
|
+
if (draft !== result.original && !window.confirm('输入框内容在优化期间已变化,仍要替换吗?')) return;
|
|
1330
|
+
props.inputActions.setDraft(text);
|
|
1331
|
+
setNote(text === result.original ? '已恢复优化前原文' : '已写回输入框;不会自动发送');
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
function copyResult() {
|
|
1335
|
+
if (!result) return;
|
|
1336
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
1337
|
+
navigator.clipboard.writeText(result.optimized).then(function () { setNote('已复制优化结果'); }).catch(function () { setNote('复制失败,请手动选择文本'); });
|
|
1338
|
+
} else {
|
|
1339
|
+
setNote('浏览器不支持自动复制,请手动选择文本');
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
function importConfig(event) {
|
|
1344
|
+
var file = event.target.files && event.target.files[0];
|
|
1345
|
+
event.target.value = '';
|
|
1346
|
+
if (!file) return;
|
|
1347
|
+
file.text().then(function (raw) {
|
|
1348
|
+
var parsed = JSON.parse(raw);
|
|
1349
|
+
return promptRequest('import', { config: parsed });
|
|
1350
|
+
}).then(function (data) {
|
|
1351
|
+
publishConfig(data);
|
|
1352
|
+
setNote('JSON 配置已导入并生效');
|
|
1353
|
+
}).catch(function (err) {
|
|
1354
|
+
setNote('导入失败:' + String(err && err.message || err));
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
function exportConfig() {
|
|
1359
|
+
if (!configState || !configState.config) { setNote('配置尚未载入'); return; }
|
|
1360
|
+
try {
|
|
1361
|
+
var blob = new Blob([JSON.stringify(configState.config, null, 2) + '\n'], { type: 'application/json' });
|
|
1362
|
+
var url = URL.createObjectURL(blob);
|
|
1363
|
+
var a = document.createElement('a');
|
|
1364
|
+
a.href = url;
|
|
1365
|
+
a.download = 'iris-prompt-optimizer.json';
|
|
1366
|
+
a.click();
|
|
1367
|
+
setTimeout(function () { URL.revokeObjectURL(url); }, 0);
|
|
1368
|
+
setNote('已导出当前 JSON 配置');
|
|
1369
|
+
} catch (_) {
|
|
1370
|
+
setNote('导出失败');
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
function resetConfig() {
|
|
1375
|
+
if (!window.confirm('恢复 Iris 内置默认 Prompt、目标模板和会话模型路由?')) return;
|
|
1376
|
+
promptRequest('reset', {}).then(function (data) {
|
|
1377
|
+
publishConfig(data);
|
|
1378
|
+
setNote('已恢复 Iris 内置默认配置');
|
|
1379
|
+
}).catch(function (err) {
|
|
1380
|
+
setNote('重置失败:' + String(err && err.message || err));
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
function disableEntry() {
|
|
1385
|
+
if (busy) return;
|
|
1386
|
+
promptRequest('enabled', { enabled: false }).then(function (data) {
|
|
1387
|
+
publishConfig(data);
|
|
1388
|
+
setOpen(false);
|
|
1389
|
+
}).catch(function (err) { setNote('关闭失败:' + String(err && err.message || err)); });
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
if (configState && configState.config && configState.config.enabled === false) return null;
|
|
1393
|
+
|
|
1394
|
+
var routeLabel = 'DSH 默认模型';
|
|
1395
|
+
if (configState && configState.config && configState.config.route.mode === 'fixed') {
|
|
1396
|
+
routeLabel = configState.config.route.provider + ' / ' + configState.config.route.model;
|
|
1397
|
+
} else if (selectedRoute) {
|
|
1398
|
+
routeLabel = selectedRoute.provider + ' / ' + selectedRoute.model;
|
|
1399
|
+
}
|
|
1400
|
+
var reasoningSetting = configState && configState.config && configState.config.generation && configState.config.generation.reasoningEffort || 'off-if-supported';
|
|
1401
|
+
var reasoningLabel = reasoningSetting === 'off-if-supported' ? '支持时关闭' : reasoningSetting === 'provider-default' ? '供应商默认' : reasoningSetting === 'inherit' ? '跟随会话' : reasoningSetting;
|
|
1402
|
+
var outputBudget = configState && configState.config && configState.config.generation && configState.config.generation.maxOutputTokens || 1200;
|
|
1403
|
+
|
|
1404
|
+
var panel = null;
|
|
1405
|
+
if (open) {
|
|
1406
|
+
panel = React.createElement(React.Fragment, {},
|
|
1407
|
+
React.createElement('div', { className: 'iris-po-backdrop', onClick: function () { setOpen(false); }, 'aria-hidden': 'true' }),
|
|
1408
|
+
React.createElement('div', { className: 'iris-po-panel', role: 'dialog', 'aria-modal': 'true', 'aria-label': 'Iris 提示词优化', onClick: function (e) { e.stopPropagation(); } },
|
|
1409
|
+
React.createElement('div', { className: 'iris-po-head' },
|
|
1410
|
+
React.createElement('strong', { className: 'iris-po-title' }, React.createElement('span', { className: 'iris-po-mark' }, '🫧'), 'Iris 提示词优化'),
|
|
1411
|
+
React.createElement('button', { className: 'iris-po-close', onClick: function () { setOpen(false); }, title: '关闭' }, '✕')),
|
|
1412
|
+
React.createElement('div', { className: 'iris-po-muted' }, '只处理当前未发送草稿,不读取会话历史;调用模型可能产生费用。'),
|
|
1413
|
+
React.createElement('label', { className: 'iris-po-field' },
|
|
1414
|
+
React.createElement('span', {}, '优化目标'),
|
|
1415
|
+
React.createElement('select', { value: target, disabled: busy, onChange: function (e) { setTarget(e.target.value); } },
|
|
1416
|
+
React.createElement('option', { value: 'general' }, '通用对话'),
|
|
1417
|
+
React.createElement('option', { value: 'image' }, '图片生成'),
|
|
1418
|
+
React.createElement('option', { value: 'video' }, '视频生成'),
|
|
1419
|
+
React.createElement('option', { value: 's2v' }, '首尾帧视频'))),
|
|
1420
|
+
React.createElement('div', { className: 'iris-po-route', title: routeLabel }, '模型:' + routeLabel + ' · 思考:' + reasoningLabel + ' · 输出:' + outputBudget + ' tokens'),
|
|
1421
|
+
React.createElement('div', { className: 'iris-po-actions' },
|
|
1422
|
+
React.createElement('button', { className: 'iris-po-primary', disabled: busy || !draft || !draft.trim() || phase !== 'plain' || refCount > 0, onClick: optimize }, busy ? '优化中…' : '生成预览'),
|
|
1423
|
+
busy ? React.createElement('button', { className: 'iris-po-btn', onClick: cancel }, '取消') : null),
|
|
1424
|
+
result ? React.createElement('div', { className: 'iris-po-result' },
|
|
1425
|
+
React.createElement('div', { className: 'iris-po-result-label' }, '优化结果'),
|
|
1426
|
+
React.createElement('textarea', { readOnly: true, value: result.optimized }),
|
|
1427
|
+
React.createElement('div', { className: 'iris-po-route' }, '实际使用:' + result.route.provider + ' / ' + result.route.model + ' · 思考:' + (result.route.reasoningEffort === 'provider-default' ? '供应商默认' : result.route.reasoningEffort)),
|
|
1428
|
+
React.createElement('div', { className: 'iris-po-actions' },
|
|
1429
|
+
React.createElement('button', { className: 'iris-po-primary', onClick: function () { replaceDraft(result.optimized); } }, '写回输入框'),
|
|
1430
|
+
React.createElement('button', { className: 'iris-po-btn', onClick: copyResult }, '复制'),
|
|
1431
|
+
React.createElement('button', { className: 'iris-po-btn', onClick: optimize }, '再优化'),
|
|
1432
|
+
React.createElement('button', { className: 'iris-po-btn', onClick: function () { replaceDraft(result.original); } }, '恢复原文'))) : null,
|
|
1433
|
+
note ? React.createElement('div', { className: 'iris-po-note' }, note) : null,
|
|
1434
|
+
React.createElement('details', { className: 'iris-po-config' },
|
|
1435
|
+
React.createElement('summary', {}, 'JSON 配置'),
|
|
1436
|
+
React.createElement('div', { className: 'iris-po-muted' }, '可导出后修改 systemPrompt、targets、route 和 generation,再重新导入。'),
|
|
1437
|
+
React.createElement('div', { className: 'iris-po-actions' },
|
|
1438
|
+
React.createElement('button', { className: 'iris-po-btn', onClick: exportConfig }, '导出'),
|
|
1439
|
+
React.createElement('button', { className: 'iris-po-btn', onClick: function () { if (fileRef.current) fileRef.current.click(); } }, '导入'),
|
|
1440
|
+
React.createElement('button', { className: 'iris-po-btn danger', onClick: resetConfig }, '恢复默认'),
|
|
1441
|
+
React.createElement('input', { ref: fileRef, className: 'iris-po-file', type: 'file', accept: 'application/json,.json', onChange: importConfig })),
|
|
1442
|
+
React.createElement('div', { className: 'iris-po-muted' }, '当前:' + (configState && configState.source === 'custom' ? '用户 JSON' : 'Iris 默认'))),
|
|
1443
|
+
React.createElement('button', { className: 'iris-po-disable', disabled: busy, onClick: disableEntry }, '关闭此对话入口(工作台仍保留)')));
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
return React.createElement('div', { className: 'iris-po-wrap' },
|
|
1447
|
+
React.createElement('button', {
|
|
1448
|
+
className: 'iris-po-trigger',
|
|
1449
|
+
type: 'button',
|
|
1450
|
+
title: refCount ? '含结构化引用的草稿暂不支持优化' : '优化当前输入框提示词',
|
|
1451
|
+
'aria-expanded': open ? 'true' : 'false',
|
|
1452
|
+
onClick: function () { setOpen(!open); }
|
|
1453
|
+
}, '🫧'),
|
|
1454
|
+
panel);
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1030
1457
|
function ProgressDock() {
|
|
1031
1458
|
var state = useIrisState(5000);
|
|
1032
1459
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
@@ -1045,6 +1472,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1045
1472
|
var providers = (state && state.providers) || [];
|
|
1046
1473
|
var configured = providers.some(function (p) { return p.enabled && p.apiKeyHint; });
|
|
1047
1474
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
1475
|
+
var attention = (state && state.tasks && state.tasks.attention) || [];
|
|
1048
1476
|
|
|
1049
1477
|
var posPair = React.useState(function () {
|
|
1050
1478
|
var saved = null;
|
|
@@ -1130,8 +1558,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1130
1558
|
style = { right: DEFAULT_X + 'px', bottom: DEFAULT_Y + 'px' };
|
|
1131
1559
|
}
|
|
1132
1560
|
|
|
1133
|
-
var
|
|
1134
|
-
|
|
1561
|
+
var badgeCount = running.length + attention.length;
|
|
1562
|
+
var badge = badgeCount
|
|
1563
|
+
? React.createElement('span', { className: 'iris-bubble-badge', title: attention.length ? attention.length + ' 个任务需要处理' : '' }, badgeCount)
|
|
1135
1564
|
: null;
|
|
1136
1565
|
|
|
1137
1566
|
var panel = null;
|
|
@@ -1167,6 +1596,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1167
1596
|
}, WorkbenchPanel);
|
|
1168
1597
|
});
|
|
1169
1598
|
|
|
1599
|
+
ctx.slots.inject('conversation.input.right', function () {
|
|
1600
|
+
return ctx.slots.register({
|
|
1601
|
+
name: 'conversation.input.right',
|
|
1602
|
+
id: 'iris-prompt-optimizer',
|
|
1603
|
+
order: 40,
|
|
1604
|
+
label: function () { return 'Iris 提示词优化'; }
|
|
1605
|
+
}, PromptOptimizerControl);
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1170
1608
|
ctx.slots.inject('conversation.input.dock', function () {
|
|
1171
1609
|
return ctx.slots.register({
|
|
1172
1610
|
name: 'conversation.input.dock',
|