@xmanrui/dsh-im 4.10.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROACTIVE_DELIVERY.en.md +15 -1
- package/PROACTIVE_DELIVERY.md +15 -1
- package/README.en.md +5 -1
- package/README.md +6 -2
- package/lib/client.js +1619 -769
- package/lib/index.js +248 -244
- package/package.json +4 -3
- package/plugin-src/client/channel-card-meta.js +9 -1
- package/plugin-src/client/channels/dingtalk/api.js +4 -0
- package/plugin-src/client/channels/dingtalk/index.js +25 -2
- package/plugin-src/client/channels/feishu/api.js +4 -0
- package/plugin-src/client/channels/feishu/group-settings.js +581 -0
- package/plugin-src/client/channels/feishu/index.js +26 -204
- package/plugin-src/client/channels/qq/api.js +4 -0
- package/plugin-src/client/channels/qq/index.js +25 -2
- package/plugin-src/client/channels/shared/token-api.js +4 -0
- package/plugin-src/client/channels/shared/token-channel.js +25 -3
- package/plugin-src/client/channels/wecom/api.js +4 -0
- package/plugin-src/client/channels/wecom/index.js +25 -2
- package/plugin-src/client/channels/weixin/api.js +4 -0
- package/plugin-src/client/channels/weixin/index.js +25 -2
- package/plugin-src/client/channels/whatsapp/api.js +4 -0
- package/plugin-src/client/channels/whatsapp/index.js +24 -2
- package/plugin-src/client/delivery-settings.js +73 -5
- package/plugin-src/client/i18n.js +20 -0
- package/plugin-src/client/model-setting.js +104 -0
- package/plugin-src/client/styles.js +50 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +3 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
- package/plugin-src/host/channels/feishu/production.mjs +3 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
- package/plugin-src/host/channels/qq/production.mjs +3 -0
- package/plugin-src/host/channels/qq/rpc.mjs +11 -0
- package/plugin-src/host/channels/shared/model-setting-rpc.mjs +19 -0
- package/plugin-src/host/channels/shared/production.mjs +3 -0
- package/plugin-src/host/channels/shared/rpc.mjs +9 -0
- package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
- package/plugin-src/host/channels/slack/production.mjs +3 -0
- package/plugin-src/host/channels/slack/rpc.mjs +9 -0
- package/plugin-src/host/channels/wecom/production.mjs +3 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
- package/plugin-src/host/channels/weixin/production.mjs +3 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +3 -0
- package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
- package/plugin-src/host/delivery-adapter.mjs +141 -3
- package/plugin-src/host/delivery-rpc.mjs +12 -0
- package/plugin-src/host/delivery-service.mjs +110 -3
- package/plugin-src/host/delivery-suggestions.mjs +46 -0
- package/plugin-src/host/harness-session-coordinator.mjs +6 -5
- package/plugin-src/host/index.mjs +18 -1
- package/plugin-src/host/session-sync-coordinator.mjs +217 -0
- package/scripts/verify-package.mjs +12 -6
- package/src/channels/shared/bot-workspace-store.mjs +285 -38
- package/src/channels/shared/harness-client.mjs +124 -43
- package/src/channels/shared/model-setting.mjs +110 -0
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
AgentPresetEditor,
|
|
11
11
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
12
12
|
} from '../../agent-preset.js';
|
|
13
|
+
import {
|
|
14
|
+
EMPTY_MODEL_CATALOG,
|
|
15
|
+
ModelCatalogContext,
|
|
16
|
+
ModelEditor,
|
|
17
|
+
} from '../../model-setting.js';
|
|
13
18
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
14
19
|
import {
|
|
15
20
|
BotSettingsButton,
|
|
@@ -166,6 +171,7 @@ export function AccountCard({
|
|
|
166
171
|
removing,
|
|
167
172
|
onReconnect,
|
|
168
173
|
onWorkspaceSave,
|
|
174
|
+
onModelSave,
|
|
169
175
|
onAgentPresetSave,
|
|
170
176
|
onContextEnhancementSave,
|
|
171
177
|
onRequestRemove,
|
|
@@ -203,6 +209,11 @@ export function AccountCard({
|
|
|
203
209
|
disabled: Boolean(busy),
|
|
204
210
|
onSave: onWorkspaceSave,
|
|
205
211
|
}),
|
|
212
|
+
h(ModelEditor, {
|
|
213
|
+
model: account.model,
|
|
214
|
+
disabled: Boolean(busy),
|
|
215
|
+
onSave: onModelSave,
|
|
216
|
+
}),
|
|
206
217
|
h(AgentPresetEditor, {
|
|
207
218
|
agentPreset: account.agentPreset,
|
|
208
219
|
disabled: Boolean(busy),
|
|
@@ -237,6 +248,7 @@ export function WecomSettingsTab({ rpcCall }) {
|
|
|
237
248
|
const [model, setModel] = React.useState({
|
|
238
249
|
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
|
239
250
|
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
|
251
|
+
modelCatalog: EMPTY_MODEL_CATALOG,
|
|
240
252
|
});
|
|
241
253
|
const [provision, setProvision] = React.useState(null);
|
|
242
254
|
const [busy, setBusy] = React.useState(false);
|
|
@@ -298,6 +310,7 @@ export function WecomSettingsTab({ rpcCall }) {
|
|
|
298
310
|
setModel({
|
|
299
311
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
300
312
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
313
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
301
314
|
});
|
|
302
315
|
if (restore && snapshot.provisioning) setProvision({
|
|
303
316
|
...snapshot.provisioning,
|
|
@@ -365,6 +378,7 @@ export function WecomSettingsTab({ rpcCall }) {
|
|
|
365
378
|
setModel({
|
|
366
379
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
367
380
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
381
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
368
382
|
});
|
|
369
383
|
}
|
|
370
384
|
setCredentialOpen(false);
|
|
@@ -428,6 +442,7 @@ export function WecomSettingsTab({ rpcCall }) {
|
|
|
428
442
|
setModel({
|
|
429
443
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
430
444
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
445
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
431
446
|
});
|
|
432
447
|
}
|
|
433
448
|
return snapshot;
|
|
@@ -509,6 +524,12 @@ export function WecomSettingsTab({ rpcCall }) {
|
|
|
509
524
|
WECOM_ENDPOINTS.setWorkspace,
|
|
510
525
|
{ botId: account.botId, workspace },
|
|
511
526
|
),
|
|
527
|
+
onModelSave: (selectedModel) => botAction(
|
|
528
|
+
account,
|
|
529
|
+
'model',
|
|
530
|
+
WECOM_ENDPOINTS.setModel,
|
|
531
|
+
{ botId: account.botId, model: selectedModel },
|
|
532
|
+
),
|
|
512
533
|
onAgentPresetSave: (agentPreset) => botAction(
|
|
513
534
|
account,
|
|
514
535
|
'preset',
|
|
@@ -544,7 +565,9 @@ export function WecomSettingsTab({ rpcCall }) {
|
|
|
544
565
|
})
|
|
545
566
|
: null;
|
|
546
567
|
|
|
547
|
-
return h(
|
|
568
|
+
return h(ModelCatalogContext.Provider, {
|
|
569
|
+
value: model.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
570
|
+
}, h(AgentPresetCatalogContext.Provider, {
|
|
548
571
|
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
549
572
|
}, h('section', { className: 'ddt-page dwecom-page dim-channelPage', 'aria-label': '企业微信设置' },
|
|
550
573
|
h(Heading, {
|
|
@@ -565,5 +588,5 @@ export function WecomSettingsTab({ rpcCall }) {
|
|
|
565
588
|
provisionView,
|
|
566
589
|
model.bots.length === 0 && !provision && !credentialOpen
|
|
567
590
|
? h(EmptyView, { busy, onStart: () => void startProvisioning() }) : null,
|
|
568
|
-
botList)));
|
|
591
|
+
botList))));
|
|
569
592
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeModelCatalog, normalizeModelSelection, SET_MODEL_ENDPOINT } from '../../model-setting.js';
|
|
2
3
|
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
3
4
|
import { normalizeAccessPolicy } from '../../../../src/channels/shared/access-policy.mjs';
|
|
4
5
|
import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
|
|
@@ -13,6 +14,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|
|
13
14
|
reconnectBot: 'bot.reconnect',
|
|
14
15
|
deleteBot: 'bot.delete',
|
|
15
16
|
setWorkspace: 'bot.workspace.set',
|
|
17
|
+
setModel: SET_MODEL_ENDPOINT,
|
|
16
18
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
17
19
|
setContextEnhancement: 'bot.context-enhancement.set',
|
|
18
20
|
setAccessPolicy: 'bot.access-policy.set',
|
|
@@ -124,6 +126,7 @@ function normalizeBot(value) {
|
|
|
124
126
|
connected,
|
|
125
127
|
configured: value.configured === true,
|
|
126
128
|
workspace: string(value.workspace).slice(0, 4_096),
|
|
129
|
+
model: normalizeModelSelection(value.model),
|
|
127
130
|
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
|
128
131
|
contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
|
|
129
132
|
...(Object.hasOwn(value, 'accessPolicy')
|
|
@@ -169,6 +172,7 @@ export function normalizeSnapshot(value) {
|
|
|
169
172
|
provisioning: value.provisioning ? normalizeProvisioning(value.provisioning) : null,
|
|
170
173
|
testMessage: normalizeTestMessage(value.testMessage),
|
|
171
174
|
agentPresetCatalog: normalizeAgentPresetCatalog(value.agentPresetCatalog),
|
|
175
|
+
modelCatalog: normalizeModelCatalog(value.modelCatalog),
|
|
172
176
|
};
|
|
173
177
|
}
|
|
174
178
|
|
|
@@ -21,6 +21,11 @@ import {
|
|
|
21
21
|
AgentPresetEditor,
|
|
22
22
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
23
23
|
} from '../../agent-preset.js';
|
|
24
|
+
import {
|
|
25
|
+
EMPTY_MODEL_CATALOG,
|
|
26
|
+
ModelCatalogContext,
|
|
27
|
+
ModelEditor,
|
|
28
|
+
} from '../../model-setting.js';
|
|
24
29
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
25
30
|
import {
|
|
26
31
|
BotSettingsButton,
|
|
@@ -208,6 +213,7 @@ export function AccountCard({
|
|
|
208
213
|
removing,
|
|
209
214
|
onReconnect,
|
|
210
215
|
onWorkspaceSave,
|
|
216
|
+
onModelSave,
|
|
211
217
|
onAgentPresetSave,
|
|
212
218
|
onContextEnhancementSave,
|
|
213
219
|
onRequestRemove,
|
|
@@ -244,6 +250,11 @@ export function AccountCard({
|
|
|
244
250
|
disabled: Boolean(busy),
|
|
245
251
|
onSave: onWorkspaceSave,
|
|
246
252
|
}),
|
|
253
|
+
h(ModelEditor, {
|
|
254
|
+
model: account.model,
|
|
255
|
+
disabled: Boolean(busy),
|
|
256
|
+
onSave: onModelSave,
|
|
257
|
+
}),
|
|
247
258
|
h(AgentPresetEditor, {
|
|
248
259
|
agentPreset: account.agentPreset,
|
|
249
260
|
disabled: Boolean(busy),
|
|
@@ -296,6 +307,7 @@ function AccountList(props) {
|
|
|
296
307
|
removing: props.removeTarget === account.botId,
|
|
297
308
|
onReconnect: () => props.onReconnect(account),
|
|
298
309
|
onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
|
|
310
|
+
onModelSave: (model) => props.onModelSave(account, model),
|
|
299
311
|
onAgentPresetSave: (agentPreset) => props.onAgentPresetSave(account, agentPreset),
|
|
300
312
|
onContextEnhancementSave: (config) => props.onContextEnhancementSave(account, config),
|
|
301
313
|
onRequestRemove: () => props.onRequestRemove(account),
|
|
@@ -324,6 +336,7 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
324
336
|
const [model, setModel] = React.useState({
|
|
325
337
|
phase: 'loading', bots: [], totals: EMPTY_TOTALS, revision: 0, error: null,
|
|
326
338
|
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
|
339
|
+
modelCatalog: EMPTY_MODEL_CATALOG,
|
|
327
340
|
});
|
|
328
341
|
const [provision, setProvision] = React.useState(null);
|
|
329
342
|
const [busy, setBusy] = React.useState(false);
|
|
@@ -367,6 +380,7 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
367
380
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals,
|
|
368
381
|
revision: snapshot.revision, error: null,
|
|
369
382
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
383
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
370
384
|
});
|
|
371
385
|
if (snapshot.provisioning) {
|
|
372
386
|
setProvision((current) => mergeWeixinProvisioningSnapshot(
|
|
@@ -558,6 +572,7 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
558
572
|
setModel((current) => ({
|
|
559
573
|
...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision,
|
|
560
574
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
|
|
575
|
+
modelCatalog: snapshot.modelCatalog ?? current.modelCatalog,
|
|
561
576
|
}));
|
|
562
577
|
}
|
|
563
578
|
const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
|
|
@@ -603,6 +618,7 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
603
618
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals,
|
|
604
619
|
revision: snapshot.revision, error: null,
|
|
605
620
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
621
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
606
622
|
});
|
|
607
623
|
}
|
|
608
624
|
} finally {
|
|
@@ -625,6 +641,7 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
625
641
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals,
|
|
626
642
|
revision: snapshot.revision, error: null,
|
|
627
643
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
644
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
628
645
|
});
|
|
629
646
|
}
|
|
630
647
|
} finally {
|
|
@@ -646,6 +663,7 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
646
663
|
setModel((current) => ({
|
|
647
664
|
...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision,
|
|
648
665
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? current.agentPresetCatalog,
|
|
666
|
+
modelCatalog: snapshot.modelCatalog ?? current.modelCatalog,
|
|
649
667
|
}));
|
|
650
668
|
}
|
|
651
669
|
setRemoveTarget(null);
|
|
@@ -686,7 +704,9 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
686
704
|
});
|
|
687
705
|
}
|
|
688
706
|
|
|
689
|
-
return h(
|
|
707
|
+
return h(ModelCatalogContext.Provider, {
|
|
708
|
+
value: model.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
709
|
+
}, h(AgentPresetCatalogContext.Provider, {
|
|
690
710
|
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
691
711
|
}, h('section', { className: 'dxw-page dim-channelPage', 'aria-label': '微信设置' },
|
|
692
712
|
h(Heading, {
|
|
@@ -721,6 +741,9 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
721
741
|
removeTarget,
|
|
722
742
|
onReconnect: (account) => void reconnect(account),
|
|
723
743
|
onWorkspaceSave: saveWorkspace,
|
|
744
|
+
onModelSave: (account, selectedModel) => saveBotSetting(
|
|
745
|
+
account, 'model', WEIXIN_ENDPOINTS.setModel, { model: selectedModel },
|
|
746
|
+
),
|
|
724
747
|
onAgentPresetSave: (account, agentPreset) => saveBotSetting(
|
|
725
748
|
account, 'preset', WEIXIN_ENDPOINTS.setAgentPreset, { agentPreset },
|
|
726
749
|
),
|
|
@@ -732,5 +755,5 @@ export function WeixinSettingsTab({ rpcCall }) {
|
|
|
732
755
|
onCancelRemove: () => setRemoveTarget(null),
|
|
733
756
|
})
|
|
734
757
|
: null),
|
|
735
|
-
));
|
|
758
|
+
)));
|
|
736
759
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeModelCatalog, normalizeModelSelection, SET_MODEL_ENDPOINT } from '../../model-setting.js';
|
|
2
3
|
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
3
4
|
import { normalizeAccessPolicy } from '../../../../src/channels/shared/access-policy.mjs';
|
|
4
5
|
import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
|
|
@@ -14,6 +15,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
14
15
|
deleteBot: 'bot.delete',
|
|
15
16
|
setAccessPolicy: 'bot.access-policy.set',
|
|
16
17
|
setWorkspace: 'bot.workspace.set',
|
|
18
|
+
setModel: SET_MODEL_ENDPOINT,
|
|
17
19
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
18
20
|
setContextEnhancement: 'bot.context-enhancement.set',
|
|
19
21
|
});
|
|
@@ -90,6 +92,7 @@ function normalizeBot(value) {
|
|
|
90
92
|
connected,
|
|
91
93
|
state: connected ? 'connected' : state,
|
|
92
94
|
workspace: text(value.workspace, '', 4_096),
|
|
95
|
+
model: normalizeModelSelection(value.model),
|
|
93
96
|
agentPreset: normalizeAgentPresetId(value.agentPreset),
|
|
94
97
|
contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
|
|
95
98
|
...(Object.hasOwn(value, 'accessPolicy')
|
|
@@ -124,6 +127,7 @@ export function normalizeSnapshot(value) {
|
|
|
124
127
|
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
|
125
128
|
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
|
|
126
129
|
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
|
|
130
|
+
modelCatalog: normalizeModelCatalog(source.modelCatalog),
|
|
127
131
|
};
|
|
128
132
|
}
|
|
129
133
|
|
|
@@ -10,6 +10,11 @@ import {
|
|
|
10
10
|
AgentPresetEditor,
|
|
11
11
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
12
12
|
} from '../../agent-preset.js';
|
|
13
|
+
import {
|
|
14
|
+
EMPTY_MODEL_CATALOG,
|
|
15
|
+
ModelCatalogContext,
|
|
16
|
+
ModelEditor,
|
|
17
|
+
} from '../../model-setting.js';
|
|
13
18
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
14
19
|
import {
|
|
15
20
|
BotSettingsButton,
|
|
@@ -185,6 +190,7 @@ export function WhatsappAccountCard({
|
|
|
185
190
|
removing,
|
|
186
191
|
onReconnect,
|
|
187
192
|
onWorkspaceSave,
|
|
193
|
+
onModelSave,
|
|
188
194
|
onAgentPresetSave,
|
|
189
195
|
onContextEnhancementSave,
|
|
190
196
|
onRequestRemove,
|
|
@@ -226,6 +232,11 @@ export function WhatsappAccountCard({
|
|
|
226
232
|
disabled: Boolean(busy),
|
|
227
233
|
onSave: onWorkspaceSave,
|
|
228
234
|
}),
|
|
235
|
+
h(ModelEditor, {
|
|
236
|
+
model: account.model,
|
|
237
|
+
disabled: Boolean(busy),
|
|
238
|
+
onSave: onModelSave,
|
|
239
|
+
}),
|
|
229
240
|
h(AgentPresetEditor, {
|
|
230
241
|
agentPreset: account.agentPreset,
|
|
231
242
|
disabled: Boolean(busy),
|
|
@@ -266,6 +277,7 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
266
277
|
const [model, setModel] = React.useState({
|
|
267
278
|
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
|
268
279
|
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
|
280
|
+
modelCatalog: EMPTY_MODEL_CATALOG,
|
|
269
281
|
});
|
|
270
282
|
const [provision, setProvision] = React.useState(null);
|
|
271
283
|
const [busy, setBusy] = React.useState(false);
|
|
@@ -304,6 +316,7 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
304
316
|
setModel({
|
|
305
317
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
306
318
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
319
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
307
320
|
});
|
|
308
321
|
if (restore && snapshot.provisioning) setProvision({
|
|
309
322
|
...snapshot.provisioning,
|
|
@@ -435,6 +448,7 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
435
448
|
setModel({
|
|
436
449
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
437
450
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
451
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
438
452
|
});
|
|
439
453
|
if (operation === 'reconnect') {
|
|
440
454
|
setTestNoticeByBot((current) => ({
|
|
@@ -487,6 +501,12 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
487
501
|
WHATSAPP_ENDPOINTS.setWorkspace,
|
|
488
502
|
{ botId: account.botId, workspace },
|
|
489
503
|
),
|
|
504
|
+
onModelSave: (selectedModel) => botAction(
|
|
505
|
+
account,
|
|
506
|
+
'model',
|
|
507
|
+
WHATSAPP_ENDPOINTS.setModel,
|
|
508
|
+
{ botId: account.botId, model: selectedModel },
|
|
509
|
+
),
|
|
490
510
|
onAgentPresetSave: (agentPreset) => botAction(
|
|
491
511
|
account,
|
|
492
512
|
'preset',
|
|
@@ -511,7 +531,9 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
511
531
|
})))))
|
|
512
532
|
: null;
|
|
513
533
|
|
|
514
|
-
return h(
|
|
534
|
+
return h(ModelCatalogContext.Provider, {
|
|
535
|
+
value: model.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
536
|
+
}, h(AgentPresetCatalogContext.Provider, {
|
|
515
537
|
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
516
538
|
}, h('section', {
|
|
517
539
|
className: 'ddt-page dwa-page dim-channelPage',
|
|
@@ -550,5 +572,5 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
550
572
|
: model.bots.length === 0
|
|
551
573
|
? h(EmptyView, { busy, onStart: () => void startProvisioning(false) })
|
|
552
574
|
: null,
|
|
553
|
-
botList)));
|
|
575
|
+
botList))));
|
|
554
576
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { AccessPolicySettingsPage } from './access-policy-settings.js';
|
|
4
|
+
import { FeishuGroupSettingsPage } from './channels/feishu/group-settings.js';
|
|
4
5
|
import { h, isEnglish, localizeText } from './i18n.js';
|
|
5
6
|
|
|
6
7
|
export const DELIVERY_RPC_CHANNEL = '/dsh-im-delivery';
|
|
@@ -16,6 +17,7 @@ export const DELIVERY_ENDPOINTS = Object.freeze({
|
|
|
16
17
|
create: 'target.create',
|
|
17
18
|
update: 'target.update',
|
|
18
19
|
delete: 'target.delete',
|
|
20
|
+
sessionSync: 'target.session-sync.set',
|
|
19
21
|
test: 'target.test',
|
|
20
22
|
});
|
|
21
23
|
|
|
@@ -24,6 +26,15 @@ export const BOT_SETTINGS_TABS = Object.freeze([
|
|
|
24
26
|
Object.freeze({ id: 'access', label: '访问设置' }),
|
|
25
27
|
]);
|
|
26
28
|
|
|
29
|
+
export const FEISHU_BOT_SETTINGS_TABS = Object.freeze([
|
|
30
|
+
...BOT_SETTINGS_TABS,
|
|
31
|
+
Object.freeze({ id: 'group', label: '群聊' }),
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export function botSettingsTabsForChannel(channel) {
|
|
35
|
+
return channel === 'feishu' ? FEISHU_BOT_SETTINGS_TABS : BOT_SETTINGS_TABS;
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
const CHANNEL_DEFINITIONS = Object.freeze({
|
|
28
39
|
weixin: {
|
|
29
40
|
label: '微信',
|
|
@@ -296,7 +307,9 @@ function TargetForm({
|
|
|
296
307
|
h('div', { className: 'dim-targetFormHeading' },
|
|
297
308
|
h('h3', null, editing ? '编辑投递目标' : '新建投递目标'),
|
|
298
309
|
editing
|
|
299
|
-
?
|
|
310
|
+
? initialValue?.sessionSync?.enabled
|
|
311
|
+
? h('p', null, '修改接收位置会关闭会话双向同步。')
|
|
312
|
+
: null
|
|
300
313
|
: h('p', null, source === 'suggestion'
|
|
301
314
|
? '已从会话自动填入目标信息;确认后再保存。'
|
|
302
315
|
: '请手动填写从对应平台取得的原生标识。')),
|
|
@@ -457,8 +470,17 @@ function TargetSuggestionPicker({
|
|
|
457
470
|
function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
|
|
458
471
|
const [action, setAction] = React.useState(null);
|
|
459
472
|
const [testState, setTestState] = React.useState(null);
|
|
473
|
+
const [syncFeedback, setSyncFeedback] = React.useState(null);
|
|
460
474
|
const [copyState, setCopyState] = React.useState(null);
|
|
461
475
|
const [confirmDelete, setConfirmDelete] = React.useState(false);
|
|
476
|
+
const sessionSync = target.sessionSync ?? { enabled: false, state: 'unavailable' };
|
|
477
|
+
const syncDescription = sessionSync.state === 'active'
|
|
478
|
+
? '自动跟随该私聊的当前会话'
|
|
479
|
+
: sessionSync.state === 'waiting'
|
|
480
|
+
? '等待该私聊建立新会话'
|
|
481
|
+
: sessionSync.state === 'unavailable'
|
|
482
|
+
? '仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标'
|
|
483
|
+
: '关闭时不发送 DSH 会话消息';
|
|
462
484
|
const testTarget = async () => {
|
|
463
485
|
setAction('test');
|
|
464
486
|
setTestState(null);
|
|
@@ -494,6 +516,28 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
|
|
|
494
516
|
}
|
|
495
517
|
};
|
|
496
518
|
|
|
519
|
+
const setSessionSync = async (enabled) => {
|
|
520
|
+
setAction('sync');
|
|
521
|
+
setSyncFeedback(null);
|
|
522
|
+
try {
|
|
523
|
+
await rpcCall(DELIVERY_ENDPOINTS.sessionSync, {
|
|
524
|
+
botId,
|
|
525
|
+
targetId: target.targetId,
|
|
526
|
+
enabled,
|
|
527
|
+
});
|
|
528
|
+
await onChanged();
|
|
529
|
+
} catch (error) {
|
|
530
|
+
setSyncFeedback({
|
|
531
|
+
tone: 'error',
|
|
532
|
+
message: error?.code === 'session-sync-unavailable'
|
|
533
|
+
? '会话同步仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标。'
|
|
534
|
+
: presentError(error, '会话同步设置失败,请稍后重试。'),
|
|
535
|
+
});
|
|
536
|
+
} finally {
|
|
537
|
+
setAction(null);
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
497
541
|
return h('li', { className: 'dim-targetRow', 'data-target-id': target.targetId },
|
|
498
542
|
h('div', { className: 'dim-targetSummary' },
|
|
499
543
|
h('div', { className: 'dim-targetTitle' },
|
|
@@ -514,7 +558,24 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
|
|
|
514
558
|
onClick: () => setConfirmDelete(true),
|
|
515
559
|
disabled: Boolean(action),
|
|
516
560
|
}, '删除')),
|
|
561
|
+
h('label', { className: 'dim-targetSessionSync' },
|
|
562
|
+
h('span', { className: 'dim-targetSessionSyncCopy' },
|
|
563
|
+
h('strong', null, '会话双向同步'),
|
|
564
|
+
h('small', null, syncDescription)),
|
|
565
|
+
h('input', {
|
|
566
|
+
type: 'checkbox',
|
|
567
|
+
checked: sessionSync.enabled === true,
|
|
568
|
+
disabled: Boolean(action)
|
|
569
|
+
|| (sessionSync.enabled !== true && sessionSync.state === 'unavailable'),
|
|
570
|
+
onChange: (event) => void setSessionSync(event.target.checked),
|
|
571
|
+
'aria-label': '会话双向同步',
|
|
572
|
+
})),
|
|
517
573
|
copyState ? h('p', { className: 'dim-targetFeedback', role: 'status' }, copyState) : null,
|
|
574
|
+
syncFeedback ? h('p', {
|
|
575
|
+
className: 'dim-targetFeedback',
|
|
576
|
+
'data-tone': syncFeedback.tone,
|
|
577
|
+
role: 'alert',
|
|
578
|
+
}, syncFeedback.message) : null,
|
|
518
579
|
testState ? h('p', {
|
|
519
580
|
className: 'dim-targetFeedback',
|
|
520
581
|
'data-tone': testState.tone,
|
|
@@ -524,7 +585,8 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
|
|
|
524
585
|
confirmDelete ? h('div', { className: 'dim-targetDeleteConfirm', role: 'alertdialog' },
|
|
525
586
|
h('p', null,
|
|
526
587
|
'删除 ', h('code', null, target.targetId),
|
|
527
|
-
'?使用这个 targetId 的外部调用将返回 unknown-target。'
|
|
588
|
+
'?使用这个 targetId 的外部调用将返回 unknown-target。',
|
|
589
|
+
sessionSync.enabled ? ' 会话同步设置也会一并删除。' : null),
|
|
528
590
|
h('div', { className: 'dim-targetFormActions' },
|
|
529
591
|
h(DeliveryButton, {
|
|
530
592
|
onClick: () => setConfirmDelete(false),
|
|
@@ -672,8 +734,9 @@ export function DeliveryTargetSettingsPage({
|
|
|
672
734
|
}
|
|
673
735
|
};
|
|
674
736
|
|
|
675
|
-
const
|
|
676
|
-
|
|
737
|
+
const settingsTabs = botSettingsTabsForChannel(channel);
|
|
738
|
+
const activeTab = settingsTabs.find((tab) => tab.id === activeTabId)
|
|
739
|
+
?? settingsTabs[0];
|
|
677
740
|
const activeTabDomId = `dim-bot-settings-${activeTab.id}-tab`;
|
|
678
741
|
const activePanelId = `dim-bot-settings-${activeTab.id}-panel`;
|
|
679
742
|
|
|
@@ -688,7 +751,7 @@ export function DeliveryTargetSettingsPage({
|
|
|
688
751
|
className: 'dim-botSettingsTabs',
|
|
689
752
|
role: 'tablist',
|
|
690
753
|
'aria-label': '机器人设置页签',
|
|
691
|
-
},
|
|
754
|
+
}, settingsTabs.map((tab) => h('button', {
|
|
692
755
|
key: tab.id,
|
|
693
756
|
id: `dim-bot-settings-${tab.id}-tab`,
|
|
694
757
|
type: 'button',
|
|
@@ -712,6 +775,11 @@ export function DeliveryTargetSettingsPage({
|
|
|
712
775
|
rpcCall: accessRpcCall,
|
|
713
776
|
onSaved: setAccessPolicy,
|
|
714
777
|
})
|
|
778
|
+
: activeTab.id === 'group' && channel === 'feishu'
|
|
779
|
+
? h(FeishuGroupSettingsPage, {
|
|
780
|
+
account,
|
|
781
|
+
rpcCall: accessRpcCall,
|
|
782
|
+
})
|
|
715
783
|
: h(React.Fragment, null,
|
|
716
784
|
h('section', { className: 'dim-deliveryIdentity', 'aria-labelledby': 'dim-delivery-bot-title' },
|
|
717
785
|
h('div', { className: 'dim-deliveryIdentityHeading' },
|
|
@@ -351,6 +351,13 @@ const EN = Object.freeze({
|
|
|
351
351
|
'请选择 Agent Preset。': 'Choose an Agent Preset.',
|
|
352
352
|
'Agent Preset 无效。': 'The Agent Preset is invalid.',
|
|
353
353
|
'Agent Preset 不存在或不可用。': 'The Agent Preset does not exist or is unavailable.',
|
|
354
|
+
'模型': 'Model',
|
|
355
|
+
'查看模型设置说明': 'View model setting help',
|
|
356
|
+
'跟随默认模型': 'Follow the default model',
|
|
357
|
+
'模型修改失败,请重试。': 'Could not update the model. Try again.',
|
|
358
|
+
'模型无效。': 'The model is invalid.',
|
|
359
|
+
'模型不存在或不可用。': 'The model does not exist or is unavailable.',
|
|
360
|
+
'当前模型已不可用,请选择其他模型或跟随默认模型。': 'The current model is unavailable. Choose another model or follow the default.',
|
|
354
361
|
'尚未检查': 'Not checked yet',
|
|
355
362
|
'刚刚': 'Just now',
|
|
356
363
|
'检查连接': 'Check connection',
|
|
@@ -766,6 +773,10 @@ const EN = Object.freeze({
|
|
|
766
773
|
'填写接收消息的用户 ID': 'Enter the recipient user ID',
|
|
767
774
|
'私聊': 'Direct message',
|
|
768
775
|
'群聊': 'Group',
|
|
776
|
+
'群聊设置': 'Group settings',
|
|
777
|
+
'当前机器人': 'Current bot',
|
|
778
|
+
'飞书群聊设置暂不可用。': 'Feishu group settings are currently unavailable.',
|
|
779
|
+
'飞书服务没有返回当前机器人的设置': 'Feishu did not return settings for this bot',
|
|
769
780
|
'群 Chat ID': 'Group Chat ID',
|
|
770
781
|
'用户 ID': 'User ID',
|
|
771
782
|
'填写钉钉用户 ID': 'Enter the DingTalk user ID',
|
|
@@ -828,6 +839,15 @@ const EN = Object.freeze({
|
|
|
828
839
|
'?使用这个 targetId 的外部调用将返回 unknown-target。': '? External calls using this targetId will return unknown-target.',
|
|
829
840
|
'正在删除…': 'Deleting…',
|
|
830
841
|
'确认删除': 'Delete target',
|
|
842
|
+
'会话双向同步': 'Two-way Session sync',
|
|
843
|
+
'自动跟随该私聊的当前会话': 'Automatically follow this direct message’s current Session',
|
|
844
|
+
'等待该私聊建立新会话': 'Waiting for this direct message to establish a new Session',
|
|
845
|
+
'关闭时不发送 DSH 会话消息': 'DSH Session messages are not sent while disabled',
|
|
846
|
+
'仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标': 'Only a known direct message with a current Session on this Host Harness is supported',
|
|
847
|
+
'会话同步仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标。': 'Session sync requires a known direct message with a current Session on this Host Harness.',
|
|
848
|
+
'会话同步设置失败,请稍后重试。': 'Could not update Session sync. Try again later.',
|
|
849
|
+
'修改接收位置会关闭会话双向同步。': 'Changing the destination disables two-way Session sync.',
|
|
850
|
+
' 会话同步设置也会一并删除。': ' Its Session sync setting will also be deleted.',
|
|
831
851
|
'投递目标设置暂不可用。': 'Delivery target settings are unavailable.',
|
|
832
852
|
'无法读取投递目标,请稍后重试。': 'Could not load delivery targets. Try again later.',
|
|
833
853
|
'← 返回机器人列表': '← Back to bots',
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
EMPTY_MODEL_CATALOG,
|
|
5
|
+
modelSelectionId,
|
|
6
|
+
normalizeModelCatalog,
|
|
7
|
+
normalizeModelSelection,
|
|
8
|
+
} from '../../src/channels/shared/model-setting.mjs';
|
|
9
|
+
import { h } from './i18n.js';
|
|
10
|
+
|
|
11
|
+
export const SET_MODEL_ENDPOINT = 'bot.model.set';
|
|
12
|
+
export { EMPTY_MODEL_CATALOG, normalizeModelCatalog, normalizeModelSelection };
|
|
13
|
+
|
|
14
|
+
export const ModelCatalogContext = React.createContext(EMPTY_MODEL_CATALOG);
|
|
15
|
+
|
|
16
|
+
function optionValue(selection) {
|
|
17
|
+
return JSON.stringify([selection.provider, selection.model]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ModelEditor({ model = null, disabled = false, onSave }) {
|
|
21
|
+
const catalog = normalizeModelCatalog(
|
|
22
|
+
React.useContext(ModelCatalogContext) ?? EMPTY_MODEL_CATALOG,
|
|
23
|
+
);
|
|
24
|
+
const helpId = React.useId();
|
|
25
|
+
const current = normalizeModelSelection(model);
|
|
26
|
+
const [saving, setSaving] = React.useState(false);
|
|
27
|
+
const [error, setError] = React.useState(null);
|
|
28
|
+
const currentValue = current ? optionValue(current) : '';
|
|
29
|
+
const currentAvailable = !current || catalog.groups.some(
|
|
30
|
+
(group) => group.id === current.provider
|
|
31
|
+
&& group.models.some((entry) => entry.id === current.model),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const selections = new Map();
|
|
35
|
+
for (const group of catalog.groups) {
|
|
36
|
+
for (const entry of group.models) {
|
|
37
|
+
const selection = { provider: group.id, model: entry.id };
|
|
38
|
+
selections.set(optionValue(selection), selection);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (current && !currentAvailable) selections.set(currentValue, current);
|
|
42
|
+
|
|
43
|
+
const change = async (event) => {
|
|
44
|
+
const nextValue = event.target.value;
|
|
45
|
+
if (nextValue === currentValue || saving || disabled) return;
|
|
46
|
+
const next = nextValue ? selections.get(nextValue) : null;
|
|
47
|
+
if (nextValue && !next) return;
|
|
48
|
+
setSaving(true);
|
|
49
|
+
setError(null);
|
|
50
|
+
try {
|
|
51
|
+
await onSave?.(next);
|
|
52
|
+
} catch (cause) {
|
|
53
|
+
setError(cause?.message ?? '模型修改失败,请重试。');
|
|
54
|
+
} finally {
|
|
55
|
+
setSaving(false);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return h('div', { className: 'dim-preset dim-modelSetting' },
|
|
60
|
+
h('div', { className: 'dim-presetHeader' },
|
|
61
|
+
h('span', { className: 'dim-presetTitle' },
|
|
62
|
+
h('span', null, '模型'),
|
|
63
|
+
h('span', { className: 'dim-presetHelp' },
|
|
64
|
+
h('button', {
|
|
65
|
+
type: 'button',
|
|
66
|
+
className: 'dim-presetHelpButton',
|
|
67
|
+
'aria-label': '查看模型设置说明',
|
|
68
|
+
'aria-describedby': helpId,
|
|
69
|
+
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
70
|
+
h('span', {
|
|
71
|
+
id: helpId,
|
|
72
|
+
className: 'dim-presetTooltip',
|
|
73
|
+
role: 'tooltip',
|
|
74
|
+
}, '只影响新建会话;若当前聊天已有会话,先发送 /new,再发送普通消息生效。'))),
|
|
75
|
+
saving ? h('span', { className: 'dim-presetStatus' }, '保存中…') : null),
|
|
76
|
+
h('select', {
|
|
77
|
+
className: 'dim-presetSelect dim-modelSelect',
|
|
78
|
+
value: currentValue,
|
|
79
|
+
disabled: disabled || saving,
|
|
80
|
+
'aria-label': '模型',
|
|
81
|
+
onChange: (event) => { void change(event); },
|
|
82
|
+
},
|
|
83
|
+
h('option', { value: '' }, '跟随默认模型'),
|
|
84
|
+
...catalog.groups.map((group) => h(
|
|
85
|
+
'optgroup',
|
|
86
|
+
{ key: group.id, label: group.name },
|
|
87
|
+
...group.models.map((entry) => {
|
|
88
|
+
const selection = { provider: group.id, model: entry.id };
|
|
89
|
+
const id = modelSelectionId(selection);
|
|
90
|
+
return h('option', { key: id, value: optionValue(selection) },
|
|
91
|
+
entry.name && entry.name !== entry.id ? `${entry.name}(${id})` : id);
|
|
92
|
+
}),
|
|
93
|
+
)),
|
|
94
|
+
current && !currentAvailable
|
|
95
|
+
? h('option', { value: currentValue }, [modelSelectionId(current), '(已不可用)'])
|
|
96
|
+
: null,
|
|
97
|
+
),
|
|
98
|
+
error || !currentAvailable ? h(
|
|
99
|
+
'p',
|
|
100
|
+
{ className: 'dim-presetError', role: error ? 'alert' : 'status' },
|
|
101
|
+
error ?? '当前模型已不可用,请选择其他模型或跟随默认模型。',
|
|
102
|
+
) : null,
|
|
103
|
+
);
|
|
104
|
+
}
|