dsh-advisor-group 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -100
- package/README.zh.md +135 -100
- package/lib/client.js +855 -116
- package/lib/index.d.mts +108 -1
- package/lib/index.mjs +3191 -479
- package/package.json +42 -28
package/lib/client.js
CHANGED
|
@@ -283,12 +283,74 @@ const DEFAULT_ADVISOR_PROMPT = `
|
|
|
283
283
|
* advisor-group/message -> update (live bubbles)
|
|
284
284
|
* advisor-group/end -> update (final synopsis)
|
|
285
285
|
*/
|
|
286
|
-
|
|
286
|
+
/** The per-boot token as injected into the page, when that injection arrived. */
|
|
287
|
+
function injectedAdvisorGroupToken() {
|
|
287
288
|
return globalThis.__ADVISOR_GROUP_TOKEN__ ?? "";
|
|
288
289
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
290
|
+
let advisorGroupTokenCache = "";
|
|
291
|
+
let advisorGroupTokenFetch = null;
|
|
292
|
+
/** Token known so far: the injected one wins, otherwise the last handshake. */
|
|
293
|
+
function currentAdvisorGroupToken() {
|
|
294
|
+
return injectedAdvisorGroupToken() || advisorGroupTokenCache;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* The host normally injects the per-boot token into the served index. The
|
|
298
|
+
* desktop shell can compose its window before this plugin registers that
|
|
299
|
+
* injection, so the page may never receive it — fall back to fetching it from
|
|
300
|
+
* the plugin's own handshake route.
|
|
301
|
+
*/
|
|
302
|
+
async function resolveAdvisorGroupToken(force = false) {
|
|
303
|
+
if (!force) {
|
|
304
|
+
const known = currentAdvisorGroupToken();
|
|
305
|
+
if (known) return known;
|
|
306
|
+
}
|
|
307
|
+
if (!advisorGroupTokenFetch) advisorGroupTokenFetch = (async () => {
|
|
308
|
+
try {
|
|
309
|
+
const res = await fetch(`/advisor-group/boot-token`, { headers: { accept: "application/json" } });
|
|
310
|
+
if (!res.ok) return "";
|
|
311
|
+
const body = await res.json();
|
|
312
|
+
return typeof body.token === "string" ? body.token : "";
|
|
313
|
+
} catch {
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
316
|
+
})().then((token) => {
|
|
317
|
+
advisorGroupTokenCache = token;
|
|
318
|
+
advisorGroupTokenFetch = null;
|
|
319
|
+
return token;
|
|
320
|
+
});
|
|
321
|
+
return await advisorGroupTokenFetch || injectedAdvisorGroupToken();
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* `fetch` with the per-boot token attached. A 401 means the token rotated (a
|
|
325
|
+
* live plugin reload mints a new one) or that the injected copy was stale, so
|
|
326
|
+
* refresh it once and retry before surfacing the failure.
|
|
327
|
+
*/
|
|
328
|
+
async function authedFetch(path, init = {}) {
|
|
329
|
+
const send = (token) => fetch(path, {
|
|
330
|
+
...init,
|
|
331
|
+
headers: {
|
|
332
|
+
...init.headers,
|
|
333
|
+
...token ? { "x-advisor-group-token": token } : {}
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
const res = await send(await resolveAdvisorGroupToken());
|
|
337
|
+
if (res.status !== 401) return res;
|
|
338
|
+
const refreshed = await resolveAdvisorGroupToken(true);
|
|
339
|
+
return refreshed ? send(refreshed) : res;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Browser-side twin of `sanitizeAdvisorContent` (host module cannot be
|
|
343
|
+
* imported by the client bundle). The SSE live overlay streams the RAW model
|
|
344
|
+
* output, whose length usually EXCEEDS the sanitized durable message — so the
|
|
345
|
+
* merge below must sanitize whichever content it picks or the raw tool-call
|
|
346
|
+
* markup would keep rendering until the overlay is gone.
|
|
347
|
+
*/
|
|
348
|
+
const CLIENT_RAW_TOOL_CALL_BLOCK = /<(?:[||]{1,2})?(?:tool_calls|DSML)[\s\S]*?<\/(?:[||]{1,2})?(?:tool_calls|DSML)\s*>/g;
|
|
349
|
+
const CLIENT_RAW_TOOL_CALL_TAIL = /<(?:[||]{1,2})?(?:tool_calls|DSML)[\s\S]{0,60}?(?:<invoke|<parameter|<[||]{1,2}DSML)[\s\S]*$/g;
|
|
350
|
+
const CLIENT_EMPTY_BODY_NOTICE = "(顾问未生成正文:输出仅为工具调用描述,请参考其思维链与工具调用记录。)";
|
|
351
|
+
function sanitizeClientContent(content) {
|
|
352
|
+
if (!content.trim()) return content;
|
|
353
|
+
return content.replace(CLIENT_RAW_TOOL_CALL_BLOCK, "").replace(CLIENT_RAW_TOOL_CALL_TAIL, "").trim() || CLIENT_EMPTY_BODY_NOTICE;
|
|
292
354
|
}
|
|
293
355
|
function locationOf(context) {
|
|
294
356
|
return context.start?.location ?? context.matches[0]?.location ?? { kind: "unresolved" };
|
|
@@ -301,7 +363,8 @@ function viewData(state) {
|
|
|
301
363
|
...state.context === void 0 ? {} : { context: state.context },
|
|
302
364
|
advisors: state.advisors,
|
|
303
365
|
messages: state.messages,
|
|
304
|
-
...state.summary === void 0 ? {} : { summary: state.summary }
|
|
366
|
+
...state.summary === void 0 ? {} : { summary: state.summary },
|
|
367
|
+
...state.mode === void 0 ? {} : { mode: state.mode }
|
|
305
368
|
};
|
|
306
369
|
}
|
|
307
370
|
const advisorGroupDefinition = {
|
|
@@ -312,7 +375,7 @@ const advisorGroupDefinition = {
|
|
|
312
375
|
id: event.data.sessionId,
|
|
313
376
|
role: "start"
|
|
314
377
|
};
|
|
315
|
-
if (event.type === "advisor-group/message" || event.type === "advisor-group/delta" || event.type === "advisor-group/end") return {
|
|
378
|
+
if (event.type === "advisor-group/message" || event.type === "advisor-group/delta" || event.type === "advisor-group/end" || event.type === "advisor-group/resume") return {
|
|
316
379
|
id: event.data.sessionId,
|
|
317
380
|
role: "update"
|
|
318
381
|
};
|
|
@@ -321,7 +384,7 @@ const advisorGroupDefinition = {
|
|
|
321
384
|
start: (_context, match) => {
|
|
322
385
|
if (match.event.type !== "advisor-group/start") throw new Error("advisor-group requires advisor-group/start");
|
|
323
386
|
const data = match.event.data;
|
|
324
|
-
|
|
387
|
+
const base = {
|
|
325
388
|
sessionId: data.sessionId,
|
|
326
389
|
turn: data.turn,
|
|
327
390
|
step: data.step,
|
|
@@ -337,6 +400,11 @@ const advisorGroupDefinition = {
|
|
|
337
400
|
}],
|
|
338
401
|
status: "running"
|
|
339
402
|
};
|
|
403
|
+
if (data.kind === "ask") return {
|
|
404
|
+
...base,
|
|
405
|
+
mode: "ask"
|
|
406
|
+
};
|
|
407
|
+
return base;
|
|
340
408
|
},
|
|
341
409
|
update: (context, match) => {
|
|
342
410
|
if (match.event.type === "advisor-group/message") {
|
|
@@ -385,9 +453,13 @@ const advisorGroupDefinition = {
|
|
|
385
453
|
}
|
|
386
454
|
if (match.event.type === "advisor-group/end") return {
|
|
387
455
|
...context.state,
|
|
388
|
-
status: "completed",
|
|
456
|
+
status: match.event.data.summary.stopped ? "cancelled" : "completed",
|
|
389
457
|
summary: match.event.data.summary
|
|
390
458
|
};
|
|
459
|
+
if (match.event.type === "advisor-group/resume") return {
|
|
460
|
+
...context.state,
|
|
461
|
+
status: "running"
|
|
462
|
+
};
|
|
391
463
|
return context.state;
|
|
392
464
|
},
|
|
393
465
|
publication: (match) => match.event.type === "advisor-group/message" || match.event.type === "advisor-group/delta" ? "animation-frame" : "immediate",
|
|
@@ -465,13 +537,6 @@ const headerStyle = {
|
|
|
465
537
|
letterSpacing: 1,
|
|
466
538
|
fontSize: 11
|
|
467
539
|
};
|
|
468
|
-
const questionStyle = {
|
|
469
|
-
color: "#d9f99d",
|
|
470
|
-
fontWeight: 700,
|
|
471
|
-
whiteSpace: "pre-wrap",
|
|
472
|
-
wordBreak: "break-word",
|
|
473
|
-
marginBottom: 6
|
|
474
|
-
};
|
|
475
540
|
const advisorsLineStyle = {
|
|
476
541
|
fontSize: 11,
|
|
477
542
|
color: "#4ade80",
|
|
@@ -507,81 +572,327 @@ const waitingStyle = {
|
|
|
507
572
|
color: "#fbbf24",
|
|
508
573
|
marginTop: 6
|
|
509
574
|
};
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
575
|
+
const noticeStyle = {
|
|
576
|
+
color: "#fda4af",
|
|
577
|
+
marginTop: 6
|
|
578
|
+
};
|
|
579
|
+
/** Internal announcements that must never reach the user (the tool list). */
|
|
580
|
+
const INTERNAL_TOOL_STEP_NAMES = /* @__PURE__ */ new Set(["⚙️ tools", "⚙ tools"]);
|
|
581
|
+
/** True when the tool result represents a failure — the wrapper prefix, or a
|
|
582
|
+
* JSON payload carrying `error`/`detail`/HTTP error code (unconfigured/401
|
|
583
|
+
* tools), robust to nested objects before the error key. */
|
|
584
|
+
function isFailedToolResult(text) {
|
|
585
|
+
if (text.startsWith("(工具执行失败")) return true;
|
|
586
|
+
try {
|
|
587
|
+
const parsed = JSON.parse(text);
|
|
588
|
+
if (parsed && (parsed.error !== void 0 || parsed.detail !== void 0)) return true;
|
|
589
|
+
if (parsed && typeof parsed.code === "number" && parsed.code >= 400 && parsed.code <= 599) return true;
|
|
590
|
+
} catch {}
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Pair call/result tool steps into one `ToolStepView` per invocation, in
|
|
595
|
+
* stream order, and drop internal announcements (「本次可用」).
|
|
596
|
+
*/
|
|
597
|
+
function buildToolStepViews(steps) {
|
|
598
|
+
const out = [];
|
|
599
|
+
let last;
|
|
600
|
+
for (const step of steps ?? []) {
|
|
601
|
+
if (INTERNAL_TOOL_STEP_NAMES.has(step.name)) continue;
|
|
602
|
+
if (step.kind === "call") {
|
|
603
|
+
last = {
|
|
604
|
+
name: step.name,
|
|
605
|
+
input: step.text,
|
|
606
|
+
atMs: step.atMs
|
|
607
|
+
};
|
|
608
|
+
out.push(last);
|
|
609
|
+
} else if (step.kind === "result") {
|
|
610
|
+
if (last && last.output === void 0 && last.input !== void 0) {
|
|
611
|
+
last.output = step.text;
|
|
612
|
+
if (isFailedToolResult(step.text)) last.failed = true;
|
|
613
|
+
} else out.push({
|
|
614
|
+
name: step.name,
|
|
615
|
+
output: step.text,
|
|
616
|
+
atMs: step.atMs,
|
|
617
|
+
...isFailedToolResult(step.text) ? { failed: true } : {}
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return out.filter((view) => view.input !== void 0 || view.output !== void 0);
|
|
622
|
+
}
|
|
623
|
+
const stepRowShellStyle = {
|
|
624
|
+
border: "1px solid rgba(34,197,94,0.28)",
|
|
625
|
+
borderRadius: 3,
|
|
626
|
+
margin: "5px 0",
|
|
627
|
+
background: "rgba(34,197,94,0.05)",
|
|
628
|
+
overflow: "hidden"
|
|
629
|
+
};
|
|
630
|
+
const stepRowHeaderStyle = (color) => ({
|
|
631
|
+
width: "100%",
|
|
632
|
+
display: "flex",
|
|
633
|
+
alignItems: "center",
|
|
634
|
+
gap: 8,
|
|
635
|
+
cursor: "pointer",
|
|
636
|
+
background: "transparent",
|
|
637
|
+
border: "none",
|
|
638
|
+
color,
|
|
639
|
+
fontSize: 12,
|
|
640
|
+
textAlign: "left",
|
|
641
|
+
padding: "4px 8px"
|
|
642
|
+
});
|
|
643
|
+
const stepRowBodyStyle = {
|
|
644
|
+
borderTop: "1px dashed rgba(47,158,68,0.45)",
|
|
645
|
+
padding: "5px 8px"
|
|
646
|
+
};
|
|
647
|
+
function StepRow({ title, badges, defaultOpen, color = "#4ade80", body }) {
|
|
648
|
+
const [open, setOpen] = (0, react.useState)(defaultOpen);
|
|
649
|
+
return (0, react.createElement)("div", { style: stepRowShellStyle }, (0, react.createElement)("button", {
|
|
650
|
+
type: "button",
|
|
651
|
+
onClick: () => setOpen((value) => !value),
|
|
652
|
+
style: stepRowHeaderStyle(color)
|
|
653
|
+
}, (0, react.createElement)("span", null, open ? "▾" : "▸"), (0, react.createElement)("span", { style: { fontWeight: 700 } }, title), ...badges ?? []), open ? (0, react.createElement)("div", { style: stepRowBodyStyle }, body) : null);
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Inline thinking annotation: an UNFRAMED block that sits directly above the
|
|
657
|
+
* tool row it led to (each step reads: 💭 思考文本 → ⛭ 工具), with the full
|
|
658
|
+
* segment visible (scrollable) — not a separate bordered card.
|
|
659
|
+
*/
|
|
660
|
+
const thinkingInlineStyle = {
|
|
661
|
+
margin: "4px 0 2px",
|
|
662
|
+
padding: "4px 8px",
|
|
663
|
+
borderLeft: "2px solid rgba(251,191,36,0.45)",
|
|
664
|
+
background: "rgba(217,119,6,0.04)"
|
|
665
|
+
};
|
|
666
|
+
const thinkingTagStyle = {
|
|
667
|
+
color: "#fbbf24",
|
|
668
|
+
fontSize: 11,
|
|
669
|
+
marginBottom: 2,
|
|
670
|
+
fontWeight: 700
|
|
671
|
+
};
|
|
672
|
+
const descriptionBodyStyle = {
|
|
673
|
+
whiteSpace: "pre-wrap",
|
|
674
|
+
wordBreak: "break-word",
|
|
675
|
+
color: "#d9f99d",
|
|
676
|
+
fontSize: 12,
|
|
677
|
+
padding: "2px 6px 4px"
|
|
678
|
+
};
|
|
679
|
+
/**
|
|
680
|
+
* Short "next-action narrative" of a thinking segment — the visible 📋 行动·N
|
|
681
|
+
* block shown directly after the 💭 思考·N panel and before the ⛭ tool row.
|
|
682
|
+
* Standard-agent style: pick the FIRST SHORT sentence that says what the model
|
|
683
|
+
* is about to do (action markers like 我先/接下来/我需要…), else the first
|
|
684
|
+
* non-generic short sentence; hard-cap at ~140 chars. The long internal
|
|
685
|
+
* monologue stays in the thinking panel.
|
|
686
|
+
*/
|
|
687
|
+
function extractActionDescription(segment) {
|
|
688
|
+
const text = segment.trim();
|
|
689
|
+
if (!text) return "";
|
|
690
|
+
const sentences = (text.match(/[^。!?!?]+[。!?!?]?/g) ?? []).map((sentence) => sentence.replace(/\s+/g, " ").trim()).filter(Boolean);
|
|
691
|
+
const cut = (value, max) => value.length > max ? `${value.slice(0, max)}…` : value;
|
|
692
|
+
const ACTION = /(我先|接下来|下一步|下面|准备|打算|我需要|先做|先看|先检查|先核对|先核实|先验证|先确认|先读|先搜索|先检索|先找|计划|步骤|让我先|我会|需要先|先补|先抓|先取|先查|先分析|先整理|先修改|先跟踪)/;
|
|
693
|
+
const GENERIC = /^(让我|我明白|好的|嗯|OK|Okay|我是|我是一位|我的角色|Let me|Sure|The user|用户)/;
|
|
694
|
+
for (const sentence of sentences.slice(0, 5)) if (sentence.length <= 160 && ACTION.test(sentence)) return cut(sentence, 140);
|
|
695
|
+
for (const sentence of sentences.slice(0, 3)) if (sentence.length <= 120 && !GENERIC.test(sentence)) return cut(sentence, 120);
|
|
696
|
+
return cut(sentences[0] ?? text, 100);
|
|
697
|
+
}
|
|
698
|
+
const thinkingBodyStyle = {
|
|
699
|
+
maxHeight: 200,
|
|
700
|
+
overflowY: "auto",
|
|
701
|
+
whiteSpace: "pre-wrap",
|
|
702
|
+
wordBreak: "break-word",
|
|
703
|
+
color: "#fde68a",
|
|
704
|
+
fontSize: 12,
|
|
705
|
+
padding: "4px 6px"
|
|
706
|
+
};
|
|
707
|
+
const toolBodyStyle = {
|
|
708
|
+
fontSize: 12,
|
|
709
|
+
color: "#38bdf8"
|
|
710
|
+
};
|
|
711
|
+
const toolIoTitleStyle = {
|
|
712
|
+
color: "#86efac",
|
|
713
|
+
fontWeight: 700,
|
|
714
|
+
margin: "2px 0 2px"
|
|
715
|
+
};
|
|
716
|
+
const toolIoBodyStyle = {
|
|
717
|
+
whiteSpace: "pre-wrap",
|
|
718
|
+
wordBreak: "break-word",
|
|
719
|
+
color: "var(--dsh-color-muted, #8b90a0)",
|
|
720
|
+
marginBottom: 6
|
|
721
|
+
};
|
|
722
|
+
const badgeStyle = (color) => ({
|
|
723
|
+
color,
|
|
724
|
+
fontSize: 11,
|
|
725
|
+
opacity: .95
|
|
726
|
+
});
|
|
727
|
+
/**
|
|
728
|
+
* Interleave action announcements, thinking segments and tool views in the
|
|
729
|
+
* official DSH block order (text → reasoning → tool-call), then the body.
|
|
730
|
+
* Each round contributes at most one 行动 / 思考 / 工具 item; missing actions
|
|
731
|
+
* are derived from the thinking segment so pre-actionDescriptions logs still
|
|
732
|
+
* render sensibly.
|
|
733
|
+
*/
|
|
734
|
+
function buildStepSequence(actionDescriptions, thinkingSegments, toolViews) {
|
|
735
|
+
const items = [];
|
|
736
|
+
const count = Math.max(actionDescriptions.length, thinkingSegments.length, toolViews.length);
|
|
737
|
+
for (let index = 0; index < count; index += 1) {
|
|
738
|
+
const step = index + 1;
|
|
739
|
+
const described = actionDescriptions[index]?.trim();
|
|
740
|
+
const segment = thinkingSegments[index];
|
|
741
|
+
const action = described || (segment !== void 0 && segment.trim() ? extractActionDescription(segment) : "");
|
|
742
|
+
if (action) items.push({
|
|
743
|
+
kind: "action",
|
|
744
|
+
text: action,
|
|
745
|
+
step
|
|
746
|
+
});
|
|
747
|
+
if (segment !== void 0 && segment.trim()) items.push({
|
|
748
|
+
kind: "thinking",
|
|
749
|
+
segment,
|
|
750
|
+
step
|
|
751
|
+
});
|
|
752
|
+
const view = toolViews[index];
|
|
753
|
+
if (view !== void 0) items.push({
|
|
754
|
+
kind: "tool",
|
|
755
|
+
view
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
items.push({ kind: "body" });
|
|
759
|
+
return items;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Render one consultation message as a stack of STEP rows (one row per step,
|
|
763
|
+
* appearing in stream order, each independently expandable):
|
|
764
|
+
* 💭 思考·1 → ⛭ 工具×1(输入/输出)→ 💭 思考·2 → ⛭ 工具×2 → … → 📄 正文
|
|
765
|
+
* Main/system messages stay a single collapsible row.
|
|
766
|
+
*/
|
|
767
|
+
function AdvisorSteps({ message, plain = false }) {
|
|
516
768
|
const isMain = message.role === "main";
|
|
517
769
|
const isSystem = message.role === "system";
|
|
518
|
-
const justify = isMain ? "flex-end" : "flex-start";
|
|
519
|
-
const background = isSystem ? "rgba(217,119,6,0.08)" : isMain ? "rgba(34,197,94,0.14)" : "rgba(34,197,94,0.06)";
|
|
520
|
-
const borderColor = isSystem ? "#b45309" : "#16a34a";
|
|
521
770
|
const label = isMain ? "YOU" : isSystem ? "SYSTEM" : message.advisorName ?? message.advisorId ?? "ADVISOR";
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
background
|
|
533
|
-
} }, (0, react.createElement)("div", { style: {
|
|
534
|
-
fontSize: 11,
|
|
535
|
-
color: isSystem ? "#fcd34d" : "#4ade80",
|
|
536
|
-
marginBottom: 2
|
|
537
|
-
} }, `${label}${message.round ? ` · R${message.round}` : ""}`), hasThinking ? (0, react.createElement)("div", { style: {
|
|
538
|
-
border: "1px dashed #a16207",
|
|
539
|
-
borderRadius: 3,
|
|
540
|
-
marginBottom: 4,
|
|
541
|
-
background: "rgba(217,119,6,0.06)"
|
|
542
|
-
} }, (0, react.createElement)("button", {
|
|
543
|
-
type: "button",
|
|
544
|
-
onClick: () => setThinkingCollapsed((value) => !value),
|
|
545
|
-
style: {
|
|
546
|
-
width: "100%",
|
|
547
|
-
cursor: "pointer",
|
|
548
|
-
background: "transparent",
|
|
549
|
-
border: "none",
|
|
550
|
-
color: "#fbbf24",
|
|
551
|
-
fontSize: 11,
|
|
552
|
-
textAlign: "left",
|
|
553
|
-
padding: "3px 6px"
|
|
554
|
-
}
|
|
555
|
-
}, `💭 ${thinkingCollapsed ? "已思考(点击展开)" : "思维链(点击收起)"} ${thinkingCollapsed ? "▸" : "▾"}`), !thinkingCollapsed ? (0, react.createElement)("div", {
|
|
556
|
-
ref: (el) => {
|
|
557
|
-
thinkingPanelRef.current = el;
|
|
558
|
-
},
|
|
559
|
-
style: {
|
|
560
|
-
maxHeight: 160,
|
|
561
|
-
overflowY: "auto",
|
|
771
|
+
const group = isMain || isSystem || plain ? label : `${label} · R${message.round ?? 1}`;
|
|
772
|
+
const truncated = message.truncated;
|
|
773
|
+
const toolViews = buildToolStepViews(message.toolSteps);
|
|
774
|
+
const thinkingSegments = message.thinkingSegments && message.thinkingSegments.length > 0 ? message.thinkingSegments : message.thinking ? [message.thinking] : [];
|
|
775
|
+
const baseColor = isSystem ? "#fcd34d" : "#4ade80";
|
|
776
|
+
if (isMain || isSystem) return (0, react.createElement)(StepRow, {
|
|
777
|
+
title: group,
|
|
778
|
+
defaultOpen: isMain,
|
|
779
|
+
color: baseColor,
|
|
780
|
+
body: (0, react.createElement)("div", { style: {
|
|
562
781
|
whiteSpace: "pre-wrap",
|
|
563
|
-
wordBreak: "break-word"
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
782
|
+
wordBreak: "break-word"
|
|
783
|
+
} }, message.content)
|
|
784
|
+
});
|
|
785
|
+
const sequence = buildStepSequence(message.actionDescriptions ?? [], thinkingSegments, toolViews);
|
|
786
|
+
const live = message;
|
|
787
|
+
const liveToolPhase = live.livePhase === "tool";
|
|
788
|
+
const liveAction = liveToolPhase && live.liveBody?.trim() ? live.liveBody.trim() : void 0;
|
|
789
|
+
const children = [];
|
|
790
|
+
for (const [index, item] of sequence.entries()) {
|
|
791
|
+
if (item.kind === "action") {
|
|
792
|
+
children.push((0, react.createElement)("div", {
|
|
793
|
+
key: `action-${index}`,
|
|
794
|
+
style: thinkingInlineStyle
|
|
795
|
+
}, (0, react.createElement)("div", { style: thinkingTagStyle }, `${group} 📋 行动·${item.step}`), (0, react.createElement)("div", { style: descriptionBodyStyle }, item.text)));
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (item.kind === "thinking") {
|
|
799
|
+
children.push((0, react.createElement)(StepRow, {
|
|
800
|
+
key: `think-${index}`,
|
|
801
|
+
title: `${group} 💭 思考·${item.step}`,
|
|
802
|
+
badges: [(0, react.createElement)("span", { style: badgeStyle("#fbbf24") }, `${item.segment.length} 字符`)],
|
|
803
|
+
defaultOpen: false,
|
|
804
|
+
color: "#fbbf24",
|
|
805
|
+
body: (0, react.createElement)("div", { style: thinkingBodyStyle }, item.segment)
|
|
806
|
+
}));
|
|
807
|
+
continue;
|
|
568
808
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
809
|
+
if (item.kind === "tool") {
|
|
810
|
+
const view = item.view;
|
|
811
|
+
children.push((0, react.createElement)(StepRow, {
|
|
812
|
+
key: `tool-${index}`,
|
|
813
|
+
title: `${group} ⛭ ${view.name}`,
|
|
814
|
+
badges: [(0, react.createElement)("span", { style: badgeStyle(view.failed ? "#f87171" : "#38bdf8") }, view.output === void 0 ? "调用中…" : view.failed ? "⚠ 失败" : "调用完成")],
|
|
815
|
+
defaultOpen: false,
|
|
816
|
+
color: view.failed ? "#f87171" : "#38bdf8",
|
|
817
|
+
body: (0, react.createElement)("div", null, view.input !== void 0 ? (0, react.createElement)("div", { style: toolBodyStyle }, (0, react.createElement)("div", { style: toolIoTitleStyle }, "输入"), (0, react.createElement)("div", { style: toolIoBodyStyle }, view.input)) : null, view.output !== void 0 ? (0, react.createElement)("div", { style: toolBodyStyle }, (0, react.createElement)("div", { style: toolIoTitleStyle }, "输出"), (0, react.createElement)("div", { style: toolIoBodyStyle }, view.output)) : null)
|
|
818
|
+
}));
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (liveToolPhase) continue;
|
|
822
|
+
children.push((0, react.createElement)(StepRow, {
|
|
823
|
+
key: `body-${index}`,
|
|
824
|
+
title: plain ? group : `${group} 📄 正文`,
|
|
825
|
+
badges: truncated ? [(0, react.createElement)("span", { style: badgeStyle("#f87171") }, truncated.reason === "timeout" ? "⏱ 响应超时截断" : "⚠ 流中断")] : [],
|
|
826
|
+
defaultOpen: true,
|
|
827
|
+
color: "#4ade80",
|
|
828
|
+
body: (0, react.createElement)("div", { style: { wordBreak: "break-word" } }, truncated ? (0, react.createElement)("div", { style: {
|
|
829
|
+
fontSize: 11,
|
|
830
|
+
color: "#f87171",
|
|
831
|
+
border: "1px solid #dc2626",
|
|
832
|
+
borderRadius: 3,
|
|
833
|
+
padding: "2px 6px",
|
|
834
|
+
display: "inline-block",
|
|
835
|
+
marginBottom: 3
|
|
836
|
+
} }, truncated.reason === "timeout" ? "⏱ 响应超时截断 — 思考已收到,正文未完成" : "⚠ 流中断 — 正文可能不完整") : null, renderMarkdown(message.content))
|
|
837
|
+
}));
|
|
838
|
+
}
|
|
839
|
+
if (liveAction !== void 0) children.push((0, react.createElement)("div", {
|
|
840
|
+
key: "live-action",
|
|
841
|
+
style: thinkingInlineStyle
|
|
842
|
+
}, (0, react.createElement)("div", { style: thinkingTagStyle }, `${group} 📋 行动·${toolViews.length + 1}`), (0, react.createElement)("div", { style: descriptionBodyStyle }, liveAction)));
|
|
843
|
+
return (0, react.createElement)("div", null, children);
|
|
573
844
|
}
|
|
845
|
+
/** Auto-hide delay for transient card notices (request failures, resync). */
|
|
846
|
+
const NOTICE_TTL_MS = 6e3;
|
|
574
847
|
function AdvisorGroupNodeView(props) {
|
|
575
848
|
const data = props.node.data;
|
|
576
849
|
const sessionId = data.sessionId;
|
|
850
|
+
const isAsk = data.mode === "ask";
|
|
577
851
|
const [live, setLive] = (0, react.useState)({});
|
|
578
852
|
const [contextCollapsed, setContextCollapsed] = (0, react.useState)(true);
|
|
579
853
|
const lastEventIdRef = (0, react.useRef)(0);
|
|
580
854
|
const lastBootIdRef = (0, react.useRef)("");
|
|
855
|
+
const [transientNotice, setTransientNotice] = (0, react.useState)(null);
|
|
856
|
+
const noticeTimerRef = (0, react.useRef)(null);
|
|
857
|
+
const showTransientNotice = (text, kind, ttlMs) => {
|
|
858
|
+
if (noticeTimerRef.current !== null) {
|
|
859
|
+
clearTimeout(noticeTimerRef.current);
|
|
860
|
+
noticeTimerRef.current = null;
|
|
861
|
+
}
|
|
862
|
+
setTransientNotice({
|
|
863
|
+
text,
|
|
864
|
+
kind
|
|
865
|
+
});
|
|
866
|
+
if (ttlMs !== void 0) noticeTimerRef.current = setTimeout(() => {
|
|
867
|
+
noticeTimerRef.current = null;
|
|
868
|
+
setTransientNotice(null);
|
|
869
|
+
}, ttlMs);
|
|
870
|
+
};
|
|
871
|
+
const clearStreamNotice = () => {
|
|
872
|
+
setTransientNotice((prev) => prev && prev.kind === "stream" ? null : prev);
|
|
873
|
+
};
|
|
874
|
+
const [streamTokenReady, setStreamTokenReady] = (0, react.useState)(() => currentAdvisorGroupToken() !== "");
|
|
581
875
|
(0, react.useEffect)(() => {
|
|
582
|
-
if (
|
|
583
|
-
|
|
876
|
+
if (streamTokenReady) return;
|
|
877
|
+
let alive = true;
|
|
878
|
+
resolveAdvisorGroupToken().then((token) => {
|
|
879
|
+
if (alive && token) setStreamTokenReady(true);
|
|
880
|
+
});
|
|
881
|
+
return () => {
|
|
882
|
+
alive = false;
|
|
883
|
+
};
|
|
884
|
+
}, [streamTokenReady]);
|
|
885
|
+
(0, react.useEffect)(() => {
|
|
886
|
+
if (!sessionId || isAsk || !streamTokenReady) return;
|
|
887
|
+
const es = new EventSource(`/advisor-group/stream?sessionId=${encodeURIComponent(sessionId)}&lastEventId=${lastEventIdRef.current}&bootId=${encodeURIComponent(lastBootIdRef.current)}&token=${encodeURIComponent(currentAdvisorGroupToken())}`);
|
|
888
|
+
es.onopen = () => {
|
|
889
|
+
clearStreamNotice();
|
|
890
|
+
};
|
|
891
|
+
es.onerror = () => {
|
|
892
|
+
showTransientNotice("连接中断,正在重连…", "stream");
|
|
893
|
+
};
|
|
584
894
|
es.onmessage = (event) => {
|
|
895
|
+
clearStreamNotice();
|
|
585
896
|
try {
|
|
586
897
|
const delta = JSON.parse(event.data);
|
|
587
898
|
const bootId = delta.bootId ?? "";
|
|
@@ -593,53 +904,165 @@ function AdvisorGroupNodeView(props) {
|
|
|
593
904
|
const eventId = Number(delta.eventId ?? 0);
|
|
594
905
|
if (eventId > 0 && eventId <= lastEventIdRef.current) return;
|
|
595
906
|
if (eventId > 0) lastEventIdRef.current = eventId;
|
|
907
|
+
const bucket = `${delta.advisorId}::${delta.round ?? 1}`;
|
|
596
908
|
setLive((prev) => {
|
|
597
|
-
const current = prev[
|
|
909
|
+
const current = prev[bucket] ?? {
|
|
598
910
|
content: "",
|
|
599
|
-
thinking: ""
|
|
911
|
+
thinking: "",
|
|
912
|
+
thinkingSegments: [],
|
|
913
|
+
committedLen: 0,
|
|
914
|
+
contentSegments: [],
|
|
915
|
+
committedContentLen: 0,
|
|
916
|
+
phase: "answer",
|
|
917
|
+
finalized: false,
|
|
918
|
+
toolSteps: []
|
|
600
919
|
};
|
|
920
|
+
let thinkingSegments = current.thinkingSegments;
|
|
921
|
+
let committedLen = current.committedLen;
|
|
922
|
+
let contentSegments = current.contentSegments;
|
|
923
|
+
let committedContentLen = current.committedContentLen;
|
|
924
|
+
if (delta.toolStep) {
|
|
925
|
+
const segment = current.thinking.slice(committedLen);
|
|
926
|
+
if (segment.trim()) thinkingSegments = [...current.thinkingSegments, segment];
|
|
927
|
+
committedLen = current.thinking.length;
|
|
928
|
+
const contentSegment = current.content.slice(committedContentLen);
|
|
929
|
+
if (contentSegment.trim()) contentSegments = [...current.contentSegments, contentSegment];
|
|
930
|
+
committedContentLen = current.content.length;
|
|
931
|
+
}
|
|
601
932
|
return {
|
|
602
933
|
...prev,
|
|
603
|
-
[
|
|
934
|
+
[bucket]: {
|
|
604
935
|
content: current.content + (delta.contentDelta ?? ""),
|
|
605
|
-
thinking: current.thinking + (delta.thinkingDelta ?? "")
|
|
936
|
+
thinking: current.thinking + (delta.thinkingDelta ?? ""),
|
|
937
|
+
thinkingSegments,
|
|
938
|
+
committedLen,
|
|
939
|
+
contentSegments,
|
|
940
|
+
committedContentLen,
|
|
941
|
+
phase: delta.phase ?? current.phase,
|
|
942
|
+
finalized: current.finalized || delta.done === true,
|
|
943
|
+
toolSteps: delta.toolStep ? [...current.toolSteps, delta.toolStep] : current.toolSteps
|
|
606
944
|
}
|
|
607
945
|
};
|
|
608
946
|
});
|
|
609
947
|
} catch {}
|
|
610
948
|
};
|
|
611
|
-
es.addEventListener("resync", () => {
|
|
949
|
+
es.addEventListener("resync", (event) => {
|
|
612
950
|
lastEventIdRef.current = 0;
|
|
613
951
|
setLive({});
|
|
952
|
+
let reason = "";
|
|
953
|
+
try {
|
|
954
|
+
reason = JSON.parse(event.data).reason ?? "";
|
|
955
|
+
} catch {}
|
|
956
|
+
showTransientNotice(reason === "restart" ? "服务端已重启,正在补齐对话内容…" : reason === "gap" ? "正在补齐断开期间的内容…" : "正在补齐对话内容…", "stream", NOTICE_TTL_MS);
|
|
614
957
|
});
|
|
615
|
-
return () =>
|
|
616
|
-
|
|
958
|
+
return () => {
|
|
959
|
+
es.close();
|
|
960
|
+
if (noticeTimerRef.current !== null) {
|
|
961
|
+
clearTimeout(noticeTimerRef.current);
|
|
962
|
+
noticeTimerRef.current = null;
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
}, [sessionId, streamTokenReady]);
|
|
617
966
|
const mergedMessages = data.messages.map((message) => {
|
|
618
967
|
if (message.role !== "advisor") return message;
|
|
619
968
|
const advisorId = message.advisorId ?? "";
|
|
620
|
-
const streamed = live[advisorId];
|
|
969
|
+
const streamed = live[`${advisorId}::${message.round ?? 1}`];
|
|
621
970
|
if (!streamed) return message;
|
|
622
|
-
|
|
971
|
+
if (streamed.finalized) return message;
|
|
972
|
+
const liveBody = sanitizeClientContent(streamed.content.slice(streamed.committedContentLen));
|
|
973
|
+
const content = liveBody.trim() ? liveBody : sanitizeClientContent(message.content);
|
|
623
974
|
const thinking = (streamed.thinking?.length ?? 0) > (message.thinking?.length ?? 0) ? streamed.thinking : message.thinking;
|
|
975
|
+
const toolSteps = (streamed.toolSteps?.length ?? 0) > (message.toolSteps?.length ?? 0) ? streamed.toolSteps : message.toolSteps ?? [];
|
|
976
|
+
const liveSegments = streamed.thinkingSegments?.length ? streamed.thinkingSegments : [];
|
|
977
|
+
const baseSegments = message.thinkingSegments?.length ? message.thinkingSegments : message.thinking ? [message.thinking] : [];
|
|
978
|
+
const thinkingSegments = liveSegments.length ? liveSegments : baseSegments;
|
|
979
|
+
const liveActions = streamed.contentSegments?.length ? streamed.contentSegments.map((segment) => sanitizeClientContent(segment)) : [];
|
|
980
|
+
const baseActions = message.actionDescriptions?.length ? message.actionDescriptions : [];
|
|
981
|
+
const actionDescriptions = liveActions.length ? liveActions : baseActions;
|
|
624
982
|
return {
|
|
625
983
|
...message,
|
|
626
984
|
content,
|
|
627
|
-
thinking
|
|
985
|
+
thinking,
|
|
986
|
+
thinkingSegments,
|
|
987
|
+
toolSteps,
|
|
988
|
+
actionDescriptions,
|
|
989
|
+
...streamed.phase === "tool" ? {
|
|
990
|
+
liveBody,
|
|
991
|
+
livePhase: streamed.phase
|
|
992
|
+
} : {}
|
|
628
993
|
};
|
|
629
994
|
});
|
|
630
995
|
const shortId = data.sessionId.length > 8 ? data.sessionId.slice(0, 8) : data.sessionId;
|
|
631
|
-
const title = data.status === "completed" ? "ADVISOR GROUP · DONE" : "ADVISOR GROUP · LIVE";
|
|
996
|
+
const title = isAsk ? data.status === "completed" ? "跨会话提问 · 已完成" : "跨会话提问 · 等待应答" : data.status === "completed" ? "ADVISOR GROUP · DONE" : data.status === "cancelled" ? "ADVISOR GROUP · STOPPED" : "ADVISOR GROUP · LIVE";
|
|
632
997
|
return (0, react.createElement)("div", {
|
|
633
998
|
style: rootStyle,
|
|
634
999
|
role: "log",
|
|
635
1000
|
"aria-live": "polite"
|
|
636
|
-
}, (0, react.createElement)("div", { style: scanlineStyle }), (0, react.createElement)("div", { style: headerStyle }, (0, react.createElement)("span", { key: "title" }, title), (0, react.createElement)("span", {
|
|
1001
|
+
}, (0, react.createElement)("div", { style: scanlineStyle }), (0, react.createElement)("div", { style: headerStyle }, (0, react.createElement)("span", { key: "title" }, title), (0, react.createElement)("span", {
|
|
1002
|
+
key: "stop",
|
|
1003
|
+
style: {
|
|
1004
|
+
display: "flex",
|
|
1005
|
+
alignItems: "center",
|
|
1006
|
+
gap: 8
|
|
1007
|
+
}
|
|
1008
|
+
}, data.status === "running" && !isAsk ? (0, react.createElement)("button", {
|
|
1009
|
+
type: "button",
|
|
1010
|
+
onClick: () => {
|
|
1011
|
+
(async () => {
|
|
1012
|
+
try {
|
|
1013
|
+
const body = await (await authedFetch("/advisor-group/stop", {
|
|
1014
|
+
method: "POST",
|
|
1015
|
+
headers: { "content-type": "application/json" },
|
|
1016
|
+
body: JSON.stringify({ sessionId })
|
|
1017
|
+
})).json();
|
|
1018
|
+
if (body.ok === false) showTransientNotice(body.error ?? "请求未被服务端接受,请稍后重试。", "action", NOTICE_TTL_MS);
|
|
1019
|
+
} catch {
|
|
1020
|
+
showTransientNotice("网络异常,请稍后重试。", "action", NOTICE_TTL_MS);
|
|
1021
|
+
}
|
|
1022
|
+
})();
|
|
1023
|
+
},
|
|
1024
|
+
style: {
|
|
1025
|
+
cursor: "pointer",
|
|
1026
|
+
border: "1px solid #e11d48",
|
|
1027
|
+
background: "transparent",
|
|
1028
|
+
color: "#fda4af",
|
|
1029
|
+
borderRadius: 4,
|
|
1030
|
+
padding: "2px 8px",
|
|
1031
|
+
fontSize: 11
|
|
1032
|
+
}
|
|
1033
|
+
}, "⏹ 停止") : data.status === "cancelled" && !isAsk ? (0, react.createElement)("button", {
|
|
1034
|
+
type: "button",
|
|
1035
|
+
onClick: () => {
|
|
1036
|
+
(async () => {
|
|
1037
|
+
try {
|
|
1038
|
+
const body = await (await authedFetch("/advisor-group/resume", {
|
|
1039
|
+
method: "POST",
|
|
1040
|
+
headers: { "content-type": "application/json" },
|
|
1041
|
+
body: JSON.stringify({ sessionId })
|
|
1042
|
+
})).json();
|
|
1043
|
+
if (body.ok === false) showTransientNotice(body.error ?? "请求未被服务端接受,请稍后重试。", "action", NOTICE_TTL_MS);
|
|
1044
|
+
} catch {
|
|
1045
|
+
showTransientNotice("网络异常,请稍后重试。", "action", NOTICE_TTL_MS);
|
|
1046
|
+
}
|
|
1047
|
+
})();
|
|
1048
|
+
},
|
|
1049
|
+
style: {
|
|
1050
|
+
cursor: "pointer",
|
|
1051
|
+
border: "1px solid #22c55e",
|
|
1052
|
+
background: "transparent",
|
|
1053
|
+
color: "#86efac",
|
|
1054
|
+
borderRadius: 4,
|
|
1055
|
+
padding: "2px 8px",
|
|
1056
|
+
fontSize: 11
|
|
1057
|
+
}
|
|
1058
|
+
}, "▶ 继续聊天") : null, (0, react.createElement)("span", { key: "id" }, `#${shortId}`))), data.context ? (0, react.createElement)("div", { style: contextBoxStyle }, (0, react.createElement)("button", {
|
|
637
1059
|
type: "button",
|
|
638
1060
|
onClick: () => setContextCollapsed((value) => !value),
|
|
639
1061
|
style: contextToggleStyle
|
|
640
|
-
}, `📋 项目背景 ${contextCollapsed ? "▸" : "▾"}`), !contextCollapsed ? (0, react.createElement)("div", { style: contextBodyStyle }, data.context) : null) : null, (0, react.createElement)("div", { style: advisorsLineStyle }, `ADVISORS: ${data.advisors.map((a) => a.avatar ? `${a.avatar} ${a.name}` : a.name).join(" · ")}`), mergedMessages.map((message, index) => (0, react.createElement)(
|
|
1062
|
+
}, `📋 项目背景 ${contextCollapsed ? "▸" : "▾"}`), !contextCollapsed ? (0, react.createElement)("div", { style: contextBodyStyle }, data.context) : null) : null, (0, react.createElement)("div", { style: advisorsLineStyle }, isAsk ? `提问方: ${data.advisors[0]?.name ?? "?"} → 目标会话: ${data.advisors[1]?.name ?? "?"}` : `ADVISORS: ${data.advisors.map((a) => a.avatar ? `${a.avatar} ${a.name}` : a.name).join(" · ")}`), mergedMessages.map((message, index) => (0, react.createElement)(AdvisorSteps, {
|
|
641
1063
|
key: index,
|
|
642
|
-
message
|
|
1064
|
+
message,
|
|
1065
|
+
plain: isAsk
|
|
643
1066
|
})), data.status === "completed" && data.summary?.conclusion ? (0, react.createElement)("div", { style: {
|
|
644
1067
|
borderTop: "1px dashed #2f9e44",
|
|
645
1068
|
marginTop: 8,
|
|
@@ -648,7 +1071,32 @@ function AdvisorGroupNodeView(props) {
|
|
|
648
1071
|
color: "#86efac",
|
|
649
1072
|
fontWeight: 700,
|
|
650
1073
|
marginBottom: 4
|
|
651
|
-
} }, "📌 综合结论"), (0, react.createElement)("div", null, renderMarkdown(data.summary.conclusion))) : null, data.status === "
|
|
1074
|
+
} }, isAsk ? "📌 投递说明" : "📌 综合结论"), (0, react.createElement)("div", null, renderMarkdown(data.summary.conclusion))) : null, data.status === "completed" && (data.summary?.riskNotes ?? []).length > 0 ? (0, react.createElement)("div", { style: {
|
|
1075
|
+
borderTop: "1px dashed #2f9e44",
|
|
1076
|
+
marginTop: 8,
|
|
1077
|
+
paddingTop: 8
|
|
1078
|
+
} }, (data.summary?.riskNotes ?? []).map((note, index) => (0, react.createElement)("div", {
|
|
1079
|
+
key: index,
|
|
1080
|
+
style: {
|
|
1081
|
+
color: "#fbbf24",
|
|
1082
|
+
fontSize: 12,
|
|
1083
|
+
marginTop: 2
|
|
1084
|
+
}
|
|
1085
|
+
}, note))) : null, data.status === "cancelled" ? (0, react.createElement)("div", { style: {
|
|
1086
|
+
borderTop: "1px dashed #2f9e44",
|
|
1087
|
+
marginTop: 8,
|
|
1088
|
+
paddingTop: 8
|
|
1089
|
+
} }, (0, react.createElement)("div", { style: {
|
|
1090
|
+
color: "#fde68a",
|
|
1091
|
+
marginBottom: 4
|
|
1092
|
+
} }, "本次讨论已停止;点 ▶ 继续聊天 可从断点续跑。"), (data.summary?.riskNotes ?? []).map((note, index) => (0, react.createElement)("div", {
|
|
1093
|
+
key: index,
|
|
1094
|
+
style: {
|
|
1095
|
+
color: "#fbbf24",
|
|
1096
|
+
fontSize: 12,
|
|
1097
|
+
marginTop: 2
|
|
1098
|
+
}
|
|
1099
|
+
}, note))) : null, data.status === "running" ? (0, react.createElement)("div", { style: waitingStyle }, isAsk ? "▊ 已投递,等待对方回应…" : "▊ AWAITING RESPONSES…") : null, transientNotice ? (0, react.createElement)("div", { style: noticeStyle }, transientNotice.text) : null);
|
|
652
1100
|
}
|
|
653
1101
|
const settingsRootStyle = {
|
|
654
1102
|
fontFamily: "'Inter', 'Segoe UI', system-ui, sans-serif",
|
|
@@ -815,6 +1263,21 @@ const AVATAR_OPTIONS = [
|
|
|
815
1263
|
"🚀",
|
|
816
1264
|
"🛡️"
|
|
817
1265
|
];
|
|
1266
|
+
function defaultJevConfig() {
|
|
1267
|
+
return {
|
|
1268
|
+
enabled: false,
|
|
1269
|
+
provider: "typesafe",
|
|
1270
|
+
model: "jev-latest",
|
|
1271
|
+
baseURL: "",
|
|
1272
|
+
timeoutMs: 1e4,
|
|
1273
|
+
confidenceThreshold: .6,
|
|
1274
|
+
useEnglishState: false
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
const JEV_DEFAULT_ENDPOINTS = {
|
|
1278
|
+
typesafe: "https://api.typesafe.ai/v1/systemone",
|
|
1279
|
+
openrouter: "https://openrouter.ai/api/alpha/decisions"
|
|
1280
|
+
};
|
|
818
1281
|
function AdvisorGroupSettingsTab() {
|
|
819
1282
|
const [config, setConfig] = (0, react.useState)(null);
|
|
820
1283
|
const [providers, setProviders] = (0, react.useState)([]);
|
|
@@ -833,7 +1296,7 @@ function AdvisorGroupSettingsTab() {
|
|
|
833
1296
|
setMsg("");
|
|
834
1297
|
setError("");
|
|
835
1298
|
try {
|
|
836
|
-
const [configData, providerData] = await Promise.all([
|
|
1299
|
+
const [configData, providerData] = await Promise.all([authedFetch("/advisor-group/config").then((r) => r.json()), authedFetch("/advisor-group/providers").then((r) => r.json())]);
|
|
837
1300
|
if (configData.ok && configData.config) {
|
|
838
1301
|
setConfig(configData.config);
|
|
839
1302
|
savedJsonRef.current = JSON.stringify(configData.config);
|
|
@@ -889,12 +1352,9 @@ function AdvisorGroupSettingsTab() {
|
|
|
889
1352
|
setError("");
|
|
890
1353
|
setMsg("");
|
|
891
1354
|
try {
|
|
892
|
-
const data = await (await
|
|
1355
|
+
const data = await (await authedFetch("/advisor-group/models", {
|
|
893
1356
|
method: "POST",
|
|
894
|
-
headers: {
|
|
895
|
-
"content-type": "application/json",
|
|
896
|
-
...authHeaders()
|
|
897
|
-
},
|
|
1357
|
+
headers: { "content-type": "application/json" },
|
|
898
1358
|
body: JSON.stringify({
|
|
899
1359
|
advisorId: advisor.id,
|
|
900
1360
|
provider: advisor.provider,
|
|
@@ -930,12 +1390,9 @@ function AdvisorGroupSettingsTab() {
|
|
|
930
1390
|
setError("");
|
|
931
1391
|
setMsg("");
|
|
932
1392
|
try {
|
|
933
|
-
const data = await (await
|
|
1393
|
+
const data = await (await authedFetch("/advisor-group/test-connection", {
|
|
934
1394
|
method: "POST",
|
|
935
|
-
headers: {
|
|
936
|
-
"content-type": "application/json",
|
|
937
|
-
...authHeaders()
|
|
938
|
-
},
|
|
1395
|
+
headers: { "content-type": "application/json" },
|
|
939
1396
|
body: JSON.stringify({
|
|
940
1397
|
advisorId: advisor.id,
|
|
941
1398
|
provider: advisor.provider,
|
|
@@ -963,14 +1420,15 @@ function AdvisorGroupSettingsTab() {
|
|
|
963
1420
|
try {
|
|
964
1421
|
const configPayload = {
|
|
965
1422
|
...config,
|
|
1423
|
+
trigger: {
|
|
1424
|
+
...config.trigger,
|
|
1425
|
+
jev: config.trigger.jev ? (({ apiKeyMeta: _meta, ...rest }) => rest)(config.trigger.jev) : defaultJevConfig()
|
|
1426
|
+
},
|
|
966
1427
|
advisors: config.advisors.map(({ apiKeyMetaByProvider: _meta, ...rest }) => rest)
|
|
967
1428
|
};
|
|
968
|
-
const data = await (await
|
|
1429
|
+
const data = await (await authedFetch("/advisor-group/config", {
|
|
969
1430
|
method: "POST",
|
|
970
|
-
headers: {
|
|
971
|
-
"content-type": "application/json",
|
|
972
|
-
...authHeaders()
|
|
973
|
-
},
|
|
1431
|
+
headers: { "content-type": "application/json" },
|
|
974
1432
|
body: JSON.stringify({
|
|
975
1433
|
config: configPayload,
|
|
976
1434
|
expectedRevision: revision
|
|
@@ -1003,11 +1461,43 @@ function AdvisorGroupSettingsTab() {
|
|
|
1003
1461
|
...config,
|
|
1004
1462
|
enabled: e.target.checked
|
|
1005
1463
|
})
|
|
1006
|
-
}), label("启用顾问群")),
|
|
1007
|
-
|
|
1464
|
+
}), label("启用顾问群")), (0, react.createElement)("div", { style: {
|
|
1465
|
+
display: "flex",
|
|
1466
|
+
alignItems: "center",
|
|
1467
|
+
gap: 8,
|
|
1468
|
+
marginTop: 4
|
|
1469
|
+
} }, (0, react.createElement)("input", {
|
|
1470
|
+
type: "checkbox",
|
|
1471
|
+
checked: config.quota.enabled,
|
|
1472
|
+
onChange: (e) => setConfig({
|
|
1473
|
+
...config,
|
|
1474
|
+
quota: {
|
|
1475
|
+
...config.quota,
|
|
1476
|
+
enabled: e.target.checked
|
|
1477
|
+
}
|
|
1478
|
+
})
|
|
1479
|
+
}), label("启用每日咨询上限(成本安全阀)")), (0, react.createElement)("div", { style: { marginTop: 6 } }, label(`每日上限次数(${config.quota.enabled ? "生效中" : "已关闭,不拦截"})`), (0, react.createElement)("input", {
|
|
1480
|
+
type: "number",
|
|
1481
|
+
min: 1,
|
|
1482
|
+
max: 1e5,
|
|
1483
|
+
value: String(config.quota.maxPerDay),
|
|
1484
|
+
disabled: !config.quota.enabled,
|
|
1485
|
+
onChange: (e) => setConfig({
|
|
1486
|
+
...config,
|
|
1487
|
+
quota: {
|
|
1488
|
+
...config.quota,
|
|
1489
|
+
maxPerDay: Math.max(1, Number(e.target.value))
|
|
1490
|
+
}
|
|
1491
|
+
}),
|
|
1492
|
+
style: {
|
|
1493
|
+
...settingsInputStyle,
|
|
1494
|
+
width: 120,
|
|
1495
|
+
marginTop: 4
|
|
1496
|
+
}
|
|
1497
|
+
}))), (0, react.createElement)("div", { style: settingsSectionStyle }, dailyGuard ? (0, react.createElement)("div", { style: {
|
|
1008
1498
|
fontSize: 12,
|
|
1009
|
-
color: dailyGuard.remaining > 0 ? "var(--dsh-color-muted, #8b90a0)" : "#e11d48"
|
|
1010
|
-
} }, dailyGuard.remaining > 0 ? `今日剩余咨询次数:${dailyGuard.remaining} / ${dailyGuard.limit}(UTC 日切)` : `今日咨询次数已用尽(${dailyGuard.used} / ${dailyGuard.limit}),将于 UTC 日切后恢复。`) : null), (0, react.createElement)("div", { style: settingsSectionStyle }, (0, react.createElement)("div", { style: {
|
|
1499
|
+
color: !dailyGuard.enabled || dailyGuard.remaining > 0 ? "var(--dsh-color-muted, #8b90a0)" : "#e11d48"
|
|
1500
|
+
} }, dailyGuard.enabled ? dailyGuard.remaining > 0 ? `今日剩余咨询次数:${dailyGuard.remaining} / ${dailyGuard.limit}(UTC 日切)` : `今日咨询次数已用尽(${dailyGuard.used} / ${dailyGuard.limit}),将于 UTC 日切后恢复。` : `今日已咨询 ${dailyGuard.used} 次(每日上限已关闭)。`) : null), (0, react.createElement)("div", { style: settingsSectionStyle }, (0, react.createElement)("div", { style: {
|
|
1011
1501
|
display: "grid",
|
|
1012
1502
|
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
1013
1503
|
gap: 8
|
|
@@ -1040,7 +1530,7 @@ function AdvisorGroupSettingsTab() {
|
|
|
1040
1530
|
min: 1e3,
|
|
1041
1531
|
max: 6e5,
|
|
1042
1532
|
step: 1e3,
|
|
1043
|
-
value: String(config.discussion.advisorTimeoutMs ??
|
|
1533
|
+
value: String(config.discussion.advisorTimeoutMs ?? 6e5),
|
|
1044
1534
|
onChange: (e) => setConfig({
|
|
1045
1535
|
...config,
|
|
1046
1536
|
discussion: {
|
|
@@ -1049,6 +1539,20 @@ function AdvisorGroupSettingsTab() {
|
|
|
1049
1539
|
}
|
|
1050
1540
|
}),
|
|
1051
1541
|
style: settingsInputStyle
|
|
1542
|
+
})), (0, react.createElement)("div", null, label("驱动模型生成超时(毫秒)"), (0, react.createElement)("input", {
|
|
1543
|
+
type: "number",
|
|
1544
|
+
min: 1e3,
|
|
1545
|
+
max: 12e5,
|
|
1546
|
+
step: 1e3,
|
|
1547
|
+
value: String(config.discussion.driverTimeoutMs ?? 6e5),
|
|
1548
|
+
onChange: (e) => setConfig({
|
|
1549
|
+
...config,
|
|
1550
|
+
discussion: {
|
|
1551
|
+
...config.discussion,
|
|
1552
|
+
driverTimeoutMs: Math.max(1e3, Number(e.target.value))
|
|
1553
|
+
}
|
|
1554
|
+
}),
|
|
1555
|
+
style: settingsInputStyle
|
|
1052
1556
|
}))), (0, react.createElement)("div", { style: { marginTop: 8 } }, label("置信度阈值(0-1,低于此值升级顾问群)"), (0, react.createElement)("input", {
|
|
1053
1557
|
type: "number",
|
|
1054
1558
|
min: 0,
|
|
@@ -1063,7 +1567,188 @@ function AdvisorGroupSettingsTab() {
|
|
|
1063
1567
|
}
|
|
1064
1568
|
}),
|
|
1065
1569
|
style: settingsInputStyle
|
|
1066
|
-
}))), (0, react.createElement)("div", { style: settingsSectionStyle }, (0, react.createElement)("div", { style: {
|
|
1570
|
+
}))), (0, react.createElement)("div", { style: settingsSectionStyle }, (0, react.createElement)("strong", null, "Jev 前置分类"), (0, react.createElement)("div", { style: {
|
|
1571
|
+
fontSize: 12,
|
|
1572
|
+
color: "var(--dsh-color-muted, #8b90a0)",
|
|
1573
|
+
margin: "4px 0 8px"
|
|
1574
|
+
} }, "启用后由 Jev 判断是否需要顾问群;未配置或调用失败时自动使用本地分类器。"), (0, react.createElement)("div", { style: {
|
|
1575
|
+
display: "flex",
|
|
1576
|
+
alignItems: "center",
|
|
1577
|
+
gap: 8,
|
|
1578
|
+
marginBottom: 8
|
|
1579
|
+
} }, (0, react.createElement)("input", {
|
|
1580
|
+
type: "checkbox",
|
|
1581
|
+
checked: config.trigger.jev?.enabled ?? false,
|
|
1582
|
+
onChange: (e) => setConfig({
|
|
1583
|
+
...config,
|
|
1584
|
+
trigger: {
|
|
1585
|
+
...config.trigger,
|
|
1586
|
+
jev: {
|
|
1587
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1588
|
+
enabled: e.target.checked
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
})
|
|
1592
|
+
}), label("启用 Jev")), (0, react.createElement)("div", { style: {
|
|
1593
|
+
display: "grid",
|
|
1594
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
1595
|
+
gap: 8
|
|
1596
|
+
} }, (0, react.createElement)("div", null, label("Jev 渠道"), (0, react.createElement)("select", {
|
|
1597
|
+
value: config.trigger.jev?.provider ?? "typesafe",
|
|
1598
|
+
onChange: (e) => {
|
|
1599
|
+
const current = config.trigger.jev ?? defaultJevConfig();
|
|
1600
|
+
const previousProvider = current.provider ?? "typesafe";
|
|
1601
|
+
const currentURL = current.baseURL ?? "";
|
|
1602
|
+
const usesDefault = !currentURL || currentURL === JEV_DEFAULT_ENDPOINTS[previousProvider];
|
|
1603
|
+
setConfig({
|
|
1604
|
+
...config,
|
|
1605
|
+
trigger: {
|
|
1606
|
+
...config.trigger,
|
|
1607
|
+
jev: {
|
|
1608
|
+
...current,
|
|
1609
|
+
provider: e.target.value,
|
|
1610
|
+
...usesDefault ? { baseURL: JEV_DEFAULT_ENDPOINTS[e.target.value] } : {}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
});
|
|
1614
|
+
},
|
|
1615
|
+
style: settingsInputStyle
|
|
1616
|
+
}, (0, react.createElement)("option", { value: "typesafe" }, "TypeSafe 官方 API"), (0, react.createElement)("option", { value: "openrouter" }, "OpenRouter Decisions API"))), (0, react.createElement)("div", null, label("Jev 模型"), (0, react.createElement)("input", {
|
|
1617
|
+
value: config.trigger.jev?.model ?? "jev-latest",
|
|
1618
|
+
onChange: (e) => setConfig({
|
|
1619
|
+
...config,
|
|
1620
|
+
trigger: {
|
|
1621
|
+
...config.trigger,
|
|
1622
|
+
jev: {
|
|
1623
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1624
|
+
model: e.target.value
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}),
|
|
1628
|
+
style: settingsInputStyle
|
|
1629
|
+
})), (0, react.createElement)("div", null, label("判断阈值(0-1)"), (0, react.createElement)("input", {
|
|
1630
|
+
type: "number",
|
|
1631
|
+
min: 0,
|
|
1632
|
+
max: 1,
|
|
1633
|
+
step: .05,
|
|
1634
|
+
value: String(config.trigger.jev?.confidenceThreshold ?? .6),
|
|
1635
|
+
onChange: (e) => setConfig({
|
|
1636
|
+
...config,
|
|
1637
|
+
trigger: {
|
|
1638
|
+
...config.trigger,
|
|
1639
|
+
jev: {
|
|
1640
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1641
|
+
confidenceThreshold: Number(e.target.value)
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}),
|
|
1645
|
+
style: settingsInputStyle
|
|
1646
|
+
})), (0, react.createElement)("div", null, label("高风险阈值(0-1,空=跟随判断阈值)"), (0, react.createElement)("input", {
|
|
1647
|
+
type: "number",
|
|
1648
|
+
min: 0,
|
|
1649
|
+
max: 1,
|
|
1650
|
+
step: .05,
|
|
1651
|
+
placeholder: String(config.trigger.jev?.confidenceThreshold ?? .6),
|
|
1652
|
+
value: config.trigger.jev?.highRiskThreshold !== void 0 ? String(config.trigger.jev.highRiskThreshold) : "",
|
|
1653
|
+
onChange: (e) => setConfig({
|
|
1654
|
+
...config,
|
|
1655
|
+
trigger: {
|
|
1656
|
+
...config.trigger,
|
|
1657
|
+
jev: {
|
|
1658
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1659
|
+
highRiskThreshold: e.target.value === "" ? void 0 : Number(e.target.value)
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
}),
|
|
1663
|
+
style: settingsInputStyle
|
|
1664
|
+
})), (0, react.createElement)("div", null, label("请求超时(毫秒)"), (0, react.createElement)("input", {
|
|
1665
|
+
type: "number",
|
|
1666
|
+
min: 1e3,
|
|
1667
|
+
max: 6e4,
|
|
1668
|
+
step: 1e3,
|
|
1669
|
+
value: String(config.trigger.jev?.timeoutMs ?? 1e4),
|
|
1670
|
+
onChange: (e) => setConfig({
|
|
1671
|
+
...config,
|
|
1672
|
+
trigger: {
|
|
1673
|
+
...config.trigger,
|
|
1674
|
+
jev: {
|
|
1675
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1676
|
+
timeoutMs: Math.max(1e3, Number(e.target.value))
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}),
|
|
1680
|
+
style: settingsInputStyle
|
|
1681
|
+
}))), (0, react.createElement)("div", { style: { marginTop: 8 } }, label("API 地址(留空使用渠道默认地址)"), (0, react.createElement)("input", {
|
|
1682
|
+
value: config.trigger.jev?.baseURL || JEV_DEFAULT_ENDPOINTS[config.trigger.jev?.provider ?? "typesafe"],
|
|
1683
|
+
placeholder: JEV_DEFAULT_ENDPOINTS[config.trigger.jev?.provider ?? "typesafe"],
|
|
1684
|
+
onChange: (e) => setConfig({
|
|
1685
|
+
...config,
|
|
1686
|
+
trigger: {
|
|
1687
|
+
...config.trigger,
|
|
1688
|
+
jev: {
|
|
1689
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1690
|
+
baseURL: e.target.value
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
}),
|
|
1694
|
+
style: settingsInputStyle
|
|
1695
|
+
})), (0, react.createElement)("div", { style: {
|
|
1696
|
+
display: "grid",
|
|
1697
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))",
|
|
1698
|
+
gap: 8,
|
|
1699
|
+
marginTop: 8
|
|
1700
|
+
} }, (0, react.createElement)("div", null, label("Jev API Key"), (0, react.createElement)("input", {
|
|
1701
|
+
type: "password",
|
|
1702
|
+
placeholder: config.trigger.jev?.apiKeyMeta?.configured ? `已配置 ••••${config.trigger.jev.apiKeyMeta.last4 ?? ""}` : "输入 API Key",
|
|
1703
|
+
value: config.trigger.jev?.apiKey ?? "",
|
|
1704
|
+
onChange: (e) => setConfig({
|
|
1705
|
+
...config,
|
|
1706
|
+
trigger: {
|
|
1707
|
+
...config.trigger,
|
|
1708
|
+
jev: {
|
|
1709
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1710
|
+
apiKey: e.target.value
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}),
|
|
1714
|
+
style: settingsInputStyle
|
|
1715
|
+
})), (0, react.createElement)("div", null, label("API Key 环境变量名(可选)"), (0, react.createElement)("input", {
|
|
1716
|
+
placeholder: "TYPESAFE_API_KEY 或 OPENROUTER_API_KEY",
|
|
1717
|
+
value: config.trigger.jev?.apiKeyEnv ?? "",
|
|
1718
|
+
onChange: (e) => setConfig({
|
|
1719
|
+
...config,
|
|
1720
|
+
trigger: {
|
|
1721
|
+
...config.trigger,
|
|
1722
|
+
jev: {
|
|
1723
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1724
|
+
apiKeyEnv: e.target.value
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}),
|
|
1728
|
+
style: settingsInputStyle
|
|
1729
|
+
}))), (0, react.createElement)("div", { style: {
|
|
1730
|
+
display: "flex",
|
|
1731
|
+
alignItems: "center",
|
|
1732
|
+
gap: 8,
|
|
1733
|
+
marginTop: 8
|
|
1734
|
+
} }, (0, react.createElement)("input", {
|
|
1735
|
+
type: "checkbox",
|
|
1736
|
+
checked: config.trigger.jev?.useEnglishState ?? false,
|
|
1737
|
+
onChange: (e) => setConfig({
|
|
1738
|
+
...config,
|
|
1739
|
+
trigger: {
|
|
1740
|
+
...config.trigger,
|
|
1741
|
+
jev: {
|
|
1742
|
+
...config.trigger.jev ?? defaultJevConfig(),
|
|
1743
|
+
useEnglishState: e.target.checked
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
})
|
|
1747
|
+
}), label("Jev 英文判定(questionEn 概要)")), (0, react.createElement)("div", { style: {
|
|
1748
|
+
fontSize: 12,
|
|
1749
|
+
color: "var(--dsh-color-muted, #8b90a0)",
|
|
1750
|
+
margin: "4px 0 0"
|
|
1751
|
+
} }, "开启后,主模型提供的英文概要(questionEn)会作为 Jev 的判定输入;展示问句、会话记录与重复检测仍用中文。默认关闭。")), (0, react.createElement)("div", { style: settingsSectionStyle }, (0, react.createElement)("div", { style: {
|
|
1067
1752
|
display: "flex",
|
|
1068
1753
|
alignItems: "center",
|
|
1069
1754
|
gap: 8
|
|
@@ -1364,7 +2049,55 @@ function AdvisorGroupSettingsTab() {
|
|
|
1364
2049
|
marginTop: 4,
|
|
1365
2050
|
resize: "vertical"
|
|
1366
2051
|
}
|
|
1367
|
-
}), (
|
|
2052
|
+
}), (() => {
|
|
2053
|
+
const supportsTools = !providers.some((p) => p.id === advisor.provider && p.kind === "llm") || Boolean(advisor.baseURL || advisor.apiKeyEnv);
|
|
2054
|
+
const globalDefault = config.discussion.advisorTools ?? "readonly";
|
|
2055
|
+
const mode = advisor.tools ?? globalDefault;
|
|
2056
|
+
const modeLabel = {
|
|
2057
|
+
readonly: "只读工具",
|
|
2058
|
+
all: "全部会话工具(含可写,慎用)",
|
|
2059
|
+
off: "关闭"
|
|
2060
|
+
};
|
|
2061
|
+
const toolPromptNote = "工具启用时,系统提示词会自动追加:『对于你不了解、不熟悉的内容,尤其是项目背景、代码细节、仓库状态等不在知识范围内的情况,请优先使用联网工具(如 web_search、web_fetch)搜索核实后再作答,不要编造未核实的事实。』";
|
|
2062
|
+
if (supportsTools) return [
|
|
2063
|
+
label(`顾问工具调用范围(未单独设置时跟随全局默认:${modeLabel[globalDefault] ?? globalDefault})`),
|
|
2064
|
+
(0, react.createElement)("select", {
|
|
2065
|
+
"aria-label": "顾问工具调用范围",
|
|
2066
|
+
value: mode,
|
|
2067
|
+
onChange: (e) => updateAdvisor(index, { tools: e.target.value }),
|
|
2068
|
+
style: settingsInputStyle
|
|
2069
|
+
}, (0, react.createElement)("option", { value: "readonly" }, "只读工具"), (0, react.createElement)("option", { value: "all" }, "全部会话工具(含可写,慎用)"), (0, react.createElement)("option", { value: "off" }, "关闭")),
|
|
2070
|
+
mode === "off" ? null : (0, react.createElement)("div", { style: {
|
|
2071
|
+
fontSize: 12,
|
|
2072
|
+
color: "var(--dsh-color-muted, #8b90a0)",
|
|
2073
|
+
border: "1px dashed var(--dsh-color-border, #3a3f4b)",
|
|
2074
|
+
borderRadius: 4,
|
|
2075
|
+
padding: "4px 6px",
|
|
2076
|
+
marginTop: 4
|
|
2077
|
+
} }, toolPromptNote)
|
|
2078
|
+
];
|
|
2079
|
+
return [label("顾问工具调用范围"), (0, react.createElement)("div", { style: {
|
|
2080
|
+
fontSize: 12,
|
|
2081
|
+
color: "var(--dsh-color-muted, #8b90a0)",
|
|
2082
|
+
border: "1px solid var(--dsh-color-border, #3a3f4b)",
|
|
2083
|
+
borderRadius: 4,
|
|
2084
|
+
padding: "4px 6px"
|
|
2085
|
+
} }, "❌ 当前为 DSH 内置通道(复用 DSH 凭据),不支持工具调用;为其配置 API 地址 / Key / 环境变量以走直连通道后即可启用(工具指引提示词也会随之生效)。")];
|
|
2086
|
+
})(), label("思考模式 / 思考强度(未设置 = 跟随模型与供应商默认)"), (0, react.createElement)("div", { style: {
|
|
2087
|
+
display: "flex",
|
|
2088
|
+
gap: 8,
|
|
2089
|
+
marginTop: 4
|
|
2090
|
+
} }, (0, react.createElement)("select", {
|
|
2091
|
+
"aria-label": "思考模式",
|
|
2092
|
+
value: advisor.thinking ?? "",
|
|
2093
|
+
onChange: (e) => updateAdvisor(index, { thinking: e.target.value === "" ? void 0 : e.target.value }),
|
|
2094
|
+
style: settingsInputStyle
|
|
2095
|
+
}, (0, react.createElement)("option", { value: "" }, "思考:默认"), (0, react.createElement)("option", { value: "enabled" }, "思考:开启"), (0, react.createElement)("option", { value: "disabled" }, "思考:关闭")), (0, react.createElement)("select", {
|
|
2096
|
+
"aria-label": "思考强度",
|
|
2097
|
+
value: advisor.reasoningEffort ?? "",
|
|
2098
|
+
onChange: (e) => updateAdvisor(index, { reasoningEffort: e.target.value === "" ? void 0 : e.target.value }),
|
|
2099
|
+
style: settingsInputStyle
|
|
2100
|
+
}, (0, react.createElement)("option", { value: "" }, "强度:默认"), (0, react.createElement)("option", { value: "off" }, "off(不思考)"), (0, react.createElement)("option", { value: "low" }, "low(低)"), (0, react.createElement)("option", { value: "high" }, "high(高)"), (0, react.createElement)("option", { value: "max" }, "max(最大)"))), (0, react.createElement)("div", { style: {
|
|
1368
2101
|
display: "flex",
|
|
1369
2102
|
gap: 8,
|
|
1370
2103
|
marginTop: 4
|
|
@@ -1430,15 +2163,21 @@ function apply(ctx) {
|
|
|
1430
2163
|
key: "advisor-group",
|
|
1431
2164
|
locale: "chat"
|
|
1432
2165
|
}, AdvisorGroupNodeView));
|
|
1433
|
-
ctx.slots.inject("settings.
|
|
1434
|
-
name: "settings.
|
|
1435
|
-
|
|
2166
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
2167
|
+
name: "settings.section",
|
|
2168
|
+
id: "dsh-advisor-group",
|
|
2169
|
+
order: 0,
|
|
2170
|
+
label: "顾问群"
|
|
1436
2171
|
}, AdvisorGroupSettingsTab));
|
|
1437
2172
|
}
|
|
1438
2173
|
|
|
1439
2174
|
//#endregion
|
|
1440
2175
|
exports.advisorGroupDefinition = advisorGroupDefinition;
|
|
1441
2176
|
exports.apply = apply;
|
|
2177
|
+
exports.buildStepSequence = buildStepSequence;
|
|
2178
|
+
exports.buildToolStepViews = buildToolStepViews;
|
|
2179
|
+
exports.extractActionDescription = extractActionDescription;
|
|
1442
2180
|
exports.inject = inject;
|
|
1443
2181
|
exports.name = name;
|
|
2182
|
+
exports.sanitizeClientContent = sanitizeClientContent;
|
|
1444
2183
|
return module.exports; } });
|