模型下拉框
This commit is contained in:
1 parent
6addf507cb
commit
b91a45639f
5 files changed
+121
-32
No files matched your search
@@ -26,7 +26,13 @@
|
||||
* 7) **Persona 抽屉**改的 model 不会立即重启编排,下一轮该席位被派活时由 `runOrchestration`
|
||||
* 读 `agentConfigs[meta.speakerId]?.model` 传入 special。prompt / temp 当前**仅前端记录**。
|
||||
*
|
||||
* 8) **常量**:`DEFAULT_MODEL`、`MAX_CYCLES=8`、`COMMANDER_SEAT_ID=1`、`INTENT_READY_RE`。
|
||||
* 8) **模型选择**:不再硬编码 "deepseek-chat"。全局选中 model 由 `selectedModel`
|
||||
* state 维护(首次 `useModels()` 完成后初始化为 `availableModels[0].name`),
|
||||
* 顶栏「模型」下拉可切换;Persona 抽屉的 per-seat 覆盖更高优先级。所有 API 调用
|
||||
* 传 `selectedModel || undefined`,后端缺省由 `lead_agent._resolve_model_name()`
|
||||
* 回退到 `config.yaml` 的 `models[0]`。内网部署只需配 `config.yaml` 即可。
|
||||
*
|
||||
* 9) **常量**:`MAX_CYCLES=8`、`COMMANDER_SEAT_ID=1`、`INTENT_READY_RE`。
|
||||
*
|
||||
* 调试开关(默认开启):
|
||||
* window.__MULTI_AGENT_INIT_TIMING__ // /api/multi-agent/init 各阶段耗时
|
||||
@@ -189,7 +195,13 @@ function getAvatar(type: string, size: string = "h-8 w-8") {
|
||||
}
|
||||
|
||||
// --- Multi-agent backend wiring ---
|
||||
const DEFAULT_MODEL = "deepseek-chat";
|
||||
//
|
||||
// 模型选择策略(与 /page/canvas/ai-writing 对齐,不再写死 "deepseek-chat"):
|
||||
// - 全局选中模型由 selectedModel state 维护(首次 useModels 完成后初始化为 models[0].name)
|
||||
// - 所有 API 调用传 model 时统一用 selectedModel || undefined;后端缺省由
|
||||
// lead_agent._resolve_model_name() 回退到 config.yaml 的 models[0]
|
||||
// - Persona 抽屉的 per-seat 覆盖优先(agentConfigs[seat]?.model > selectedModel)
|
||||
// 内网部署只需在 config.yaml 配自家模型,前后端都不需要再硬编码具体模型名。
|
||||
|
||||
type RoleEntry = {
|
||||
id: number;
|
||||
@@ -373,16 +385,24 @@ export default function RoundtablePlanningPage() {
|
||||
// for each of them concurrently instead of only the most recently set one.
|
||||
const [activeSpeakerSet, setActiveSpeakerSet] = useState<Set<number>>(new Set());
|
||||
|
||||
// Custom presets for agent model, weight, temperature and prompt editors
|
||||
// Custom presets for agent model, weight, temperature and prompt editors.
|
||||
// model 字段留空,运行时由 runOrchestration 解析为
|
||||
// agentConfigs[seat]?.model || selectedModel || undefined
|
||||
// 即「席位覆盖优先,否则跟随全局选中模型,否则让后端回退到 config.yaml models[0]」。
|
||||
const [agentConfigs, setAgentConfigs] = useState<{ [key: number]: { model: string; weight: number; prompt: string; temp: number } }>({
|
||||
2: { model: DEFAULT_MODEL, weight: 90, temp: 0.2, prompt: "你负责解析核心任务、资源极限和用户目标偏好,监督全局研商收敛。" },
|
||||
3: { model: DEFAULT_MODEL, weight: 85, temp: 0.3, prompt: "你负责搜集电力、通信系统、历史极端部署条件情报,支持备灾架构体系。" },
|
||||
4: { model: DEFAULT_MODEL, weight: 88, temp: 0.1, prompt: "你评估服务器机房热载荷、算力并发承载极限,预报运行瓶颈与风险时钟。" },
|
||||
5: { model: DEFAULT_MODEL, weight: 92, temp: 0.4, prompt: "你负责双全栈主备建模设计,高内聚低耦合,规避不可预知停机崩溃。" },
|
||||
6: { model: DEFAULT_MODEL, weight: 95, temp: 0.1, prompt: "你作为安全主管,严厉把控预算开销与合规通过率防线,识别计划漏洞。" },
|
||||
7: { model: DEFAULT_MODEL, weight: 80, temp: 0.4, prompt: "你负责对72小时里程碑执行路径规划、重排和资源调配演练序列。" },
|
||||
2: { model: "", weight: 90, temp: 0.2, prompt: "你负责解析核心任务、资源极限和用户目标偏好,监督全局研商收敛。" },
|
||||
3: { model: "", weight: 85, temp: 0.3, prompt: "你负责搜集电力、通信系统、历史极端部署条件情报,支持备灾架构体系。" },
|
||||
4: { model: "", weight: 88, temp: 0.1, prompt: "你评估服务器机房热载荷、算力并发承载极限,预报运行瓶颈与风险时钟。" },
|
||||
5: { model: "", weight: 92, temp: 0.4, prompt: "你负责双全栈主备建模设计,高内聚低耦合,规避不可预知停机崩溃。" },
|
||||
6: { model: "", weight: 95, temp: 0.1, prompt: "你作为安全主管,严厉把控预算开销与合规通过率防线,识别计划漏洞。" },
|
||||
7: { model: "", weight: 80, temp: 0.4, prompt: "你负责对72小时里程碑执行路径规划、重排和资源调配演练序列。" },
|
||||
});
|
||||
|
||||
// 全局选中模型(对应顶栏「模型」下拉)。
|
||||
// 首次 useModels 加载完成后由下面的 useEffect 初始化为 availableModels[0].name,
|
||||
// 用户可以通过下拉框切换;Persona 抽屉的 per-seat 覆盖更高优先级。
|
||||
const [selectedModel, setSelectedModel] = useState<string>("");
|
||||
|
||||
// Message lists state representation to mimic image items & enable actual conversations
|
||||
const [interactiveMessages, setInteractiveMessages] = useState([
|
||||
{
|
||||
@@ -561,6 +581,17 @@ export default function RoundtablePlanningPage() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 首次 /api/models 返回后,把 selectedModel 初始化为列表第一个 —— 与
|
||||
// AIWritingForm 的行为对齐。若 selectedModel 已经被用户在下拉框里选过,不再覆盖。
|
||||
// 若当前选中的 model 已不在 availableModels 里(后端配置变了),也回退到首项。
|
||||
useEffect(() => {
|
||||
if (availableModels.length === 0) return;
|
||||
const stillExists = availableModels.some((m) => m.name === selectedModel);
|
||||
if (!selectedModel || !stillExists) {
|
||||
setSelectedModel(availableModels[0].name);
|
||||
}
|
||||
}, [availableModels, selectedModel]);
|
||||
|
||||
// Toast auto-dismiss logic
|
||||
useEffect(() => {
|
||||
if (showToast) {
|
||||
@@ -837,7 +868,7 @@ export default function RoundtablePlanningPage() {
|
||||
dAgentThreadId: ids,
|
||||
agentName: coord,
|
||||
newMessage: nextLeaderTask,
|
||||
model: DEFAULT_MODEL,
|
||||
model: selectedModel || undefined,
|
||||
signal: leaderAborter.signal,
|
||||
onTextDelta: (text) => appendToDialogue(leaderBubbleId, text),
|
||||
onPhaseChange: (phase, toolName) => {
|
||||
@@ -918,7 +949,8 @@ export default function RoundtablePlanningPage() {
|
||||
dAgentThreadId: ids,
|
||||
agentName: subName,
|
||||
newMessage: task,
|
||||
model: perSeatModel || DEFAULT_MODEL,
|
||||
// 优先级: 席位 Persona 覆盖 → 全局选中模型 → 让后端回退到 config.yaml models[0]
|
||||
model: perSeatModel || selectedModel || undefined,
|
||||
signal: subAborter.signal,
|
||||
onTextDelta: (text) => appendToDialogue(subBubbleId, text),
|
||||
onPhaseChange: (phase, toolName) => {
|
||||
@@ -1026,7 +1058,7 @@ export default function RoundtablePlanningPage() {
|
||||
const initRes = await initMultiAgent({
|
||||
agentNames: roundtableAgentIds,
|
||||
mainAgentName: coord,
|
||||
model: DEFAULT_MODEL,
|
||||
model: selectedModel || undefined,
|
||||
// As each sub-agent's thread comes online, flip every role that
|
||||
// shares the agentId to "在线". Some seats reuse the same agent
|
||||
// (e.g. seat 8 reuses roundtable-summary) so we match on agentId
|
||||
@@ -1148,7 +1180,9 @@ export default function RoundtablePlanningPage() {
|
||||
const role = roles.find(r => r.id === id);
|
||||
const roleName = role?.name || "专家智能体";
|
||||
const cfg = agentConfigs[id];
|
||||
const targetModel = cfg?.model || DEFAULT_MODEL;
|
||||
// Persona 抽屉里的「保存」反馈文字: 优先显示用户在抽屉里选的 model,
|
||||
// 没选时退回全局 selectedModel,最后才显示「默认」二字。
|
||||
const targetModel = cfg?.model || selectedModel || "默认模型";
|
||||
|
||||
const avatarColor =
|
||||
id === 2 ? "blue"
|
||||
@@ -1206,7 +1240,9 @@ export default function RoundtablePlanningPage() {
|
||||
]);
|
||||
(async () => {
|
||||
try {
|
||||
const res = await initIntent(DEFAULT_MODEL);
|
||||
// initIntent 当前不在 thread 上落盘 model,只为占位与兼容;空时由后端
|
||||
// 走 lead_agent._resolve_model_name 回退到 config.yaml models[0]
|
||||
const res = await initIntent(selectedModel || undefined);
|
||||
if (step1InitGenRef.current !== myGen) return;
|
||||
setIntentThreadId(res.thread_id);
|
||||
setTimelineEntries([
|
||||
@@ -1540,7 +1576,7 @@ export default function RoundtablePlanningPage() {
|
||||
const res = await streamIntent({
|
||||
threadId: intentThreadId,
|
||||
message: trimmed,
|
||||
model: DEFAULT_MODEL,
|
||||
model: selectedModel || undefined,
|
||||
signal: aborter.signal,
|
||||
onTextDelta: (delta) => {
|
||||
setInteractiveMessages(prev =>
|
||||
@@ -1791,7 +1827,7 @@ export default function RoundtablePlanningPage() {
|
||||
<RecommendAgentsDialog
|
||||
open={isRecommendDialogOpen}
|
||||
intent={intentReady}
|
||||
model={DEFAULT_MODEL}
|
||||
model={selectedModel || undefined}
|
||||
onCancel={() => setIsRecommendDialogOpen(false)}
|
||||
onConfirm={handleConfirmAgents}
|
||||
/>
|
||||
@@ -1919,6 +1955,41 @@ export default function RoundtablePlanningPage() {
|
||||
|
||||
{/* Right Actions */}
|
||||
<div className="flex items-center gap-2">
|
||||
{/* 全局模型选择器
|
||||
─────────────────────────────────────────────
|
||||
影响 Step 1 / 推荐 / Step 2 init / 总控 leader / 子席位 special
|
||||
所有 API 调用。Persona 抽屉里 per-seat 设置的 model 优先级更高。
|
||||
选项来自 /api/models(由 config.yaml 配置),内网部署只需配自家模型。 */}
|
||||
<label className="flex items-center gap-1.5 bg-white border border-[#e2e8f0] rounded-xl px-2.5 py-1.5 text-xs font-bold text-[#334155] dark:border-border dark:bg-secondary dark:text-foreground">
|
||||
<Cpu className="w-3.5 h-3.5 text-primary" />
|
||||
<span className="text-[10px] text-slate-400 font-mono">模型</span>
|
||||
<select
|
||||
id="global-model-select"
|
||||
value={selectedModel}
|
||||
onChange={(e) => setSelectedModel(e.target.value)}
|
||||
disabled={modelsLoading || availableModels.length === 0}
|
||||
title={
|
||||
modelsLoading
|
||||
? "正在加载模型列表…"
|
||||
: availableModels.length === 0
|
||||
? "未在 config.yaml 中配置任何模型"
|
||||
: "切换全局推理模型;不影响 Persona 抽屉里已设置的 per-seat 覆盖"
|
||||
}
|
||||
className="bg-transparent border-none outline-none text-xs font-bold text-slate-700 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed dark:text-foreground"
|
||||
>
|
||||
{availableModels.length === 0 && (
|
||||
<option value="">{modelsLoading ? "加载中…" : "未配置模型"}</option>
|
||||
)}
|
||||
{availableModels.map((m) => (
|
||||
<option key={m.name} value={m.name}>
|
||||
{m.display_name && m.display_name !== m.name
|
||||
? `${m.display_name} (${m.name})`
|
||||
: m.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<button
|
||||
id="new-task-btn"
|
||||
onClick={handleStartNewTask}
|
||||
@@ -3091,7 +3162,8 @@ export default function RoundtablePlanningPage() {
|
||||
<div className="space-y-1">
|
||||
<label className="text-[9px] font-black text-slate-500 uppercase tracking-wider block">推理本源模型</label>
|
||||
<select
|
||||
value={agentConfigs[selectedAgentId]?.model || DEFAULT_MODEL}
|
||||
// 抽屉里显示的「当前模型」:Persona 覆盖优先,否则跟随全局 selectedModel
|
||||
value={agentConfigs[selectedAgentId]?.model || selectedModel || ""}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setAgentConfigs(prev => ({
|
||||
@@ -3106,7 +3178,7 @@ export default function RoundtablePlanningPage() {
|
||||
disabled={modelsLoading}
|
||||
>
|
||||
{availableModels.length === 0 && (
|
||||
<option value={DEFAULT_MODEL}>{DEFAULT_MODEL}</option>
|
||||
<option value="">{modelsLoading ? "加载中…" : "未配置模型"}</option>
|
||||
)}
|
||||
{availableModels.map((m) => (
|
||||
<option key={m.name} value={m.name}>
|
||||
|
||||
Reference in new issue
Block a user