@mokuyoaxis/dsh-iris 0.2.4 → 0.2.6
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/.dsh/skills/iris-use/SKILL.md +2 -2
- package/.dsh/skills/iris-use/references/media-contract.md +2 -2
- package/CHANGELOG.md +38 -1
- package/README.en.md +34 -41
- package/README.md +35 -40
- package/bin/dsh-iris.js +29 -12
- package/docs/AGENT_QUICKSTART.md +11 -13
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CLI_MANAGEMENT.md +5 -1
- package/docs/DSH_COMPATIBILITY.md +38 -8
- package/docs/DSH_PROVIDER_CONNECTIONS.md +34 -0
- package/docs/DSH_RC2_ADAPTATION.md +1 -1
- package/docs/FUNCTION_MODEL_ASSIGNMENTS.md +2 -0
- package/docs/FUNCTION_MODEL_POOLS.md +84 -0
- package/docs/HEADLESS_CLI.md +7 -7
- package/docs/HOST_ADAPTER_CONTRACT.md +8 -2
- package/docs/HOST_DOCTOR.md +3 -3
- package/docs/MUSIC.md +2 -2
- package/docs/PROVIDER_HEALTH.md +26 -2
- package/docs/ROADMAP.md +8 -4
- package/docs/SECURITY.md +12 -2
- package/docs/VISION_CLI.md +2 -2
- package/docs/releases/0.2.5.md +33 -0
- package/docs/releases/0.2.6.md +50 -0
- package/lib/actions.js +137 -30
- package/lib/adapters.js +36 -9
- package/lib/api.js +2 -1
- package/lib/client.js +717 -180
- package/lib/composite-vision-routing.js +6 -6
- package/lib/config.js +140 -29
- package/lib/doctor.js +9 -2
- package/lib/dsh-compatibility.js +7 -1
- package/lib/dsh-core-adapter.js +22 -11
- package/lib/dsh-host-adapter.js +32 -29
- package/lib/dsh-provider-config.js +35 -0
- package/lib/dsh-provider-source.js +102 -0
- package/lib/headless-vision.js +5 -5
- package/lib/host-contract.js +1 -0
- package/lib/http-vision-model-adapter.js +5 -2
- package/lib/index.js +54 -35
- package/lib/model-directory-service.js +53 -0
- package/lib/model-directory.js +103 -0
- package/lib/models.js +71 -6
- package/lib/ocr-model-routing.js +2 -2
- package/lib/provider-adapters.js +42 -9
- package/lib/provider-catalog.js +14 -9
- package/lib/provider-config-scope.js +16 -0
- package/lib/provider-config-service.js +82 -13
- package/lib/provider-health.js +33 -0
- package/lib/provider-protocol.js +30 -0
- package/lib/vision-model-routing.js +25 -12
- package/package.json +9 -9
- package/user_guide.md +28 -29
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.6';
|
|
25
25
|
var HOST_CLIENT_PROTOCOL_VERSION = 0;
|
|
26
26
|
var reportedSeats = [];
|
|
27
27
|
|
|
@@ -81,6 +81,49 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
81
81
|
'.iris-wb-link:hover { text-decoration: underline; }',
|
|
82
82
|
'.iris-wb-muted { color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 12px; }',
|
|
83
83
|
'.iris-wb-empty { color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 12px; padding: 6px 2px; }',
|
|
84
|
+
/* 工作台页面共用视觉规则;不改变对话泡泡和 composer 的布局。 */
|
|
85
|
+
'.iris-workbench-page { --iris-workbench-accent: var(--dsw-alias-brand-primary, #8c9eff); display: grid; gap: 10px; }',
|
|
86
|
+
'.iris-workbench-page *, .iris-workbench-page *::before { box-sizing: border-box; }',
|
|
87
|
+
'.iris-workbench-page [hidden], .iris-health [hidden] { display: none !important; }',
|
|
88
|
+
'.iris-workbench-page > .iris-wb-head { justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; margin: 2px 0 4px; }',
|
|
89
|
+
'.iris-workbench-page > .iris-wb-head .iris-wb-title { font-size: 20px; font-weight: 650; letter-spacing: -.03em; }',
|
|
90
|
+
'.iris-wb-section { min-width: 0; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 12px; background: var(--dsw-alias-bg-layer-1, #26262e); }',
|
|
91
|
+
'.iris-wb-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 14px; list-style: none; }',
|
|
92
|
+
'.iris-wb-section-head::-webkit-details-marker { display: none; }',
|
|
93
|
+
'.iris-wb-section-head h2 { margin: 0; font-size: 15px; font-weight: 600; letter-spacing: -.01em; }',
|
|
94
|
+
'.iris-wb-section-head p { margin: 4px 0 0; font-size: 12px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
95
|
+
'details.iris-wb-section > summary { cursor: pointer; border-radius: inherit; }',
|
|
96
|
+
'.iris-wb-section-arrow { flex-shrink: 0; display: inline-flex; gap: 7px; align-items: center; font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
97
|
+
'.iris-wb-section-arrow svg { transition: transform .15s; }',
|
|
98
|
+
'details.iris-wb-section[open] > summary .iris-wb-section-arrow svg { transform: rotate(180deg); }',
|
|
99
|
+
'.iris-wb-section-body { min-width: 0; padding: 0 14px 14px; display: grid; gap: 8px; }',
|
|
100
|
+
'.iris-workbench-page .iris-wb-sec { margin: 4px 0 0; font-size: 13px; color: var(--dsw-alias-label-primary, #e6e6e6); }',
|
|
101
|
+
'.iris-workbench-page .iris-pm, .iris-workbench-page .iris-cap-grid, .iris-workbench-page .iris-act-group { gap: 8px; }',
|
|
102
|
+
'.iris-workbench-page .iris-pm-card, .iris-workbench-page .iris-wb-card, .iris-workbench-page .iris-act-card, .iris-workbench-page .iris-cap-row { border-radius: 10px; }',
|
|
103
|
+
'.iris-workbench-page .iris-pm-card, .iris-workbench-page .iris-cap-row { padding: 10px; }',
|
|
104
|
+
'.iris-workbench-page .iris-pm-card { gap: 8px; }',
|
|
105
|
+
'.iris-workbench-page .iris-pm-body { display: grid; gap: 8px; padding-top: 10px; border-top: 1px solid var(--dsw-alias-border-l1, #3a3a44); }',
|
|
106
|
+
'.iris-workbench-page .iris-pm-field { gap: 5px; font-size: 12px; }',
|
|
107
|
+
'.iris-workbench-page :is(.iris-pm-btn, .iris-pm-add, .iris-ff-btn, .iris-act-run, .iris-clean-btn) { min-height: 32px; padding: 5px 9px; border-radius: 8px; font-family: inherit; font-size: 12px; }',
|
|
108
|
+
'.iris-workbench-page :is(button, select, input):focus-visible, .iris-wb-section-head:focus-visible { outline: 2px solid var(--iris-workbench-accent, #8c9eff); outline-offset: 3px; }',
|
|
109
|
+
'.iris-workbench-page :is(input, textarea, select) { font-family: inherit; accent-color: var(--iris-workbench-accent); }',
|
|
110
|
+
'.iris-workbench-page input:not([type=checkbox]):not([type=radio]):not([type=file]), .iris-workbench-page textarea, .iris-workbench-page select { min-width: 0; max-width: 100%; min-height: 34px; padding: 5px 8px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); font-size: 12px; }',
|
|
111
|
+
'.iris-workbench-page .iris-pm-note { line-height: 1.65; }',
|
|
112
|
+
'.iris-workbench-page .iris-pm-mrows { max-height: 400px; gap: 8px; }',
|
|
113
|
+
'.iris-workbench-page .iris-pm-mrow { min-width: 0; padding: 10px; gap: 6px; flex-wrap: wrap; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 10px; }',
|
|
114
|
+
'.iris-workbench-page .iris-pm-mname { flex-basis: 100%; white-space: normal; overflow-wrap: anywhere; font-size: 13px; }',
|
|
115
|
+
'.iris-workbench-page .iris-pm-mrow > label { display: flex; align-items: center; flex: 1 1 230px; flex-wrap: wrap; gap: 6px; }',
|
|
116
|
+
'.iris-workbench-page .iris-pm-mrow > label select { flex: 1; }',
|
|
117
|
+
'.iris-workbench-page .iris-cap-row { gap: 7px; }',
|
|
118
|
+
'.iris-workbench-page .iris-cap-row > select:first-of-type { margin-left: auto; }',
|
|
119
|
+
'.iris-workbench-page .iris-cap-row > .iris-pm-note, .iris-workbench-page .iris-cap-row > .iris-cap-feedback { flex-basis: 100%; }',
|
|
120
|
+
'.iris-workbench-page .iris-cap-row > .iris-pm-model-sel { margin-left: 0; }',
|
|
121
|
+
'.iris-workbench-page .iris-cap-chip { padding: 3px 7px; border-radius: 8px; overflow-wrap: anywhere; }',
|
|
122
|
+
'.iris-workbench-page .iris-cap-confirm { padding: 12px; border-radius: 10px; }',
|
|
123
|
+
'.iris-workbench-page .iris-act-head { min-height: 40px; padding: 9px 12px; }',
|
|
124
|
+
'.iris-workbench-page .iris-act-body { padding: 10px 12px 12px; gap: 8px; }',
|
|
125
|
+
'.iris-workbench-page .iris-doctor-row { font-size: 12px; line-height: 1.6; }',
|
|
126
|
+
'@media (max-width: 640px) { .iris-wb-section-head { padding: 12px; } .iris-wb-section-body { padding: 0 12px 12px; } .iris-workbench-page :is(.iris-pm-btn, .iris-pm-add, .iris-ff-btn, .iris-act-run, .iris-clean-btn) { min-height: 40px; } .iris-workbench-page input:not([type=checkbox]):not([type=radio]):not([type=file]), .iris-workbench-page textarea, .iris-workbench-page select { min-height: 36px; } }',
|
|
84
127
|
'.iris-progress-dock { display: grid; gap: 6px; }',
|
|
85
128
|
'.iris-progress-row { font-size: 12px; display: flex; gap: 8px; align-items: center; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
86
129
|
'.iris-wb-card { cursor: pointer; }',
|
|
@@ -157,7 +200,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
157
200
|
'.iris-pm-add:hover { border-color: var(--dsw-alias-brand-primary, #7aa2f7); }',
|
|
158
201
|
'.iris-pm-card { background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; padding: 8px 10px; display: grid; gap: 6px; }',
|
|
159
202
|
'.iris-pm-card.open { border-color: var(--dsw-alias-brand-primary, #7aa2f7); }',
|
|
160
|
-
'.iris-pm-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; cursor: pointer; }',
|
|
203
|
+
'.iris-pm-top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; width: 100%; padding: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }',
|
|
161
204
|
'.iris-pm-name { font-weight: 600; font-size: 13px; flex: 1; }',
|
|
162
205
|
'.iris-pm-btn { background: transparent; color: var(--dsw-alias-label-secondary, #9a9a9a); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 4px; font-size: 11px; padding: 2px 7px; cursor: pointer; }',
|
|
163
206
|
'.iris-pm-btn:hover { color: var(--dsw-alias-label-primary, #e6e6e6); border-color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
@@ -168,25 +211,82 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
168
211
|
'.iris-pm-cap { color: var(--dsw-alias-brand-primary, #7aa2f7); font-size: 10px; }',
|
|
169
212
|
'.iris-pm-note { font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
170
213
|
'.iris-pm-field { display: grid; gap: 3px; }',
|
|
214
|
+
'details[data-provider-directory] { min-width: 0; padding: 10px 12px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; }',
|
|
215
|
+
'details[data-provider-directory] > summary { cursor: pointer; overflow-wrap: anywhere; }',
|
|
216
|
+
'details[data-provider-directory] p { margin: 6px 0; overflow-wrap: anywhere; }',
|
|
217
|
+
'details[data-provider-directory] > summary:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #6b78e8); outline-offset: 3px; }',
|
|
171
218
|
'.iris-pm-field input, .iris-pm-field textarea { background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 4px; padding: 4px 6px; font-size: 12px; width: 100%; box-sizing: border-box; }',
|
|
172
219
|
'.iris-pm-act { font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
173
220
|
'.iris-pm-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--dsw-alias-label-tertiary, #73737d); margin-right: 4px; }',
|
|
174
221
|
'.iris-pm-dot.configured { background: #6f9df6; box-shadow: 0 0 7px rgba(111,157,246,.55); }',
|
|
175
222
|
'.iris-pm-dot.verified { background: var(--dsw-alias-state-success-primary, #6fcf6f); box-shadow: 0 0 7px var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
176
223
|
'.iris-pm-dot.failed { background: #b9656d; box-shadow: 0 0 6px rgba(185,101,109,.38); opacity: .82; }',
|
|
177
|
-
'.iris-health
|
|
178
|
-
'.iris-health
|
|
179
|
-
'.iris-health-
|
|
180
|
-
'.iris-health-
|
|
181
|
-
'.iris-health-
|
|
182
|
-
'.iris-health-
|
|
183
|
-
'.iris-health-
|
|
224
|
+
'.iris-health { --iris-health-accent: var(--dsw-alias-brand-primary, #8c9eff); container-type: inline-size; padding: 0; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 12px; background: var(--dsw-alias-bg-layer-1, #26262e); }',
|
|
225
|
+
'.iris-health *, .iris-health *::before { box-sizing: border-box; }',
|
|
226
|
+
'.iris-health-head, .iris-health-title-line, .iris-health-card-top, .iris-health-card-foot, .iris-health-detail-head, .iris-health-model-head, .iris-health-confirm-actions { display: flex; align-items: center; gap: 10px; }',
|
|
227
|
+
'.iris-health-head { justify-content: space-between; flex-wrap: wrap; padding: 10px 14px; }',
|
|
228
|
+
'.iris-health-content { padding: 0 14px 14px; }',
|
|
229
|
+
'.iris-health-toolbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }',
|
|
230
|
+
'.iris-health-eyebrow { color: var(--iris-health-accent); font-size: 10px; font-weight: 600; letter-spacing: .14em; margin-bottom: 6px; }',
|
|
231
|
+
'.iris-health h2, .iris-health h3, .iris-health p { margin: 0; }',
|
|
232
|
+
'.iris-health h2 { font-size: 15px; font-weight: 600; letter-spacing: -.01em; }',
|
|
233
|
+
'.iris-health-subtitle, .iris-health-caption { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
234
|
+
'.iris-health-subtitle { margin-top: 5px !important; }',
|
|
235
|
+
'.iris-health-btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 32px; padding: 5px 10px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); font: inherit; font-size: 12px; white-space: nowrap; cursor: pointer; }',
|
|
236
|
+
'.iris-health-btn:hover { border-color: var(--iris-health-accent); }',
|
|
237
|
+
'.iris-health-btn.primary { border-color: transparent; background: var(--iris-health-accent); color: var(--dsw-alias-label-primary-inverted, #1b2034); font-weight: 600; }',
|
|
238
|
+
'.iris-health-btn:disabled { opacity: .5; cursor: default; }',
|
|
239
|
+
'.iris-health button:focus-visible { outline: 2px solid var(--iris-health-accent); outline-offset: 3px; }',
|
|
240
|
+
'.iris-health-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }',
|
|
241
|
+
'.iris-health-row { --iris-health-tone: var(--dsw-alias-label-secondary, #9a9a9a); display: grid; gap: 10px; min-width: 0; padding: 12px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #26262e); color: var(--dsw-alias-label-primary, #e6e6e6); font: inherit; text-align: left; cursor: pointer; transition: border-color .15s, transform .15s; }',
|
|
242
|
+
'.iris-health-row:hover:not(:disabled) { transform: translateY(-2px); border-color: var(--iris-health-accent); }',
|
|
243
|
+
'.iris-health-row:disabled { cursor: wait; opacity: .7; }',
|
|
244
|
+
'.iris-health-row.active { border-color: var(--iris-health-accent); box-shadow: inset 0 0 0 1px var(--iris-health-accent); }',
|
|
245
|
+
'.iris-health-row.configured { --iris-health-tone: var(--iris-health-accent); }',
|
|
246
|
+
'.iris-health-row.verified { --iris-health-tone: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
247
|
+
'.iris-health-row.failed, .iris-health-row.rate-limited { --iris-health-tone: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
248
|
+
'.iris-health-row.cooling { --iris-health-tone: #c68b23; }',
|
|
249
|
+
'.iris-health-icon { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 32px; height: 32px; border-radius: 9px; background: rgba(132,115,238,.1); color: var(--iris-health-accent); }',
|
|
250
|
+
'.iris-health-card-top { justify-content: space-between; }',
|
|
251
|
+
'.iris-health-name { font-size: 14px; font-weight: 600; }',
|
|
252
|
+
'.iris-health-state { display: inline-flex; align-items: center; gap: 5px; margin-top: 7px; font-size: 11px; color: var(--iris-health-tone); }',
|
|
253
|
+
'.iris-health-state::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }',
|
|
254
|
+
'.iris-health-card-foot { justify-content: space-between; min-width: 0; padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l1, #3a3a44); font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
255
|
+
'.iris-health-link { color: var(--iris-health-accent); white-space: nowrap; }',
|
|
256
|
+
'.iris-health-footer { margin-top: 13px !important; font-size: 11px; line-height: 1.6; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
257
|
+
'.iris-health-notice { margin-top: 12px; padding: 11px 13px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 10px; font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }',
|
|
258
|
+
'.iris-health-notice.error { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
259
|
+
'.iris-health-detail { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--dsw-alias-border-l1, #3a3a44); }',
|
|
260
|
+
'.iris-health-detail-head { justify-content: space-between; align-items: flex-start; margin-bottom: 10px; }',
|
|
261
|
+
'.iris-health-detail-head > button { flex-shrink: 0; }',
|
|
262
|
+
'.iris-health h3 { font-size: 15px; font-weight: 600; margin-bottom: 4px; }',
|
|
263
|
+
'.iris-health-models { display: grid; gap: 8px; }',
|
|
264
|
+
'.iris-health-model { padding: 10px 12px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 10px; background: var(--dsw-alias-bg-layer-1, #26262e); min-width: 0; }',
|
|
265
|
+
'.iris-health-model-head { justify-content: space-between; align-items: flex-start; }',
|
|
266
|
+
'.iris-health-model-title { min-width: 0; flex: 1; }',
|
|
267
|
+
'.iris-health-model-title strong { display: block; font-size: 13px; font-weight: 550; overflow-wrap: anywhere; }',
|
|
268
|
+
'.iris-health-result { display: flex; gap: 7px; align-items: flex-start; margin-top: 10px; font-size: 12px; line-height: 1.6; overflow-wrap: anywhere; }',
|
|
269
|
+
'.iris-health-result.passed { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
270
|
+
'.iris-health-result.failed, .iris-health-result.unknown { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
271
|
+
'.iris-health-result.running { color: var(--iris-health-accent); }',
|
|
272
|
+
'.iris-health-spinner { display: inline-block; flex: 0 0 13px; width: 13px; height: 13px; margin-top: 3px; border: 1.5px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: iris-health-spin .8s linear infinite; }',
|
|
273
|
+
'.iris-health-confirm { margin-top: 12px; padding: 14px; border-radius: 11px; border: 1px solid var(--iris-health-accent); background: rgba(132,115,238,.06); font-size: 12px; line-height: 1.7; }',
|
|
274
|
+
'.iris-health-confirm-actions { margin-top: 12px; flex-wrap: wrap; }',
|
|
275
|
+
'@keyframes iris-health-spin { to { transform: rotate(360deg); } }',
|
|
276
|
+
'@container (max-width: 650px) { .iris-health-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }',
|
|
277
|
+
'@container (max-width: 420px) { .iris-health-grid { grid-template-columns: 1fr; } }',
|
|
278
|
+
'@media (max-width: 640px) { .iris-health-head { flex-wrap: nowrap; padding: 12px; } .iris-health-head > div { flex: 1; min-width: 0; } .iris-health-head > button { flex-shrink: 0; } .iris-health-content { padding: 0 12px 12px; } .iris-health-grid { grid-template-columns: 1fr; gap: 8px; } .iris-health-row { position: relative; grid-template-columns: 32px minmax(0, 1fr); gap: 5px 9px; padding: 10px; } .iris-health-card-top { display: contents; } .iris-health-card-top .iris-health-icon { grid-row: 1 / 3; grid-column: 1; } .iris-health-card-top .iris-health-caption { position: absolute; right: 10px; top: 10px; font-size: 10px; } .iris-health-card-main { grid-column: 2; min-width: 0; } .iris-health-name { padding-right: 58px; } .iris-health-state { display: flex; line-height: 1.6; margin-top: 5px; } .iris-health-state::before { flex-shrink: 0; } .iris-health-card-foot { grid-column: 2; border: 0; padding-top: 0; font-size: 10px; flex-wrap: wrap; gap: 3px; } .iris-health-link { margin-left: auto; } .iris-health-btn { min-height: 40px; } .iris-health-model-head { flex-wrap: wrap; } .iris-health-model-head > .iris-health-btn { margin-left: auto; } }',
|
|
279
|
+
'@media (prefers-reduced-motion: reduce) { .iris-health-row { transition: none; } .iris-health-row:hover { transform: none; } .iris-health-spinner { animation: none; } }',
|
|
184
280
|
'.iris-cap-grid { display: grid; gap: 6px; }',
|
|
185
281
|
'.iris-cap-row { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; padding: 6px 9px; font-size: 12px; }',
|
|
186
282
|
'.iris-cap-name { font-weight: 600; min-width: 64px; }',
|
|
187
283
|
'.iris-cap-chip { display: inline-flex; align-items: center; gap: 3px; background: var(--dsw-alias-bg-layer-2, #32323c); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 10px; padding: 1px 6px; font-size: 11px; }',
|
|
188
284
|
'.iris-cap-chip b { color: var(--dsw-alias-brand-primary, #7aa2f7); }',
|
|
189
285
|
'.iris-cap-auto { color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 11px; font-style: italic; }',
|
|
286
|
+
'.iris-cap-feedback { flex-basis: 100%; color: var(--dsw-alias-brand-primary, #7aa2f7); }',
|
|
287
|
+
'.iris-cap-feedback.saved { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
288
|
+
'.iris-cap-feedback.unknown, .iris-cap-compatibility { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
289
|
+
'.iris-cap-confirm { flex-basis: 100%; padding: 8px; border: 1px solid var(--dsw-alias-brand-primary, #7aa2f7); border-radius: 6px; }',
|
|
190
290
|
'.iris-pm-model-sel { background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 4px; padding: 4px 6px; font-size: 12px; width: 100%; }',
|
|
191
291
|
'.iris-wb-drawer .iris-wb-loading { color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 12px; }',
|
|
192
292
|
'.iris-bubble { position: fixed; z-index: 9999; cursor: grab; user-select: none; touch-action: none; }',
|
|
@@ -268,7 +368,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
268
368
|
'.iris-pm-mname { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
|
269
369
|
'.iris-pm-mtag { font-size: 9px; color: var(--dsw-alias-brand-primary, #7aa2f7); border: 1px solid var(--dsw-alias-brand-primary, #7aa2f7); border-radius: 3px; padding: 0 3px; margin-left: 4px; }',
|
|
270
370
|
'.iris-pm-mcaps { display: flex; gap: 3px; }',
|
|
271
|
-
'.iris-pm-ver { font-size:
|
|
371
|
+
'.iris-pm-ver { font: inherit; font-size: 11px; padding: 3px 7px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 7px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-secondary, #9a9a9a); cursor: pointer; }',
|
|
272
372
|
'.iris-pm-ver.configured { color: #7da8fa; }',
|
|
273
373
|
'.iris-pm-ver.verified { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
274
374
|
'.iris-pm-ver.failed { color: #c87a81; border-color: rgba(185,101,109,.5); }',
|
|
@@ -370,13 +470,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
370
470
|
].join('\n');
|
|
371
471
|
|
|
372
472
|
function adoptStyles() {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
473
|
+
var style = document.getElementById(STYLE_ID);
|
|
474
|
+
if (style === null) {
|
|
475
|
+
style = document.createElement('style');
|
|
476
|
+
style.id = STYLE_ID;
|
|
477
|
+
style.dataset.plugin = 'dsh-iris';
|
|
478
|
+
style.dataset.pluginCss = STYLE_ID;
|
|
479
|
+
document.head.appendChild(style);
|
|
480
|
+
}
|
|
481
|
+
if (style.textContent !== cssText) style.textContent = cssText;
|
|
380
482
|
}
|
|
381
483
|
|
|
382
484
|
var inject = ['slots'];
|
|
@@ -514,7 +616,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
514
616
|
|
|
515
617
|
function healthText(health) {
|
|
516
618
|
var status = health && HEALTH_META[health.status] ? health.status : 'unconfigured';
|
|
517
|
-
var text = health && health.rateLimited
|
|
619
|
+
var text = health && health.modelUnavailable ? '模型不存在,已暂停自动选用' : health && health.rateLimited
|
|
518
620
|
? rateLimitLabel(health) + (health.retryAt
|
|
519
621
|
? ',' + new Date(health.retryAt).toLocaleTimeString() + ' 恢复候选资格;可提前实测'
|
|
520
622
|
: ',实测成功后恢复')
|
|
@@ -524,25 +626,238 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
524
626
|
return text + (age ? ' · ' + age : '');
|
|
525
627
|
}
|
|
526
628
|
|
|
629
|
+
var HEALTH_ROWS = [
|
|
630
|
+
{ cap: 'image-gen', label: '图片', action: 'draw', sample: '生成一张红色圆形测试图,可能需要约一分钟。', icon: 'M4 4h16v16H4z M4 16l5-5 4 4 3-3 4 4 M8 8h.01' },
|
|
631
|
+
{ cap: 'video-gen', label: '视频', action: 'video', taskOnly: true, sample: '请到视频功能填写创作要求或素材,选择模型后提交验证。', icon: 'M3 6h12v12H3z M15 10l6-3v10l-6-3' },
|
|
632
|
+
{ cap: 'tts', label: '语音', action: 'tts', sample: '合成一句“你好”,以接口返回音频作为通过依据。', icon: 'M4 10h4l5-4v12l-5-4H4z M16 8c3 2 3 6 0 8 M19 5c5 4 5 10 0 14' },
|
|
633
|
+
{ cap: 'music-gen', label: '音乐', action: 'music', taskOnly: true, sample: '请到音乐功能填写实际创作要求,选择模型后提交验证。', icon: 'M9 17V5l11-2v12 M9 5v4l11-2 M9 17a3 3 0 1 1-3-3h3 M20 15a3 3 0 1 1-3-3h3' },
|
|
634
|
+
{ cap: 'transcribe', label: '转写', action: 'transcribe', taskOnly: true, sample: '请到转写功能提供真实音频,选择模型后提交验证。', icon: 'M9 5a3 3 0 0 1 6 0v7a3 3 0 0 1-6 0z M5 10v2a7 7 0 0 0 14 0v-2 M12 19v3 M8 22h8' },
|
|
635
|
+
{ cap: 'vision', label: '视觉', action: 'ocr', sample: '发送一张内置红色测试图,检查模型能否识别颜色。', icon: 'M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0' }
|
|
636
|
+
];
|
|
637
|
+
function capabilityIcon(row) {
|
|
638
|
+
return React.createElement('svg', { width: 20, height: 20, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor',
|
|
639
|
+
strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true }, React.createElement('path', { d: row.icon }));
|
|
640
|
+
}
|
|
641
|
+
function inspectionSupported(snapshot) {
|
|
642
|
+
return !!(snapshot && snapshot.policies && snapshot.readiness && HEALTH_ROWS.every(function (row) {
|
|
643
|
+
return snapshot.policies[row.cap] && ['auto', 'manual', 'disabled'].includes(snapshot.policies[row.cap].mode) && snapshot.readiness[row.cap];
|
|
644
|
+
}));
|
|
645
|
+
}
|
|
646
|
+
function inspectionModels(snapshot, row, requested) {
|
|
647
|
+
if (!snapshot) return [];
|
|
648
|
+
var order = snapshot.order[row.cap] || [];
|
|
649
|
+
var policy = snapshot.policies && snapshot.policies[row.cap];
|
|
650
|
+
var options = (snapshot.poolByCapability[row.cap] || []).slice();
|
|
651
|
+
if (requested && requested.ref && !options.some(function (option) { return option.ref === requested.ref; })) {
|
|
652
|
+
options.push({ ...requested, removed: true });
|
|
653
|
+
}
|
|
654
|
+
return options.filter(function (option) {
|
|
655
|
+
return order.includes(option.ref) || (requested && option.providerId === requested.providerId && option.id === requested.modelId);
|
|
656
|
+
}).sort(function (a, b) { return order.indexOf(a.ref) - order.indexOf(b.ref); }).map(function (option) {
|
|
657
|
+
var provider = snapshot.providers.find(function (item) { return item.id === option.providerId; });
|
|
658
|
+
var model = provider && provider.models.find(function (item) { return item.id === option.id; });
|
|
659
|
+
var capability = snapshot.health && snapshot.health.capabilities && snapshot.health.capabilities[row.cap];
|
|
660
|
+
var evidence = capability && (capability.candidates || []).find(function (item) { return item.providerId === option.providerId && item.modelId === option.id; });
|
|
661
|
+
var health = evidence || model && model.health && model.health[row.cap] || { status: 'configured' };
|
|
662
|
+
var host = option.ref === 'host:current-vision';
|
|
663
|
+
var reason = !inspectionSupported(snapshot) ? '请先让 DSH 加载新版功能池接口,再刷新配置。'
|
|
664
|
+
: policy.mode === 'disabled' ? '此功能已停用,请先调整调用方式。'
|
|
665
|
+
: option.removed ? '此模型已从当前配置中移除。'
|
|
666
|
+
: !order.includes(option.ref) ? '此模型未纳入当前功能池,请先调整调用方式或所选模型。'
|
|
667
|
+
: option.pendingConfirmation ? '请先在供应商中确认模型用途。'
|
|
668
|
+
: !host && (!provider || provider.enabled === false) ? '供应商当前不可用,请先检查设置。'
|
|
669
|
+
: provider && provider.dshProvider && (!provider.connection || !provider.connection.available) ? 'DSH 连接尚未就绪,请先检查宿主供应商设置。'
|
|
670
|
+
: option.protocolSupported === false ? option.reason || '当前接口协议不支持此功能。'
|
|
671
|
+
: option.unavailable && !health.rateLimited && !health.modelUnavailable && !option.modelUnavailable ? option.reason || '连接尚未就绪,请先检查供应商设置。' : '';
|
|
672
|
+
return { ...option, providerName: provider ? provider.name : 'DSH 宿主', health: health,
|
|
673
|
+
taskOnly: row.taskOnly || host, reason: reason, canTest: !reason };
|
|
674
|
+
});
|
|
675
|
+
}
|
|
527
676
|
function CapabilityHealthOverview() {
|
|
528
677
|
var state = useIrisState(5000);
|
|
529
|
-
var
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
];
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
678
|
+
var expandedPair = React.useState(false), expanded = expandedPair[0];
|
|
679
|
+
var snapshotPair = React.useState(null), snapshot = snapshotPair[0];
|
|
680
|
+
var loadingPair = React.useState(false), loading = loadingPair[0];
|
|
681
|
+
var freshPair = React.useState(false), fresh = freshPair[0];
|
|
682
|
+
var errorPair = React.useState('');
|
|
683
|
+
var checkedPair = React.useState('');
|
|
684
|
+
var selectedPair = React.useState(null), selected = selectedPair[0];
|
|
685
|
+
var requestedPair = React.useState(null), requested = requestedPair[0];
|
|
686
|
+
var confirmPair = React.useState(null), confirmation = confirmPair[0];
|
|
687
|
+
var resultsPair = React.useState({}), results = resultsPair[0];
|
|
688
|
+
var busyPair = React.useState(null), busy = busyPair[0];
|
|
689
|
+
var running = React.useRef(false), readVersion = React.useRef(0);
|
|
690
|
+
function noteResult(key, value) { resultsPair[1](function (previous) { return { ...previous, [key]: value }; }); }
|
|
691
|
+
function load() {
|
|
692
|
+
var version = ++readVersion.current;
|
|
693
|
+
var controller = new AbortController();
|
|
694
|
+
var timer = setTimeout(function () { controller.abort(); }, 10000);
|
|
695
|
+
loadingPair[1](true); freshPair[1](false); errorPair[1]('');
|
|
696
|
+
return Promise.all([
|
|
697
|
+
postAction('assignments_get', {}, { signal: controller.signal }),
|
|
698
|
+
postAction('providers_list', {}, { signal: controller.signal }),
|
|
699
|
+
fetch('/iris/api/state', { signal: controller.signal }).then(function (r) {
|
|
700
|
+
if (!r.ok) throw new Error('健康状态读取失败(HTTP ' + r.status + ')');
|
|
701
|
+
return r.json();
|
|
702
|
+
})
|
|
703
|
+
]).then(function (values) {
|
|
704
|
+
if (version !== readVersion.current) return null;
|
|
705
|
+
if (!values[0].ok || values[0].d.ok !== true || !values[1].ok || values[1].d.ok !== true) throw new Error('配置读取失败,请重新检查');
|
|
706
|
+
var data = { ...values[0].d, providers: values[1].d.providers, health: values[2].health };
|
|
707
|
+
snapshotPair[1](data); freshPair[1](true); checkedPair[1](new Date().toLocaleTimeString()); return data;
|
|
708
|
+
}).catch(function (error) {
|
|
709
|
+
if (version === readVersion.current) errorPair[1](error.name === 'AbortError' ? '配置检查超时,请重试;未发起模型实测。' : error.message);
|
|
710
|
+
return null;
|
|
711
|
+
}).finally(function () { clearTimeout(timer); controller.abort(); if (version === readVersion.current) loadingPair[1](false); });
|
|
712
|
+
}
|
|
713
|
+
function inspect(row, target) {
|
|
714
|
+
if (running.current) return;
|
|
715
|
+
expandedPair[1](true);
|
|
716
|
+
selectedPair[1](row.cap); requestedPair[1](target || null); confirmPair[1](null);
|
|
717
|
+
}
|
|
718
|
+
React.useEffect(function () {
|
|
719
|
+
load();
|
|
720
|
+
function open(event) {
|
|
721
|
+
var detail = event.detail || {};
|
|
722
|
+
var row = HEALTH_ROWS.find(function (item) { return item.cap === detail.capability; });
|
|
723
|
+
if (row && !running.current) {
|
|
724
|
+
inspect(row, detail); load();
|
|
725
|
+
var section = document.getElementById('iris-capability-health');
|
|
726
|
+
if (section) section.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
window.addEventListener('iris-capability-inspect', open);
|
|
730
|
+
window.addEventListener('iris-function-configuration-changed', load);
|
|
731
|
+
return function () {
|
|
732
|
+
++readVersion.current;
|
|
733
|
+
window.removeEventListener('iris-capability-inspect', open);
|
|
734
|
+
window.removeEventListener('iris-function-configuration-changed', load);
|
|
735
|
+
};
|
|
736
|
+
}, []);
|
|
737
|
+
function configure(row) {
|
|
738
|
+
var field = document.querySelector('[data-function="' + row.cap + '"] > select');
|
|
739
|
+
if (field) { revealWorkbenchItem(field); field.scrollIntoView({ block: 'center', behavior: 'smooth' }); field.focus({ preventScroll: true }); }
|
|
740
|
+
}
|
|
741
|
+
function openTask(row) {
|
|
742
|
+
var card = document.querySelector('[data-iris-action="' + row.action + '"]');
|
|
743
|
+
if (!card) return;
|
|
744
|
+
revealWorkbenchItem(card);
|
|
745
|
+
if (!card.classList.contains('open')) card.querySelector('.iris-act-head').click();
|
|
746
|
+
card.scrollIntoView({ block: 'start', behavior: 'smooth' });
|
|
747
|
+
}
|
|
748
|
+
function probe(row, option) {
|
|
749
|
+
if (running.current) return;
|
|
750
|
+
running.current = true; confirmPair[1](null);
|
|
751
|
+
requestedPair[1]({ ...option, modelId: option.id });
|
|
752
|
+
var key = row.cap + '|' + option.ref;
|
|
753
|
+
busyPair[1](key); noteResult(key, { phase: 'running', note: '正在核对当前模型范围…' });
|
|
754
|
+
load().then(function (current) {
|
|
755
|
+
var target = inspectionModels(current, row, option).find(function (item) { return item.ref === option.ref; });
|
|
756
|
+
if (!current || !target || !target.canTest || target.taskOnly) {
|
|
757
|
+
noteResult(key, { phase: 'skipped', note: target && target.reason || '配置未确认或调用范围已变化,未发起实测。' }); return;
|
|
758
|
+
}
|
|
759
|
+
noteResult(key, { phase: 'running', note: '正在实测…' + (row.cap === 'image-gen' ? '图片可能需要约一分钟,请勿重复提交。' : '请等待接口返回。') });
|
|
760
|
+
var controller = new AbortController();
|
|
761
|
+
var timer = setTimeout(function () { controller.abort(); }, 90000);
|
|
762
|
+
return postAction('providers_test_model', { id: target.providerId, model_id: target.id, capability: row.cap, confirm_paid: true },
|
|
763
|
+
{ signal: controller.signal }).then(function (r) {
|
|
764
|
+
if (!r.ok || !r.d || r.d.ok !== true || (r.d.skipped !== true && typeof r.d.passed !== 'boolean')) {
|
|
765
|
+
noteResult(key, { phase: 'unknown', note: r.d && r.d.error || '未收到有效检测结果;请先核对供应商记录,避免重复计费。' }); return;
|
|
766
|
+
}
|
|
767
|
+
noteResult(key, { phase: r.d.skipped ? 'skipped' : r.d.passed ? 'passed' : 'failed',
|
|
768
|
+
note: String(r.d.text || '').replace(/^\[iris\][^\n]*?:/, '') || (r.d.passed ? '接口实测通过' : '接口未通过实测') });
|
|
769
|
+
}).catch(function () {
|
|
770
|
+
noteResult(key, { phase: 'unknown', note: '结果未确认,请先核对供应商记录,避免重复计费。本页不会自动重试。' });
|
|
771
|
+
}).finally(function () { clearTimeout(timer); });
|
|
772
|
+
}).then(function () { return load(); }).finally(function () { running.current = false; busyPair[1](null); });
|
|
773
|
+
}
|
|
774
|
+
var currentHealth = snapshot && snapshot.health;
|
|
775
|
+
if (!currentHealth || (state && state.health && state.health.generatedAt > currentHealth.generatedAt)) currentHealth = state && state.health;
|
|
776
|
+
var viewSnapshot = snapshot && { ...snapshot, health: currentHealth };
|
|
777
|
+
var supported = inspectionSupported(snapshot);
|
|
778
|
+
var row = HEALTH_ROWS.find(function (item) { return item.cap === selected; });
|
|
779
|
+
var modes = { auto: '自动选择', manual: '所选模型', disabled: '已停用' };
|
|
780
|
+
var verifiedCount = HEALTH_ROWS.filter(function (item) { return capabilityHealth({ health: currentHealth }, item.cap).status === 'verified'; }).length;
|
|
781
|
+
var blockedCount = HEALTH_ROWS.filter(function (item) { return capabilityHealth({ health: currentHealth }, item.cap).status === 'failed'; }).length;
|
|
782
|
+
var summary = busy ? '正在检测,请等待本次结果。' : errorPair[0] ? '配置读取失败,展开查看详情。'
|
|
783
|
+
: snapshot && !supported ? '服务端需重新加载,展开查看说明。'
|
|
784
|
+
: currentHealth ? '6 项能力 · ' + verifiedCount + ' 项近期成功' + (blockedCount ? ' · ' + blockedCount + ' 项调用受阻' : '')
|
|
785
|
+
: '正在读取能力状态…';
|
|
786
|
+
return React.createElement('section', { className: 'iris-health', id: 'iris-capability-health', 'aria-label': '能力状态与检测' },
|
|
787
|
+
React.createElement('div', { className: 'iris-health-head' },
|
|
788
|
+
React.createElement('div', {}, React.createElement('h2', {}, '能力状态与检测'),
|
|
789
|
+
React.createElement('p', { className: 'iris-health-subtitle', role: 'status' }, summary)),
|
|
790
|
+
React.createElement('button', { className: 'iris-health-btn', 'aria-expanded': expanded, 'aria-controls': 'iris-health-content',
|
|
791
|
+
onClick: function () { expandedPair[1](!expanded); confirmPair[1](null); } }, expanded ? '收起检测' : '展开检测')),
|
|
792
|
+
React.createElement('div', { className: 'iris-health-content', id: 'iris-health-content', hidden: !expanded },
|
|
793
|
+
React.createElement('div', { className: 'iris-health-toolbar' },
|
|
794
|
+
React.createElement('span', { className: 'iris-health-caption' }, '配置检查只读;实测前单独确认。'),
|
|
795
|
+
React.createElement('button', { className: 'iris-health-btn', 'data-health-refresh': true, disabled: loading || !!busy, onClick: function () { load(); } },
|
|
796
|
+
loading ? '正在检查配置…' : '检查配置')),
|
|
797
|
+
errorPair[0] ? React.createElement('div', { className: 'iris-health-notice error', role: 'alert' }, errorPair[0]) : null,
|
|
798
|
+
snapshot && !supported ? React.createElement('div', { className: 'iris-health-notice', role: 'status' },
|
|
799
|
+
'服务端尚未加载新版功能池接口。请重启 DSH 后刷新页面;当前只展示历史状态,暂停模型实测。') : null,
|
|
800
|
+
React.createElement('div', { className: 'iris-health-grid' }, HEALTH_ROWS.map(function (item) {
|
|
801
|
+
var health = capabilityHealth({ health: currentHealth }, item.cap);
|
|
802
|
+
var candidates = inspectionModels(viewSnapshot, item);
|
|
803
|
+
var verified = candidates.filter(function (model) { return model.health.status === 'verified'; }).length;
|
|
804
|
+
var ready = snapshot && snapshot.readiness && snapshot.readiness[item.cap];
|
|
805
|
+
var policy = snapshot && snapshot.policies && snapshot.policies[item.cap];
|
|
806
|
+
return React.createElement('button', { key: item.cap, className: 'iris-health-row ' + healthClass(health) + (selected === item.cap ? ' active' : ''),
|
|
807
|
+
'data-health-capability': item.cap, 'aria-expanded': selected === item.cap, 'aria-controls': 'iris-health-detail', disabled: !!busy,
|
|
808
|
+
onClick: function () { inspect(item); } },
|
|
809
|
+
React.createElement('div', { className: 'iris-health-card-top' },
|
|
810
|
+
React.createElement('span', { className: 'iris-health-icon' }, capabilityIcon(item)),
|
|
811
|
+
React.createElement('span', { className: 'iris-health-caption' }, policy ? modes[policy.mode] : '读取配置')),
|
|
812
|
+
React.createElement('div', { className: 'iris-health-card-main' }, React.createElement('div', { className: 'iris-health-name' }, item.label),
|
|
813
|
+
React.createElement('div', { className: 'iris-health-state', title: healthText(health) }, snapshot || state ? healthText(health) : '正在读取状态…')),
|
|
814
|
+
React.createElement('div', { className: 'iris-health-card-foot' },
|
|
815
|
+
React.createElement('span', {}, ready ? candidates.length ? ready.candidateCount + ' 个可用 · ' + verified + '/' + candidates.length + ' 近期成功' : '暂无候选模型' : '等待配置检查'),
|
|
816
|
+
React.createElement('span', { className: 'iris-health-link' }, '查看检测 ›')));
|
|
817
|
+
})),
|
|
818
|
+
React.createElement('p', { className: 'iris-health-footer', role: 'status' },
|
|
819
|
+
(checkedPair[0] ? '配置检查于 ' + checkedPair[0] + ' · ' : '') + '配置检查只读,实测需单独确认。绿色有效 7 天;不会后台探测。'),
|
|
820
|
+
row ? React.createElement('div', { className: 'iris-health-detail', id: 'iris-health-detail' },
|
|
821
|
+
React.createElement('div', { className: 'iris-health-detail-head' },
|
|
822
|
+
React.createElement('div', {}, React.createElement('h3', {}, row.label + '能力检测'),
|
|
823
|
+
React.createElement('p', { className: 'iris-health-caption' }, row.sample)),
|
|
824
|
+
React.createElement('button', { className: 'iris-health-btn', disabled: !!busy, 'aria-label': '收起能力检测',
|
|
825
|
+
onClick: function () { selectedPair[1](null); confirmPair[1](null); } }, '收起')),
|
|
826
|
+
React.createElement('div', { className: 'iris-health-models' },
|
|
827
|
+
inspectionModels(viewSnapshot, row, requested).map(function (option) {
|
|
828
|
+
var key = row.cap + '|' + option.ref;
|
|
829
|
+
var result = results[key];
|
|
830
|
+
var pending = confirmation && confirmation.ref === option.ref && confirmation.cap === row.cap;
|
|
831
|
+
return React.createElement('div', { key: option.ref, className: 'iris-health-model', 'data-health-model': option.ref, 'aria-busy': busy === key },
|
|
832
|
+
React.createElement('div', { className: 'iris-health-model-head' },
|
|
833
|
+
React.createElement('div', { className: 'iris-health-model-title' }, React.createElement('strong', {}, option.id),
|
|
834
|
+
React.createElement('span', { className: 'iris-health-caption' }, option.providerName + ' · ' + healthText(option.health))),
|
|
835
|
+
React.createElement('button', { className: 'iris-health-btn' + (!option.taskOnly ? ' primary' : ''),
|
|
836
|
+
disabled: !fresh || !option.canTest || !!busy || !!(result && result.phase === 'unknown'),
|
|
837
|
+
onClick: function () { if (running.current) return; if (option.taskOnly) openTask(row); else confirmPair[1]({ cap: row.cap, ref: option.ref }); } },
|
|
838
|
+
option.taskOnly ? '前往功能验证 ↗' : result && result.phase === 'running' ? '正在实测…' : '实测此模型')),
|
|
839
|
+
option.reason ? React.createElement('p', { className: 'iris-health-caption' }, option.reason) : null,
|
|
840
|
+
option.ref === 'host:current-vision' ? React.createElement('p', { className: 'iris-health-caption' },
|
|
841
|
+
'宿主视觉通过真实图片任务验证,请在打开的表单中选择 DSH 当前默认模型。') : null,
|
|
842
|
+
option.health.rateLimited ? React.createElement('p', { className: 'iris-health-caption' }, '显式实测成功后,会解除此模型的冷却或额度停用。') : null,
|
|
843
|
+
option.modelUnavailable || option.health.modelUnavailable ? React.createElement('p', { className: 'iris-health-caption' },
|
|
844
|
+
'当前连接曾返回模型不存在,已暂停自动选用;同步目录不会代替实测。单独实测成功后恢复,可能产生费用。') : null,
|
|
845
|
+
result ? React.createElement('div', { className: 'iris-health-result ' + result.phase, role: result.phase === 'unknown' ? 'alert' : 'status', 'aria-live': 'polite' },
|
|
846
|
+
result.phase === 'running' ? React.createElement('span', { className: 'iris-health-spinner', 'aria-hidden': true }) : null,
|
|
847
|
+
React.createElement('span', {}, (result.phase === 'passed' ? '实测通过 · ' : result.phase === 'failed' ? '实测未通过 · '
|
|
848
|
+
: result.phase === 'skipped' ? '未发起实测 · ' : '') + result.note)) : null,
|
|
849
|
+
pending ? React.createElement('div', { className: 'iris-health-confirm', role: 'group', 'aria-label': '确认模型实测' },
|
|
850
|
+
React.createElement('strong', {}, '确认向此模型发起一次真实请求?'),
|
|
851
|
+
React.createElement('p', {}, row.sample + ' 可能产生费用。本次仅验证 ' + option.id + ',失败不会改用其他模型。'),
|
|
852
|
+
React.createElement('div', { className: 'iris-health-confirm-actions' },
|
|
853
|
+
React.createElement('button', { className: 'iris-health-btn primary', disabled: !!busy, onClick: function () { probe(row, option); } }, '确认并实测'),
|
|
854
|
+
React.createElement('button', { className: 'iris-health-btn', disabled: !!busy, onClick: function () { confirmPair[1](null); } }, '取消'))) : null);
|
|
855
|
+
}),
|
|
856
|
+
!inspectionModels(viewSnapshot, row, requested).length ? React.createElement('div', { className: 'iris-health-notice' },
|
|
857
|
+
snapshot && snapshot.readiness && snapshot.readiness[row.cap] && snapshot.readiness[row.cap].note || '当前没有纳入调用范围的模型,请先配置此功能。') : null),
|
|
858
|
+
React.createElement('div', { className: 'iris-health-confirm-actions' },
|
|
859
|
+
React.createElement('button', { className: 'iris-health-btn', disabled: !!busy, onClick: function () { configure(row); } }, '查看功能设置'),
|
|
860
|
+
React.createElement('span', { className: 'iris-health-caption' }, '只检测当前调用范围内的模型;历史成功不代表所有候选均可用。'))) : null));
|
|
546
861
|
}
|
|
547
862
|
|
|
548
863
|
/* ---- 任务详情抽屉:按需拉取 /iris/api/task/:id ---- */
|
|
@@ -804,8 +1119,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
804
1119
|
}
|
|
805
1120
|
|
|
806
1121
|
/* ---- 供应商管理(阶段 6:增删 key + 模型列表 + 测试) ---- */
|
|
807
|
-
function postAction(action, body) {
|
|
808
|
-
return fetch('/iris/api/actions/' + action, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {})
|
|
1122
|
+
function postAction(action, body, options) {
|
|
1123
|
+
return fetch('/iris/api/actions/' + action, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}),
|
|
1124
|
+
...(options && options.signal ? { signal: options.signal } : {}) })
|
|
809
1125
|
.then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); });
|
|
810
1126
|
}
|
|
811
1127
|
|
|
@@ -813,9 +1129,6 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
813
1129
|
var CAP_SHORT = { 'image-gen': '图', 'video-gen': '视', 'tts': '音', 'music-gen': '乐', 'transcribe': '转', 'vision': '视👁' };
|
|
814
1130
|
function ModelPool(props) {
|
|
815
1131
|
var p = props.provider;
|
|
816
|
-
var addPair = React.useState('');
|
|
817
|
-
var newId = addPair[0];
|
|
818
|
-
var setNewId = addPair[1];
|
|
819
1132
|
var models = p.models || [];
|
|
820
1133
|
function setImageProtocol(m, value) {
|
|
821
1134
|
postAction('providers_set_model_image_protocol', { id: p.id, model_id: m.id, imageProtocol: value })
|
|
@@ -835,18 +1148,23 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
835
1148
|
return React.createElement('button', {
|
|
836
1149
|
key: c,
|
|
837
1150
|
className: cls,
|
|
838
|
-
title: title + '
|
|
1151
|
+
title: title + ';打开能力检测',
|
|
1152
|
+
'aria-label': (HEALTH_ROWS.find(function (row) { return row.cap === c; }) || { label: c }).label + '检测 · ' + m.id,
|
|
839
1153
|
onClick: function () { props.testModel(p, m, c); }
|
|
840
|
-
}, c + symbol);
|
|
1154
|
+
}, (HEALTH_ROWS.find(function (row) { return row.cap === c; }) || { label: c }).label + ' ' + symbol);
|
|
841
1155
|
}
|
|
842
1156
|
var rows = models.map(function (m) {
|
|
843
1157
|
var caps = m.capabilities || [];
|
|
844
1158
|
var blocked = caps.map(function (c) { return m.health && m.health[c]; }).find(function (health) { return health && health.rateLimited; });
|
|
845
1159
|
return React.createElement('div', { key: m.id, className: 'iris-pm-mrow' },
|
|
846
|
-
React.createElement('span', { className: 'iris-pm-mname' }, m.id, m.source === '
|
|
847
|
-
|
|
1160
|
+
React.createElement('span', { className: 'iris-pm-mname' }, m.id, m.source === 'discovered'
|
|
1161
|
+
? React.createElement('span', { className: 'iris-pm-mtag' }, '待确认用途') : null,
|
|
1162
|
+
m.catalogStatus === 'unlisted' ? React.createElement('span', { className: 'iris-pm-mtag' }, '目录未列出') : null),
|
|
1163
|
+
React.createElement('span', { className: 'iris-pm-mcaps' }, caps.length ? caps.map(function (c) {
|
|
1164
|
+
return m.directoryOnly ? React.createElement('span', { key: c }, (HEALTH_ROWS.find(function (row) { return row.cap === c; }) || { label: c }).label) : verBadge(m, c);
|
|
1165
|
+
}) : '未标注功能'),
|
|
848
1166
|
React.createElement(ModelCapabilityEditor, { provider: p, model: m, onNote: props.onNote, onDone: props.onDone }),
|
|
849
|
-
caps.includes('image-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '图片协议 ',
|
|
1167
|
+
!m.directoryOnly && caps.includes('image-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '图片协议 ',
|
|
850
1168
|
React.createElement('select', { value: m.imageProtocol || 'auto', onChange: function (e) { setImageProtocol(m, e.target.value); } },
|
|
851
1169
|
React.createElement('option', { value: 'auto' }, '继承账号默认(' + (p.mediaProtocol || '自动') + ')'),
|
|
852
1170
|
React.createElement('option', { value: 'dashscope' }, 'DashScope'),
|
|
@@ -854,44 +1172,54 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
854
1172
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
855
1173
|
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')),
|
|
856
1174
|
m.imageRouting && m.imageRouting.protocolInferred ? ' · 协议待确认' : null) : null,
|
|
857
|
-
caps.includes('music-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '音乐协议 ',
|
|
1175
|
+
!m.directoryOnly && caps.includes('music-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '音乐协议 ',
|
|
858
1176
|
React.createElement('select', { value: m.musicProtocol || '', onChange: function (e) { setMusicProtocol(m, e.target.value); } },
|
|
859
1177
|
React.createElement('option', { value: '' }, '请选择音乐协议'),
|
|
860
1178
|
React.createElement('option', { value: 'gemini-web-music' }, 'Chat Completions(内嵌音频)'))) : null,
|
|
861
|
-
caps.includes('vision') || (caps.includes('image-gen') && m.imageRouting && m.imageRouting.editSupported) ? React.createElement(VisionInputEditor, { key: 'vision-input', provider: p, model: m,
|
|
1179
|
+
!m.directoryOnly && (caps.includes('vision') || (caps.includes('image-gen') && m.imageRouting && m.imageRouting.editSupported)) ? React.createElement(VisionInputEditor, { key: 'vision-input', provider: p, model: m,
|
|
862
1180
|
onNote: props.onNote, onDone: props.onDone }) : null,
|
|
863
1181
|
blocked ? React.createElement('span', { className: 'iris-pm-ver failed ' + healthClass(blocked), title: healthText(blocked) }, rateLimitLabel(blocked)) : null,
|
|
864
|
-
React.createElement('button', { className: 'iris-pm-btn danger', title: '从池移除', onClick: function () { props.removeModel(p, m); } }, '✕'));
|
|
1182
|
+
!m.directoryOnly ? React.createElement('button', { className: 'iris-pm-btn danger', title: '从池移除', onClick: function () { props.removeModel(p, m); } }, '✕') : null);
|
|
865
1183
|
});
|
|
866
1184
|
return React.createElement('div', { className: 'iris-pm-pool' },
|
|
867
|
-
React.createElement('div', { className: 'iris-pm-note' }, '模型池(' + models.length + '
|
|
868
|
-
|
|
869
|
-
React.createElement('div', { className: 'iris-
|
|
870
|
-
|
|
871
|
-
|
|
1185
|
+
React.createElement('div', { className: 'iris-pm-note' }, '模型池(' + models.filter(function (m) { return !m.directoryOnly; }).length + ')'
|
|
1186
|
+
+ (models.some(function (m) { return m.directoryOnly; }) ? ' · 目录待确认 ' + models.filter(function (m) { return m.directoryOnly; }).length + ' 项' : '') + ':'),
|
|
1187
|
+
rows.length ? React.createElement('div', { className: 'iris-pm-mrows' }, rows) : React.createElement('div', { className: 'iris-pm-note' }, '暂无模型 — 展开「模型目录」同步,或在下方手动添加'),
|
|
1188
|
+
React.createElement(ModelUsageForm, { provider: p, setupSuggested: props.setupSuggested,
|
|
1189
|
+
onNote: props.onNote, onDone: props.onDone }));
|
|
872
1190
|
}
|
|
873
1191
|
|
|
874
|
-
function
|
|
875
|
-
var
|
|
876
|
-
var capsPair = React.useState([]);
|
|
1192
|
+
function ModelUsageForm(props) {
|
|
1193
|
+
var namePair = React.useState(props.model ? props.model.id : '');
|
|
1194
|
+
var capsPair = React.useState(props.model ? (props.model.capabilities || []).slice() : []);
|
|
1195
|
+
var autoPair = React.useState(!props.model && props.setupSuggested === true);
|
|
1196
|
+
var musicPair = React.useState(props.model && props.model.musicProtocol || '');
|
|
877
1197
|
var savingPair = React.useState(false);
|
|
878
1198
|
var selected = capsPair[0];
|
|
879
1199
|
var saving = savingPair[0];
|
|
880
1200
|
function save() {
|
|
881
1201
|
if (saving) return;
|
|
1202
|
+
if (!namePair[0].trim()) { props.onNote('请填写模型名称'); return; }
|
|
1203
|
+
if (selected.includes('music-gen') && !musicPair[0]) { props.onNote('请先选择该模型实际支持的音乐协议'); return; }
|
|
882
1204
|
savingPair[1](true);
|
|
883
|
-
postAction('
|
|
1205
|
+
postAction('providers_confirm_model', { id: props.provider.id, model_id: namePair[0].trim(), capabilities: selected,
|
|
1206
|
+
enableAutomatic: autoPair[0], ...(selected.includes('music-gen') ? { musicProtocol: musicPair[0] } : {}) })
|
|
884
1207
|
.then(function (r) {
|
|
885
1208
|
if (r.ok) {
|
|
886
|
-
|
|
887
|
-
props.onNote('
|
|
1209
|
+
if (!props.model) namePair[1]('');
|
|
1210
|
+
props.onNote(r.d.text || '模型用途已确认');
|
|
888
1211
|
props.onDone();
|
|
1212
|
+
window.dispatchEvent(new CustomEvent('iris-function-configuration-changed'));
|
|
889
1213
|
} else props.onNote(r.d.error || '保存失败');
|
|
1214
|
+
}).catch(function () { props.onNote('无法保存,请检查与宿主的连接');
|
|
890
1215
|
}).finally(function () { savingPair[1](false); });
|
|
891
1216
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1217
|
+
return React.createElement('div', { className: 'iris-pm-card' },
|
|
1218
|
+
!props.model ? React.createElement('label', { className: 'iris-pm-field' }, '模型名称',
|
|
1219
|
+
React.createElement('input', { value: namePair[0], placeholder: '发现后选模型,或填写服务提供的模型名',
|
|
1220
|
+
onChange: function (e) { namePair[1](e.target.value); } })) : null,
|
|
1221
|
+
React.createElement('div', { className: 'iris-pm-note' }, '确认此模型用于哪些功能;名称和文字接口本身不证明媒体能力。'),
|
|
1222
|
+
React.createElement('div', { className: 'iris-wb-row' },
|
|
895
1223
|
CAP_ROWS.map(function (row) {
|
|
896
1224
|
return React.createElement('label', { key: row.cap, className: 'iris-pm-note' },
|
|
897
1225
|
React.createElement('input', { type: 'checkbox', value: row.cap, checked: selected.includes(row.cap), disabled: saving,
|
|
@@ -902,9 +1230,25 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
902
1230
|
: previous.filter(function (cap) { return cap !== row.cap; });
|
|
903
1231
|
});
|
|
904
1232
|
} }), row.label);
|
|
905
|
-
}),
|
|
906
|
-
React.createElement('
|
|
907
|
-
|
|
1233
|
+
})),
|
|
1234
|
+
selected.includes('music-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '音乐协议 ',
|
|
1235
|
+
React.createElement('select', { value: musicPair[0], onChange: function (e) { musicPair[1](e.target.value); } },
|
|
1236
|
+
React.createElement('option', { value: '' }, '请选择音乐协议'),
|
|
1237
|
+
React.createElement('option', { value: 'gemini-web-music' }, 'Chat Completions(内嵌音频)'))) : null,
|
|
1238
|
+
React.createElement('label', { className: 'iris-pm-note' }, React.createElement('input', {
|
|
1239
|
+
type: 'checkbox', checked: autoPair[0], onChange: function (e) { autoPair[1](e.target.checked); }
|
|
1240
|
+
}), '同时启用所选功能的自动池(替换这些功能当前的调用方式,保留原手工名单)'),
|
|
1241
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: saving, onClick: save }, saving ? '保存中…' : '保存模型用途'));
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
function ModelCapabilityEditor(props) {
|
|
1245
|
+
var openPair = React.useState(false);
|
|
1246
|
+
if (!openPair[0]) return React.createElement('button', { className: 'iris-pm-btn', title: '确认模型用途,加入功能池',
|
|
1247
|
+
onClick: function () { openPair[1](true); } }, '设置功能');
|
|
1248
|
+
return React.createElement('div', {},
|
|
1249
|
+
React.createElement(ModelUsageForm, { provider: props.provider, model: props.model, onNote: props.onNote,
|
|
1250
|
+
onDone: function () { openPair[1](false); props.onDone(); } }),
|
|
1251
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { openPair[1](false); } }, '取消'));
|
|
908
1252
|
}
|
|
909
1253
|
|
|
910
1254
|
function VisionInputEditor(props) {
|
|
@@ -940,6 +1284,42 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
940
1284
|
React.createElement('span', { className: 'iris-pm-note' }, '用于看图、OCR 每块输入、定位和改图的发送副本。作品原图保留,定位结果换算回原图。'));
|
|
941
1285
|
}
|
|
942
1286
|
|
|
1287
|
+
function ProviderDirectory(props) {
|
|
1288
|
+
var p = props.provider, directory = p.directory || { status: 'unknown' };
|
|
1289
|
+
var busyPair = React.useState(false), notePair = React.useState(''), running = React.useRef(false);
|
|
1290
|
+
function synchronize() {
|
|
1291
|
+
if (running.current || !p.directory) return;
|
|
1292
|
+
running.current = true; busyPair[1](true); notePair[1]('');
|
|
1293
|
+
var controller = new AbortController();
|
|
1294
|
+
var timer = setTimeout(function () { controller.abort(); }, 90000);
|
|
1295
|
+
postAction('providers_sync_directory', { id: p.id, force: true }, { signal: controller.signal }).then(function (r) {
|
|
1296
|
+
if (!r.ok || !r.d || r.d.ok !== true || typeof r.d.synced !== 'boolean') {
|
|
1297
|
+
notePair[1]('同步结果未确认,请刷新状态查看。' + (r.d && r.d.error ? ' ' + String(r.d.error) : '')); return;
|
|
1298
|
+
}
|
|
1299
|
+
notePair[1](r.d.text || (r.d.synced ? '模型目录已同步' : '目录同步失败,保留上次完整目录'));
|
|
1300
|
+
props.onDone();
|
|
1301
|
+
}).catch(function () { notePair[1]('同步结果未确认,请刷新状态查看;本次没有发起模型生成。'); })
|
|
1302
|
+
.finally(function () { clearTimeout(timer); running.current = false; busyPair[1](false); });
|
|
1303
|
+
}
|
|
1304
|
+
var summary = busyPair[0] ? '正在同步…' : directory.checkedAt
|
|
1305
|
+
? (directory.status === 'stale' ? '缓存待更新' : '已同步') + ' · ' + directory.modelCount + ' 个模型 · 目录未列出 ' + (directory.missing || []).length + ' 项'
|
|
1306
|
+
: '尚未同步';
|
|
1307
|
+
return React.createElement('details', { className: 'iris-pm-field', 'data-provider-directory': p.id, 'aria-busy': busyPair[0] },
|
|
1308
|
+
React.createElement('summary', { className: 'iris-pm-note' }, '模型目录 · ' + summary),
|
|
1309
|
+
React.createElement('div', { className: 'iris-wb-row' },
|
|
1310
|
+
React.createElement('span', { className: 'iris-pm-note' }, directory.checkedAt ? '上次同步:' + new Date(directory.checkedAt).toLocaleString() : '目录与模型用途分别保存'),
|
|
1311
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: busyPair[0] || !p.directory || !p.enabled,
|
|
1312
|
+
onClick: synchronize }, busyPair[0] ? '正在同步…' : '同步目录')),
|
|
1313
|
+
!p.directory ? React.createElement('p', { className: 'iris-pm-note' }, '请先让 DSH 加载新版 Iris,再同步目录。') : null,
|
|
1314
|
+
directory.error && !notePair[0] ? React.createElement('p', { className: 'iris-pm-note', role: 'status' }, '上次同步未完成,保留旧目录:' + directory.error) : null,
|
|
1315
|
+
notePair[0] ? React.createElement('p', { className: 'iris-pm-note', role: 'status' }, notePair[0]) : null,
|
|
1316
|
+
React.createElement('p', { className: 'iris-pm-note' }, '同步只读取供应商目录,新模型仍需确认用途。目录未列出不等于下线;实测与生成需单独发起。'),
|
|
1317
|
+
(directory.added || []).length ? React.createElement('p', { className: 'iris-pm-note' }, '本次新增:' + directory.added.slice(0, 5).join('、')
|
|
1318
|
+
+ (directory.added.length > 5 ? ' 等 ' + directory.added.length + ' 项,完整条目见下方列表' : '')) : null,
|
|
1319
|
+
(directory.missing || []).length ? React.createElement('p', { className: 'iris-pm-note' }, '目录未列出:' + directory.missing.slice(0, 5).join('、')
|
|
1320
|
+
+ (directory.missing.length > 5 ? ' 等 ' + directory.missing.length + ' 项,原配置继续保留' : '')) : null);
|
|
1321
|
+
}
|
|
1322
|
+
|
|
943
1323
|
function ProviderEndpointEditor(props) {
|
|
944
1324
|
var p = props.provider;
|
|
945
1325
|
var valuePair = React.useState(p.mediaBaseUrl || '');
|
|
@@ -970,11 +1350,22 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
970
1350
|
var adding = addingPair[0];
|
|
971
1351
|
var setAdding = addingPair[1];
|
|
972
1352
|
var newForm = React.useState({ name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', type: 'openai', mediaProtocol: 'auto' });
|
|
1353
|
+
var hostConnections = React.useState([]);
|
|
1354
|
+
var hostChoice = React.useState('');
|
|
1355
|
+
var setupPair = React.useState(false);
|
|
973
1356
|
var notePair = React.useState('');
|
|
974
1357
|
var note = notePair[0];
|
|
975
1358
|
var setNote = notePair[1];
|
|
976
1359
|
function refresh() {
|
|
977
|
-
postAction('providers_list', {}).then(function (r) { if (r.ok && r.d.providers)
|
|
1360
|
+
postAction('providers_list', {}).then(function (r) { if (r.ok && r.d.providers) {
|
|
1361
|
+
setList(r.d.providers); setupPair[1](r.d.setupSuggested === true);
|
|
1362
|
+
} });
|
|
1363
|
+
}
|
|
1364
|
+
function loadHostConnections() {
|
|
1365
|
+
postAction('providers_host_connections', {}).then(function (r) {
|
|
1366
|
+
if (r.ok) { hostConnections[1](r.d.connections || []); if (r.d.note) setNote(r.d.note); }
|
|
1367
|
+
else setNote(r.d.error || '无法读取 DSH 连接');
|
|
1368
|
+
});
|
|
978
1369
|
}
|
|
979
1370
|
React.useEffect(function () {
|
|
980
1371
|
refresh();
|
|
@@ -983,9 +1374,20 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
983
1374
|
}, []);
|
|
984
1375
|
function addProvider() {
|
|
985
1376
|
var f = newForm[0];
|
|
1377
|
+
if (hostChoice[0]) {
|
|
1378
|
+
var connection = hostConnections[0].find(function (item) { return JSON.stringify([item.settingsNs, item.provider]) === hostChoice[0]; });
|
|
1379
|
+
if (!connection || !connection.available) { setNote('请重新选择可用的 DSH 连接'); return; }
|
|
1380
|
+
postAction('providers_upsert', { name: f.name || connection.name, type: 'openai', enabled: true,
|
|
1381
|
+
mediaProtocol: f.mediaProtocol, dshProvider: { provider: connection.provider, settingsNs: connection.settingsNs } })
|
|
1382
|
+
.then(function (r) {
|
|
1383
|
+
if (r.ok) { setAdding(false); openPair[1](r.d.providerId); setNote('已引用 DSH 连接,请在下方确认模型用途,即可启用自动池'); refresh(); }
|
|
1384
|
+
else setNote(r.d.error || '添加失败');
|
|
1385
|
+
});
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
986
1388
|
if (!f.baseUrl || !f.apiKey) { setNote('baseUrl 和 apiKey 必填'); return; }
|
|
987
1389
|
postAction('providers_upsert', f).then(function (r) {
|
|
988
|
-
if (r.ok) { setAdding(false); setNote('
|
|
1390
|
+
if (r.ok) { setAdding(false); openPair[1](r.d.providerId); setNote('已连接,请在下方确认模型用途'); refresh(); }
|
|
989
1391
|
else setNote(r.d.error || '添加失败');
|
|
990
1392
|
});
|
|
991
1393
|
}
|
|
@@ -999,21 +1401,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
999
1401
|
if (!window.confirm('删除供应商「' + (p.name || p.id) + '」?此操作不可撤销。')) return;
|
|
1000
1402
|
postAction('providers_remove', { id: p.id }).then(function (r) { if (r.ok) refresh(); else setNote(r.d.error || '删除失败'); });
|
|
1001
1403
|
}
|
|
1002
|
-
function testVision(p) {
|
|
1003
|
-
if (!window.confirm('视觉实测会向供应商发送一张内置测试图,可能产生费用。继续吗?')) return;
|
|
1004
|
-
setNote('测试「' + (p.name || p.id) + '」的视觉能力…');
|
|
1005
|
-
postAction('providers_test_vision', { id: p.id, confirm_paid: true }).then(function (r) { setNote(r.d.text || r.d.error || '测试完成'); refresh(); });
|
|
1006
|
-
}
|
|
1007
|
-
function discover(p) {
|
|
1008
|
-
setNote('正在发现「' + (p.name || p.id) + '」的模型…');
|
|
1009
|
-
postAction('providers_discover', { id: p.id }).then(function (r) { setNote(r.d.text || r.d.error || '发现完成'); refresh(); });
|
|
1010
|
-
}
|
|
1011
1404
|
function testModel(p, m, capability) {
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
postAction('providers_test_model', { id: p.id, model_id: m.id, capability: capability, confirm_paid: !noProbe })
|
|
1016
|
-
.then(function (r) { setNote(r.d.text || r.d.error || ''); refresh(); });
|
|
1405
|
+
window.dispatchEvent(new CustomEvent('iris-capability-inspect', {
|
|
1406
|
+
detail: { capability: capability, providerId: p.id, modelId: m.id }
|
|
1407
|
+
}));
|
|
1017
1408
|
}
|
|
1018
1409
|
function setProtocol(p, value) {
|
|
1019
1410
|
postAction('providers_upsert', { id: p.id, mediaProtocol: value }).then(function (r) {
|
|
@@ -1024,16 +1415,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1024
1415
|
if (!window.confirm('从池中移除模型「' + m.id + '」?')) return;
|
|
1025
1416
|
postAction('providers_remove_model', { id: p.id, model_id: m.id }).then(function (r) { setNote(r.d.text || r.d.error || ''); refresh(); });
|
|
1026
1417
|
}
|
|
1027
|
-
function addModel(p, modelId) {
|
|
1028
|
-
if (!modelId) { setNote('模型名不能为空'); return; }
|
|
1029
|
-
postAction('providers_add_model', { id: p.id, model_id: modelId }).then(function (r) { setNote(r.d.text || r.d.error || ''); refresh(); });
|
|
1030
|
-
}
|
|
1031
1418
|
var cards = (list || []).map(function (p) {
|
|
1032
1419
|
var open = openPair[0] === p.id;
|
|
1033
1420
|
var body = null;
|
|
1034
1421
|
if (open) {
|
|
1035
|
-
body = React.createElement('div', { className: 'iris-pm-body' },
|
|
1422
|
+
body = React.createElement('div', { className: 'iris-pm-body', id: 'iris-provider-' + p.id },
|
|
1036
1423
|
React.createElement('div', { className: 'iris-pm-note' }, 'Key ' + (p.apiKeyHint || '未配置') + ' · ' + (p.type || '') + ' · ' + (p.mediaProtocol || '')),
|
|
1424
|
+
p.dshProvider ? React.createElement('div', { className: 'iris-pm-note' }, '引用 DSH:' + p.dshProvider.provider
|
|
1425
|
+
+ ' · 地址和密钥由宿主管理;' + (p.connection && p.connection.available ? '连接可用' : ((p.connection && p.connection.reason) || '连接不可用'))) : null,
|
|
1037
1426
|
p.protocolInferred ? React.createElement('div', { className: 'iris-pm-note' }, '协议为推断值,请确认') : null,
|
|
1038
1427
|
React.createElement(ProviderEndpointEditor, { provider: p, onNote: setNote, onDone: refresh }),
|
|
1039
1428
|
React.createElement(VisionInputEditor, { provider: p, onNote: setNote, onDone: refresh }),
|
|
@@ -1045,14 +1434,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1045
1434
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
1046
1435
|
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图'))),
|
|
1047
1436
|
React.createElement('div', { className: 'iris-pm-note' }, '能力: ' + ((p.capabilities || []).join(' / ') || '无')),
|
|
1048
|
-
React.createElement(
|
|
1437
|
+
React.createElement(ProviderDirectory, { provider: p, onDone: refresh }),
|
|
1438
|
+
React.createElement(ModelPool, { provider: p, setupSuggested: setupPair[0], onNote: setNote, onDone: refresh, testModel: testModel, removeModel: removeModel }),
|
|
1049
1439
|
React.createElement('div', { className: 'iris-wb-row' },
|
|
1050
|
-
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { discover(p); } }, '发现模型'),
|
|
1051
1440
|
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { toggle(p); } }, p.enabled ? '停用' : '启用'),
|
|
1052
1441
|
React.createElement('button', { className: 'iris-pm-btn danger', onClick: function () { remove(p); } }, '删除')));
|
|
1053
1442
|
}
|
|
1054
1443
|
return React.createElement('div', { key: p.id, className: 'iris-pm-card' + (open ? ' open' : '') },
|
|
1055
|
-
React.createElement('
|
|
1444
|
+
React.createElement('button', { type: 'button', className: 'iris-pm-top', 'aria-expanded': open, 'aria-controls': 'iris-provider-' + p.id,
|
|
1445
|
+
onClick: function () { setOpen(open ? null : p.id); } },
|
|
1056
1446
|
React.createElement('span', { className: 'iris-pm-name' }, (p.name || p.id) + (p.enabled ? '' : '(停用)')),
|
|
1057
1447
|
React.createElement('span', { className: 'iris-pm-btn' }, open ? '收起' : '管理')),
|
|
1058
1448
|
body);
|
|
@@ -1060,12 +1450,19 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1060
1450
|
var addBlock = null;
|
|
1061
1451
|
if (adding) {
|
|
1062
1452
|
addBlock = React.createElement('div', { className: 'iris-pm-card' },
|
|
1453
|
+
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '连接来源'),
|
|
1454
|
+
React.createElement('select', { value: hostChoice[0], onChange: function (e) { hostChoice[1](e.target.value); } },
|
|
1455
|
+
React.createElement('option', { value: '' }, '手动填写供应商'),
|
|
1456
|
+
hostConnections[0].map(function (connection) { return React.createElement('option', {
|
|
1457
|
+
key: JSON.stringify([connection.settingsNs, connection.provider]), value: JSON.stringify([connection.settingsNs, connection.provider]), disabled: !connection.available
|
|
1458
|
+
}, 'DSH · ' + connection.name + (connection.available ? '' : '(' + connection.reason + ')')); }))),
|
|
1459
|
+
hostChoice[0] ? React.createElement('div', { className: 'iris-pm-note' }, '地址与密钥随 DSH 连接更新,不保存密钥副本。请选择该服务实际支持的媒体协议;模型能力仍需明确配置。') : null,
|
|
1063
1460
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '名称'), React.createElement('input', { value: newForm[0].name, placeholder: '如 阿里云百炼', onChange: function (e) { newForm[1]({ ...newForm[0], name: e.target.value }); } })),
|
|
1064
|
-
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'Base URL *'), React.createElement('input', { value: newForm[0].baseUrl, placeholder: 'https://dashscope.aliyuncs.com/compatible-mode/v1', onChange: function (e) { newForm[1]({ ...newForm[0], baseUrl: e.target.value }); } })),
|
|
1065
|
-
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体 Base URL(可选)'), React.createElement('input', { value: newForm[0].mediaBaseUrl, placeholder: 'Workspace 媒体端点;留空沿用 Base URL', onChange: function (e) { newForm[1]({ ...newForm[0], mediaBaseUrl: e.target.value }); } })),
|
|
1066
|
-
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'API Key *'), React.createElement('input', { type: 'password', value: newForm[0].apiKey, placeholder: 'sk-...', onChange: function (e) { newForm[1]({ ...newForm[0], apiKey: e.target.value }); } })),
|
|
1461
|
+
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'Base URL *'), React.createElement('input', { value: newForm[0].baseUrl, placeholder: 'https://dashscope.aliyuncs.com/compatible-mode/v1', onChange: function (e) { newForm[1]({ ...newForm[0], baseUrl: e.target.value }); } })) : null,
|
|
1462
|
+
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体 Base URL(可选)'), React.createElement('input', { value: newForm[0].mediaBaseUrl, placeholder: 'Workspace 媒体端点;留空沿用 Base URL', onChange: function (e) { newForm[1]({ ...newForm[0], mediaBaseUrl: e.target.value }); } })) : null,
|
|
1463
|
+
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'API Key *'), React.createElement('input', { type: 'password', value: newForm[0].apiKey, placeholder: 'sk-...', onChange: function (e) { newForm[1]({ ...newForm[0], apiKey: e.target.value }); } })) : null,
|
|
1067
1464
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体协议'), React.createElement('select', { value: newForm[0].mediaProtocol, onChange: function (e) { newForm[1]({ ...newForm[0], mediaProtocol: e.target.value }); } },
|
|
1068
|
-
React.createElement('option', { value: 'auto' }, '自动(按媒体 Base URL 安全判断)'),
|
|
1465
|
+
React.createElement('option', { value: 'auto' }, hostChoice[0] ? '请选择该服务支持的媒体协议' : '自动(按媒体 Base URL 安全判断)'),
|
|
1069
1466
|
React.createElement('option', { value: 'dashscope' }, 'DashScope(阿里云官方)'),
|
|
1070
1467
|
React.createElement('option', { value: 'openai-images' }, 'OpenAI Images 兼容'),
|
|
1071
1468
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
@@ -1077,7 +1474,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1077
1474
|
return React.createElement('div', { className: 'iris-pm' },
|
|
1078
1475
|
React.createElement('div', { className: 'iris-pm-head' },
|
|
1079
1476
|
React.createElement('span', { className: 'iris-wb-muted' }, (list ? list.length : 0) + ' 个供应商'),
|
|
1080
|
-
React.createElement('button', { className: 'iris-pm-add', onClick: function () { setAdding(!adding); } }, adding ? '收起' : '+ 添加供应商')),
|
|
1477
|
+
React.createElement('button', { className: 'iris-pm-add', onClick: function () { if (!adding) { hostChoice[1](''); loadHostConnections(); } setAdding(!adding); } }, adding ? '收起' : '+ 添加供应商')),
|
|
1081
1478
|
addBlock,
|
|
1082
1479
|
cards.length ? cards : React.createElement('div', { className: 'iris-wb-empty' }, '暂无供应商 — 点右上角「+ 添加供应商」'),
|
|
1083
1480
|
note ? React.createElement('div', { className: 'iris-pm-act' }, note) : null);
|
|
@@ -1169,9 +1566,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1169
1566
|
var setModelInfo = modelPair[1];
|
|
1170
1567
|
var irisState = useIrisState(5000);
|
|
1171
1568
|
function setVal(key, value) { var nv = {}; Object.keys(vals).forEach(function (k) { nv[k] = vals[k]; }); nv[key] = value; setVals(nv); }
|
|
1172
|
-
function loadModels() {
|
|
1569
|
+
function loadModels(event) {
|
|
1173
1570
|
if (!capability) return;
|
|
1174
|
-
|
|
1571
|
+
var snapshot = event && event.detail && event.detail.functionAssignments;
|
|
1572
|
+
(snapshot ? Promise.resolve({ ok: true, d: snapshot }) : postAction('assignments_get', {})).then(function (r) {
|
|
1175
1573
|
if (!r.ok || !r.d) return;
|
|
1176
1574
|
var opts = (r.d.poolByCapability && r.d.poolByCapability[capability]) || [];
|
|
1177
1575
|
var ord = (r.d.order && r.d.order[capability]) || [];
|
|
@@ -1179,10 +1577,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1179
1577
|
var assignedRef = ord[0] || '';
|
|
1180
1578
|
var assignedOpt = opts.find(function (o) { return o.ref === assignedRef; });
|
|
1181
1579
|
// 不再自动填 vals.model:留空 = 用能力分配的解析结果(避免绕过分配)
|
|
1182
|
-
setModelInfo({ assigned: assignedOpt ? assignedOpt.label : '', assignedRef: assignedRef, options: opts, loaded: true
|
|
1580
|
+
setModelInfo({ assigned: assignedOpt ? assignedOpt.label : '', assignedRef: assignedRef, options: opts, loaded: true,
|
|
1581
|
+
mode: r.d.policies && r.d.policies[capability] && r.d.policies[capability].mode,
|
|
1582
|
+
readiness: r.d.readiness && r.d.readiness[capability] });
|
|
1183
1583
|
});
|
|
1184
1584
|
}
|
|
1185
|
-
React.useEffect(function () {
|
|
1585
|
+
React.useEffect(function () {
|
|
1586
|
+
if (!open || !capability) return;
|
|
1587
|
+
loadModels();
|
|
1588
|
+
window.addEventListener('iris-function-configuration-changed', loadModels);
|
|
1589
|
+
return function () { window.removeEventListener('iris-function-configuration-changed', loadModels); };
|
|
1590
|
+
}, [open]);
|
|
1186
1591
|
function submit() {
|
|
1187
1592
|
if (run === 'running') return;
|
|
1188
1593
|
setRun('running');
|
|
@@ -1199,13 +1604,18 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1199
1604
|
.catch(function () { setRun({ ok: false, text: '网络错误' }); });
|
|
1200
1605
|
}
|
|
1201
1606
|
var cardHealth = capability ? capabilityHealth(irisState, capability) : { status: 'verified' };
|
|
1607
|
+
// Host references can be ready without raw endpoint/key fields in the state snapshot.
|
|
1608
|
+
if (modelInfo && modelInfo.readiness) {
|
|
1609
|
+
if (modelInfo.mode === 'disabled') cardHealth = { status: 'unconfigured' };
|
|
1610
|
+
else if (modelInfo.readiness.candidateCount > 0 && cardHealth.status === 'unconfigured') cardHealth = { status: 'configured' };
|
|
1611
|
+
}
|
|
1202
1612
|
var body = null;
|
|
1203
1613
|
if (open) {
|
|
1204
1614
|
var inputs = fields.map(function (f) {
|
|
1205
1615
|
var node;
|
|
1206
1616
|
if (f.key === 'model' && capability) {
|
|
1207
1617
|
node = React.createElement('select', { value: vals.model || '', onChange: function (e) { setVal('model', e.target.value); } },
|
|
1208
|
-
React.createElement('option', { value: '' }, '
|
|
1618
|
+
React.createElement('option', { value: '' }, modelInfo && modelInfo.mode === 'auto' ? '按功能池顺序自动选择' : '按所选模型顺序'),
|
|
1209
1619
|
((modelInfo && modelInfo.options) || []).map(function (option) {
|
|
1210
1620
|
return React.createElement('option', { key: option.ref, value: option.ref, disabled: option.unavailable }, option.label + (option.unavailable ? ' · 当前不可用' : ''));
|
|
1211
1621
|
}));
|
|
@@ -1249,14 +1659,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1249
1659
|
// 只读模型提示:当前能力解析到的模型与持久健康事实。
|
|
1250
1660
|
var modelBar = null;
|
|
1251
1661
|
if (capability) {
|
|
1252
|
-
var available = modelInfo && modelInfo.loaded && modelInfo.options.length > 0;
|
|
1662
|
+
var available = modelInfo && modelInfo.loaded && (modelInfo.readiness ? modelInfo.readiness.candidateCount > 0 : modelInfo.options.length > 0);
|
|
1253
1663
|
var shown = (vals.model && vals.model.trim()) || (modelInfo && modelInfo.assigned) || '';
|
|
1254
1664
|
modelBar = React.createElement('div', { className: 'iris-act-field' },
|
|
1255
1665
|
React.createElement('label', {},
|
|
1256
1666
|
React.createElement('span', { className: 'iris-pm-dot ' + healthClass(cardHealth) }, cardHealth.retryAt ? '◷' : HEALTH_META[cardHealth.status].symbol),
|
|
1257
1667
|
' 模型:' + (shown || '未分配') + (available ? '' : '(无可用模型)')),
|
|
1258
1668
|
React.createElement('div', { className: 'iris-wb-muted' },
|
|
1259
|
-
|
|
1669
|
+
(modelInfo && modelInfo.readiness ? modelInfo.readiness.candidateCount + ' 个候选 · ' + (modelInfo.readiness.note || '实际权限以调用结果为准') : healthText(cardHealth))
|
|
1670
|
+
+ ' · 调整方式 → 上方「功能池与调用方式」'));
|
|
1260
1671
|
}
|
|
1261
1672
|
body = React.createElement('div', { className: 'iris-act-body' },
|
|
1262
1673
|
modelBar,
|
|
@@ -1268,7 +1679,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1268
1679
|
var headStatus = capability ? cardHealth.status : 'verified';
|
|
1269
1680
|
var headTitle = capability ? healthText(cardHealth) : '本地功能';
|
|
1270
1681
|
return React.createElement('div', {
|
|
1271
|
-
className: 'iris-act-card' + (open ? ' open' : '') + (capability ? ' health-' + headStatus : '')
|
|
1682
|
+
className: 'iris-act-card' + (open ? ' open' : '') + (capability ? ' health-' + headStatus : ''), 'data-iris-action': action
|
|
1272
1683
|
},
|
|
1273
1684
|
React.createElement('div', { className: 'iris-act-head', onClick: function () { setOpen(!open); } },
|
|
1274
1685
|
React.createElement('span', { className: 'iris-act-title' }, title),
|
|
@@ -1360,59 +1771,168 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1360
1771
|
{ cap: 'vision', label: '👁 视觉' }
|
|
1361
1772
|
];
|
|
1362
1773
|
function CapabilityAssigner() {
|
|
1363
|
-
var dataPair = React.useState(null);
|
|
1364
|
-
var
|
|
1365
|
-
var
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1774
|
+
var dataPair = React.useState(null), data = dataPair[0], setData = dataPair[1];
|
|
1775
|
+
var notePair = React.useState('');
|
|
1776
|
+
var feedbackPair = React.useState({}), feedback = feedbackPair[0];
|
|
1777
|
+
var busyPair = React.useState(null), busy = busyPair[0];
|
|
1778
|
+
var loadingPair = React.useState(false), loading = loadingPair[0];
|
|
1779
|
+
var confirmPair = React.useState(null), confirmation = confirmPair[0];
|
|
1780
|
+
var saving = React.useRef(false), readVersion = React.useRef(0);
|
|
1781
|
+
var labels = { auto: '按功能自动选择', manual: '仅用所选模型', disabled: '停用' };
|
|
1782
|
+
function compatible(snapshot) {
|
|
1783
|
+
return !!(snapshot && snapshot.policies && snapshot.readiness && CAP_ROWS.every(function (row) {
|
|
1784
|
+
return snapshot.policies[row.cap] && ['auto', 'manual', 'disabled'].includes(snapshot.policies[row.cap].mode) && snapshot.readiness[row.cap];
|
|
1785
|
+
}));
|
|
1370
1786
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1787
|
+
function setFeedback(cap, value) {
|
|
1788
|
+
feedbackPair[1](function (previous) { return { ...previous, [cap]: value }; });
|
|
1789
|
+
}
|
|
1790
|
+
function request(action, body) {
|
|
1791
|
+
var controller = new AbortController();
|
|
1792
|
+
var timer = setTimeout(function () { controller.abort(); }, 10000);
|
|
1793
|
+
return postAction(action, body, { signal: controller.signal }).finally(function () { clearTimeout(timer); });
|
|
1794
|
+
}
|
|
1795
|
+
function load(event) {
|
|
1796
|
+
var version = ++readVersion.current;
|
|
1797
|
+
var shared = event && event.detail && event.detail.functionAssignments;
|
|
1798
|
+
if (shared) { setData(shared); loadingPair[1](false); return Promise.resolve(shared); }
|
|
1799
|
+
loadingPair[1](true);
|
|
1800
|
+
return request('assignments_get', {}).then(function (r) {
|
|
1801
|
+
if (version !== readVersion.current) return null;
|
|
1802
|
+
if (!r.ok || !r.d || r.d.ok !== true) throw new Error(r.d && r.d.error || '无法读取功能池');
|
|
1803
|
+
setData(r.d); notePair[1](''); return r.d;
|
|
1804
|
+
}).catch(function (error) {
|
|
1805
|
+
if (version === readVersion.current) notePair[1](error.name === 'AbortError' ? '读取状态超时,请重新检查' : '无法读取功能池,请检查与宿主的连接');
|
|
1806
|
+
return null;
|
|
1807
|
+
}).finally(function () { if (version === readVersion.current) loadingPair[1](false); });
|
|
1808
|
+
}
|
|
1809
|
+
React.useEffect(function () {
|
|
1810
|
+
load();
|
|
1811
|
+
window.addEventListener('iris-function-configuration-changed', load);
|
|
1812
|
+
return function () { window.removeEventListener('iris-function-configuration-changed', load); };
|
|
1813
|
+
}, []);
|
|
1814
|
+
function update(body) {
|
|
1815
|
+
if (saving.current || !compatible(data)) return;
|
|
1816
|
+
saving.current = true; busyPair[1](body.capability); confirmPair[1](null);
|
|
1817
|
+
++readVersion.current; // A read started before this write cannot overwrite its result.
|
|
1818
|
+
loadingPair[1](false);
|
|
1819
|
+
var desired = body.mode || (body.model_refs.length ? 'manual' : 'disabled');
|
|
1820
|
+
setFeedback(body.capability, { phase: 'saving', message: '正在保存…' });
|
|
1821
|
+
request('assignments_set', body).then(function (r) {
|
|
1822
|
+
if (!r.ok || !r.d || r.d.ok !== true) throw new Error(r.d && r.d.error || '未收到保存确认,请刷新状态核对');
|
|
1823
|
+
if ((body.mode && r.d.mode !== body.mode) || !Array.isArray(r.d.model_refs)) {
|
|
1824
|
+
setData(function (previous) { return { ...previous, policies: null, readiness: null }; });
|
|
1825
|
+
throw new Error('服务端未确认新策略,请重启 DSH 后刷新页面');
|
|
1826
|
+
}
|
|
1827
|
+
setData(function (previous) { return { ...previous,
|
|
1828
|
+
policies: { ...previous.policies, [body.capability]: { ...previous.policies[body.capability], mode: desired,
|
|
1829
|
+
...(body.hostVision !== undefined ? { hostVision: body.hostVision } : {}) } },
|
|
1830
|
+
order: { ...previous.order, [body.capability]: r.d.model_refs }
|
|
1831
|
+
}; });
|
|
1832
|
+
setFeedback(body.capability, { phase: 'refreshing', message: '已保存为“' + labels[desired] + '”,正在更新候选…' });
|
|
1833
|
+
return load().then(function (snapshot) {
|
|
1834
|
+
if (!snapshot) {
|
|
1835
|
+
setFeedback(body.capability, { phase: 'saved-stale', message: '已保存为“' + labels[desired] + '”,候选未能刷新,请核对状态' });
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
var policy = snapshot.policies && snapshot.policies[body.capability];
|
|
1839
|
+
var confirmed = compatible(snapshot) && policy.mode === desired
|
|
1840
|
+
&& (body.hostVision === undefined || policy.hostVision === body.hostVision);
|
|
1841
|
+
setFeedback(body.capability, confirmed
|
|
1842
|
+
? { phase: 'saved', message: '已保存:' + labels[desired] + ' · ' + snapshot.readiness[body.capability].candidateCount + ' 个可用候选' }
|
|
1843
|
+
: { phase: 'unknown', message: '读取的状态与本次选择不一致,请核对服务端状态' });
|
|
1844
|
+
window.dispatchEvent(new CustomEvent('iris-function-configuration-changed', { detail: { functionAssignments: snapshot } }));
|
|
1845
|
+
});
|
|
1846
|
+
}).catch(function (error) {
|
|
1847
|
+
setFeedback(body.capability, { phase: 'unknown', message: error.name === 'AbortError'
|
|
1848
|
+
? '未收到保存确认,请刷新状态核对,暂勿重复提交' : error.message || '保存结果未确认,请刷新状态核对' });
|
|
1849
|
+
}).finally(function () { saving.current = false; busyPair[1](null); });
|
|
1376
1850
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
var opts = poolBy[row.cap] || [];
|
|
1383
|
-
var available = opts.filter(function (o) { return list.indexOf(o.ref) < 0; });
|
|
1384
|
-
var chips = list.map(function (ref, i) {
|
|
1385
|
-
var opt = opts.find(function (o) { return o.ref === ref; });
|
|
1386
|
-
return React.createElement('span', { key: ref, className: 'iris-cap-chip' },
|
|
1387
|
-
React.createElement('b', {}, (i + 1) + '.'),
|
|
1388
|
-
opt ? opt.label + (opt.unavailable ? ' · 当前不可用' : '') : ref + ' · 配置已失效',
|
|
1389
|
-
i > 0 ? React.createElement('button', {
|
|
1390
|
-
className: 'iris-pm-btn', title: '上移',
|
|
1391
|
-
onClick: function () { var n = list.slice(); var t = n.splice(i, 1)[0]; n.splice(i - 1, 0, t); save(row.cap, n); }
|
|
1392
|
-
}, '↑') : null,
|
|
1393
|
-
i < list.length - 1 ? React.createElement('button', {
|
|
1394
|
-
className: 'iris-pm-btn', title: '下移',
|
|
1395
|
-
onClick: function () { var n = list.slice(); var t = n.splice(i, 1)[0]; n.splice(i + 1, 0, t); save(row.cap, n); }
|
|
1396
|
-
}, '↓') : null,
|
|
1397
|
-
React.createElement('button', {
|
|
1398
|
-
className: 'iris-pm-btn danger', title: '移出列表',
|
|
1399
|
-
onClick: function () { save(row.cap, list.filter(function (x) { return x !== ref; })); }
|
|
1400
|
-
}, '✕'));
|
|
1851
|
+
function check(cap) {
|
|
1852
|
+
if (saving.current) return;
|
|
1853
|
+
return load().then(function (snapshot) {
|
|
1854
|
+
if (compatible(snapshot)) setFeedback(cap, { phase: 'saved', message: '当前已保存:' + labels[snapshot.policies[cap].mode]
|
|
1855
|
+
+ ' · ' + snapshot.readiness[cap].candidateCount + ' 个可用候选' });
|
|
1401
1856
|
});
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
onClick: function () {
|
|
1414
|
-
|
|
1415
|
-
|
|
1857
|
+
}
|
|
1858
|
+
function save(cap, refs) { update({ capability: cap, model_refs: refs }); }
|
|
1859
|
+
if (!data) return React.createElement('div', { className: 'iris-wb-muted', role: 'status' },
|
|
1860
|
+
notePair[0] || '加载功能池…', notePair[0] ? React.createElement('button', { className: 'iris-pm-btn', disabled: loading,
|
|
1861
|
+
onClick: function () { load(); } }, loading ? '正在检查…' : '重新检查') : null);
|
|
1862
|
+
var supported = compatible(data);
|
|
1863
|
+
var order = data.order || {}, poolBy = data.poolByCapability || {};
|
|
1864
|
+
return React.createElement('div', { className: 'iris-cap-grid' },
|
|
1865
|
+
!supported ? React.createElement('div', { className: 'iris-cap-compatibility', role: 'alert' },
|
|
1866
|
+
'当前服务端尚未加载功能池策略,已暂停切换。请重启 DSH 后刷新页面;仅刷新页面无法更新服务端。',
|
|
1867
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: loading,
|
|
1868
|
+
onClick: function () { load(); } }, loading ? '正在检查…' : '重新检查服务端')) : null,
|
|
1869
|
+
CAP_ROWS.map(function (row) {
|
|
1870
|
+
var policy = data.policies && data.policies[row.cap] || { mode: (order[row.cap] || []).length ? 'manual' : 'disabled', models: order[row.cap] || [] };
|
|
1871
|
+
var mode = policy.mode;
|
|
1872
|
+
var list = order[row.cap] || [];
|
|
1873
|
+
var opts = poolBy[row.cap] || [];
|
|
1874
|
+
var status = data.readiness && data.readiness[row.cap];
|
|
1875
|
+
var available = opts.filter(function (o) { return list.indexOf(o.ref) < 0; });
|
|
1876
|
+
var message = feedback[row.cap];
|
|
1877
|
+
var blocked = !supported || !!busy || (message && message.phase === 'unknown');
|
|
1878
|
+
function setMode(value, hostVision) {
|
|
1879
|
+
if (blocked || saving.current) return;
|
|
1880
|
+
var body = { capability: row.cap, mode: value,
|
|
1881
|
+
...(row.cap === 'vision' ? { hostVision: hostVision === undefined ? policy.hostVision === true : hostVision } : {}) };
|
|
1882
|
+
if (value === 'auto' && mode !== 'auto') {
|
|
1883
|
+
var count = opts.filter(function (o) { return !o.unavailable && !o.pendingConfirmation
|
|
1884
|
+
&& (o.ref !== 'host:current-vision' || policy.hostVision === true); }).length;
|
|
1885
|
+
setFeedback(row.cap, null);
|
|
1886
|
+
confirmPair[1]({ cap: row.cap, body: body, count: count }); return;
|
|
1887
|
+
}
|
|
1888
|
+
update(body);
|
|
1889
|
+
}
|
|
1890
|
+
var chips = list.map(function (ref, i) {
|
|
1891
|
+
var opt = opts.find(function (o) { return o.ref === ref; });
|
|
1892
|
+
return React.createElement('span', { key: ref, className: 'iris-cap-chip' },
|
|
1893
|
+
React.createElement('b', {}, (i + 1) + '.'),
|
|
1894
|
+
opt ? opt.label + (opt.unavailable ? ' · ' + (opt.reason || '当前不可用') : '') : ref + ' · 配置已失效',
|
|
1895
|
+
mode === 'manual' && i > 0 ? React.createElement('button', { className: 'iris-pm-btn', title: '上移', disabled: blocked,
|
|
1896
|
+
onClick: function () { var n = list.slice(); var t = n.splice(i, 1)[0]; n.splice(i - 1, 0, t); save(row.cap, n); }
|
|
1897
|
+
}, '↑') : null,
|
|
1898
|
+
mode === 'manual' && i < list.length - 1 ? React.createElement('button', { className: 'iris-pm-btn', title: '下移', disabled: blocked,
|
|
1899
|
+
onClick: function () { var n = list.slice(); var t = n.splice(i, 1)[0]; n.splice(i + 1, 0, t); save(row.cap, n); }
|
|
1900
|
+
}, '↓') : null,
|
|
1901
|
+
mode === 'manual' ? React.createElement('button', { className: 'iris-pm-btn danger', title: '移出列表', disabled: blocked,
|
|
1902
|
+
onClick: function () { save(row.cap, list.filter(function (x) { return x !== ref; })); }
|
|
1903
|
+
}, '✕') : null);
|
|
1904
|
+
});
|
|
1905
|
+
return React.createElement('div', { key: row.cap, className: 'iris-cap-row', 'data-function': row.cap, 'aria-busy': busy === row.cap },
|
|
1906
|
+
React.createElement('span', { className: 'iris-cap-name' }, row.label),
|
|
1907
|
+
React.createElement('select', { 'aria-label': row.cap + ' 调用方式', value: confirmation && confirmation.cap === row.cap ? 'auto' : mode, disabled: blocked,
|
|
1908
|
+
onChange: function (e) { setMode(e.target.value); } },
|
|
1909
|
+
React.createElement('option', { value: 'auto' }, '按功能自动选择'),
|
|
1910
|
+
React.createElement('option', { value: 'manual' }, '仅用所选模型'),
|
|
1911
|
+
React.createElement('option', { value: 'disabled' }, '停用')),
|
|
1912
|
+
status && !(confirmation && confirmation.cap === row.cap) && !(message && ['saving', 'refreshing', 'saved-stale', 'unknown'].includes(message.phase)) ? React.createElement('span', { className: 'iris-pm-note', title: '候选来自配置和连接元数据;实际接口权限以调用结果为准' },
|
|
1913
|
+
status.candidateCount + ' 个可用候选' + (status.note ? ' · ' + status.note : '')) : null,
|
|
1914
|
+
confirmation && confirmation.cap === row.cap ? React.createElement('div', { className: 'iris-cap-confirm', role: 'group', 'aria-label': '确认自动选择' },
|
|
1915
|
+
React.createElement('div', {}, '尚未保存:将启用按功能自动选择,当前有 ' + confirmation.count + ' 个已确认候选。保留原手工名单;未确认的新发现模型不会自动调用。'),
|
|
1916
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: !!busy, onClick: function () { update(confirmation.body); } }, '确认启用自动池'),
|
|
1917
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: !!busy, onClick: function () { confirmPair[1](null); } }, '取消')) : null,
|
|
1918
|
+
message ? React.createElement('div', { className: 'iris-cap-feedback ' + message.phase, role: message.phase === 'unknown' ? 'alert' : 'status', 'aria-live': 'polite' },
|
|
1919
|
+
message.message, ['unknown', 'saved-stale'].includes(message.phase) ? React.createElement('button', { className: 'iris-pm-btn', disabled: !!busy || loading,
|
|
1920
|
+
onClick: function () { check(row.cap); } }, loading ? '正在核对…' : '刷新状态') : null) : null,
|
|
1921
|
+
mode === 'auto' ? React.createElement('span', { className: 'iris-pm-note' }, '按池顺序选择;确认模型用途后自动加入,无需再次分配。') : null,
|
|
1922
|
+
mode === 'auto' && row.cap === 'vision' ? React.createElement('label', { className: 'iris-pm-note' },
|
|
1923
|
+
React.createElement('input', { type: 'checkbox', checked: policy.hostVision === true, disabled: blocked,
|
|
1924
|
+
onChange: function (e) { setMode('auto', e.target.checked); } }),
|
|
1925
|
+
'使用DSH当前视觉模型(执行前核对图片能力)') : null,
|
|
1926
|
+
chips,
|
|
1927
|
+
mode === 'manual' && available.length ? React.createElement('select', { className: 'iris-pm-model-sel', value: '', disabled: blocked,
|
|
1928
|
+
onChange: function (e) { if (e.target.value) save(row.cap, list.concat([e.target.value])); } },
|
|
1929
|
+
React.createElement('option', { value: '' }, '+ 分配模型'),
|
|
1930
|
+
available.map(function (o) { return React.createElement('option', { key: o.ref, value: o.ref }, o.label); })) : null,
|
|
1931
|
+
mode === 'manual' && list.length ? React.createElement('button', { className: 'iris-pm-btn', title: '清空分配后,该功能不再发起新调用', disabled: blocked,
|
|
1932
|
+
onClick: function () { save(row.cap, []); }
|
|
1933
|
+
}, '清空分配') : null);
|
|
1934
|
+
}),
|
|
1935
|
+
notePair[0] ? React.createElement('div', { className: 'iris-pm-note' }, notePair[0]) : null);
|
|
1416
1936
|
}
|
|
1417
1937
|
|
|
1418
1938
|
/* ---- 泡泡常用卡片选择(localStorage + 跨座位响应) ---- */
|
|
@@ -1829,7 +2349,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1829
2349
|
React.createElement('label', {}, '改图模型', React.createElement('select', { 'aria-label': '改图模型', value: model, disabled: !!running,
|
|
1830
2350
|
onChange: function (e) { setModel(e.target.value); } }, React.createElement('option', { value: '' }, '按当前顺序选择可用改图模型'),
|
|
1831
2351
|
(models || []).map(function (entry) { return React.createElement('option', { key: entry.id, value: entry.id, disabled: entry.blocked }, entry.label + (entry.blocked ? ' · 冷却或额度耗尽' : '')); }))),
|
|
1832
|
-
models && !models.some(function (entry) { return !entry.blocked; }) ? React.createElement('div', { role: 'status' }, '
|
|
2352
|
+
models && !models.some(function (entry) { return !entry.blocked; }) ? React.createElement('div', { role: 'status' }, '暂无可用改图模型,请检查图片功能的调用方式,确认聊天生图或 DashScope Qwen 改图模型用途及连接。') : null,
|
|
1833
2353
|
React.createElement('div', { className: 'iris-gallery-actions' },
|
|
1834
2354
|
React.createElement('button', { className: 'iris-clean-btn', type: 'button', disabled: !!running || !prompt.trim() || !models || !models.some(function (entry) { return !entry.blocked; }), onClick: execute }, running ? '编辑中…' : '生成修改版'),
|
|
1835
2355
|
running ? React.createElement('button', { className: 'iris-pm-btn', type: 'button', onClick: cancel }, '停止等待') : null),
|
|
@@ -2664,6 +3184,24 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2664
3184
|
React.createElement('div', { className: 'iris-wb-muted' }, '只读本机注册证据;不调用 Browser、模型或供应商。'));
|
|
2665
3185
|
}
|
|
2666
3186
|
|
|
3187
|
+
function revealWorkbenchItem(node) {
|
|
3188
|
+
for (var parent = node.parentElement; parent; parent = parent.parentElement) {
|
|
3189
|
+
if (parent.tagName === 'DETAILS') parent.open = true;
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
function WorkbenchSection(props) {
|
|
3193
|
+
var expandedPair = React.useState(false);
|
|
3194
|
+
return React.createElement(props.collapsible ? 'details' : 'section', { className: 'iris-wb-section',
|
|
3195
|
+
'data-iris-section': props.name, 'aria-label': props.title,
|
|
3196
|
+
onToggle: props.collapsible ? function (event) { expandedPair[1](event.currentTarget.open); } : undefined },
|
|
3197
|
+
React.createElement(props.collapsible ? 'summary' : 'div', { className: 'iris-wb-section-head' },
|
|
3198
|
+
React.createElement('div', {}, React.createElement('h2', {}, props.title),
|
|
3199
|
+
props.description ? React.createElement('p', {}, props.description) : null),
|
|
3200
|
+
props.collapsible ? React.createElement('span', { className: 'iris-wb-section-arrow', 'aria-hidden': true }, expandedPair[0] ? '收起' : '展开',
|
|
3201
|
+
React.createElement('svg', { width: 15, height: 15, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8 },
|
|
3202
|
+
React.createElement('path', { d: 'm6 9 6 6 6-6' }))) : null),
|
|
3203
|
+
React.createElement('div', { className: 'iris-wb-section-body' }, props.children));
|
|
3204
|
+
}
|
|
2667
3205
|
function WorkbenchPanel() {
|
|
2668
3206
|
var state = useIrisState(5000);
|
|
2669
3207
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
@@ -2677,46 +3215,45 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2677
3215
|
var selectedTask = selPair[0];
|
|
2678
3216
|
var setSelectedTask = selPair[1];
|
|
2679
3217
|
function onSelect(id) { setSelectedTask(id); }
|
|
2680
|
-
return React.createElement('div', { className: 'iris-wb' },
|
|
3218
|
+
return React.createElement('div', { className: 'iris-wb iris-workbench-page' },
|
|
2681
3219
|
React.createElement('div', { className: 'iris-wb-head' },
|
|
2682
3220
|
React.createElement('span', { className: 'iris-wb-title' }, 'Iris 工作台'),
|
|
2683
3221
|
React.createElement('span', { className: 'iris-wb-date' }, state ? '刷新于 ' + new Date().toLocaleTimeString() : '加载中…')),
|
|
2684
|
-
React.createElement('div', { className: 'iris-wb-sec' }, '供应商'),
|
|
2685
|
-
React.createElement(ProviderManager, {}),
|
|
2686
|
-
React.createElement('div', { className: 'iris-wb-sec' }, '能力健康'),
|
|
2687
3222
|
React.createElement(CapabilityHealthOverview, {}),
|
|
2688
|
-
React.createElement(
|
|
2689
|
-
|
|
2690
|
-
React.createElement(
|
|
2691
|
-
|
|
2692
|
-
React.createElement(
|
|
2693
|
-
|
|
2694
|
-
React.createElement(PromptOptimizerSettings, {}),
|
|
2695
|
-
React.createElement('div', { className: 'iris-wb-sec' }, '宿主诊断'),
|
|
2696
|
-
React.createElement(HostDoctorPanel, {}),
|
|
2697
|
-
React.createElement('div', { className: 'iris-wb-sec' }, '运行中任务'),
|
|
2698
|
-
React.createElement('div', { className: 'iris-wb-box' },
|
|
3223
|
+
React.createElement(WorkbenchSection, { name: 'providers', title: '供应商与模型', description: '管理连接、模型用途与接口协议。' },
|
|
3224
|
+
React.createElement(ProviderManager, {})),
|
|
3225
|
+
React.createElement(WorkbenchSection, { name: 'pools', title: '功能池与调用方式', description: '选择自动池、所选模型或停用,查看每项功能的候选范围。', collapsible: true },
|
|
3226
|
+
React.createElement(CapabilityAssigner, {})),
|
|
3227
|
+
React.createElement(WorkbenchSection, { name: 'running', title: '运行中任务' },
|
|
3228
|
+
React.createElement('div', { className: 'iris-wb-box' },
|
|
2699
3229
|
running.length || coreGroups.live.length ? [].concat(
|
|
2700
3230
|
running.map(function (t) { return taskRow(t, true, selectedTask === t.id, onSelect); }),
|
|
2701
3231
|
coreGroups.live.map(coreTaskCard))
|
|
2702
|
-
: React.createElement('div', { className: 'iris-wb-empty' }, '暂无进行中任务')),
|
|
2703
|
-
attention.length || coreGroups.attention.length || coreUser.degraded ? React.createElement('
|
|
2704
|
-
React.createElement('div', { className: 'iris-wb-sec' }, '需要处理'),
|
|
3232
|
+
: React.createElement('div', { className: 'iris-wb-empty' }, '暂无进行中任务'))),
|
|
3233
|
+
attention.length || coreGroups.attention.length || coreUser.degraded ? React.createElement(WorkbenchSection, { name: 'attention', title: '需要处理' },
|
|
2705
3234
|
React.createElement('div', { className: 'iris-wb-box' },
|
|
2706
3235
|
coreUser.degraded ? React.createElement('div', { className: 'iris-clean-note' },
|
|
2707
3236
|
'部分 Core 记录损坏或媒体缺失,已跳过 ' + coreUser.droppedTasks + ' 条;其余事实照常显示。') : null,
|
|
2708
3237
|
[].concat(
|
|
2709
3238
|
attention.map(function (t) { return taskRow(t, false, selectedTask === t.id, onSelect); }),
|
|
2710
3239
|
coreGroups.attention.map(coreTaskCard)))) : null,
|
|
2711
|
-
React.createElement(
|
|
2712
|
-
|
|
2713
|
-
React.createElement(
|
|
2714
|
-
|
|
2715
|
-
React.createElement(
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
3240
|
+
React.createElement(WorkbenchSection, { name: 'artifacts', title: '作品库', description: '查看、下载和继续处理已有作品。' },
|
|
3241
|
+
React.createElement(ArtifactGallery, { total: artifactTotal })),
|
|
3242
|
+
React.createElement(WorkbenchSection, { name: 'actions', title: '创作工具', description: '生成媒体、导入图片、识别与处理素材。', collapsible: true },
|
|
3243
|
+
React.createElement(ActionGroups, {})),
|
|
3244
|
+
React.createElement(WorkbenchSection, { name: 'preferences', title: '快捷入口与偏好', description: '设置泡泡快捷卡片与对话框提示词优化。', collapsible: true },
|
|
3245
|
+
React.createElement('div', { className: 'iris-wb-sec' }, 'Iris 泡泡快捷卡片(勾选后显示在「⚡ 常用」标签)'),
|
|
3246
|
+
React.createElement(BubbleCardPicker, {}),
|
|
3247
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '对话框提示词优化'),
|
|
3248
|
+
React.createElement(PromptOptimizerSettings, {})),
|
|
3249
|
+
React.createElement(WorkbenchSection, { name: 'diagnostics', title: '诊断与运行信息', description: '查看宿主装载与 Core 任务事实。', collapsible: true },
|
|
3250
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '宿主诊断'),
|
|
3251
|
+
React.createElement(HostDoctorPanel, {}),
|
|
3252
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '高级诊断'),
|
|
3253
|
+
React.createElement(CoreRuntimePanel, {})),
|
|
3254
|
+
React.createElement(WorkbenchSection, { name: 'history', title: '历史任务', description: '查看历史记录与清理选项。', collapsible: true },
|
|
3255
|
+
React.createElement(CleanupBar, {}),
|
|
3256
|
+
React.createElement(HistoryBrowser, { recent: recent, coreHistory: [].concat(coreGroups.done, coreGroups.acknowledged), selected: selectedTask, onSelect: onSelect })));
|
|
2720
3257
|
}
|
|
2721
3258
|
|
|
2722
3259
|
/* ---- 泡泡浮层(阶段 4 续:两标签,尽量干净)---- */
|