diff --git a/frontends/conductor.html b/frontends/conductor.html
index 5380d5c8..a8155f60 100644
--- a/frontends/conductor.html
+++ b/frontends/conductor.html
@@ -76,6 +76,7 @@
}
.title { font-size: 17px; letter-spacing: .2px; font-weight: 700; }
.hint { color: var(--muted); font-size: 12px; }
+ #llm { max-width: 42vw; margin-left: 10px; padding: 4px 6px; border: 1px solid #dfe5ee; border-radius: 6px; background: #fff; font: 600 12px inherit; }
.cards {
flex: 1;
@@ -312,7 +313,7 @@
-
Conversation
+
Conversation
connecting...
@@ -331,6 +332,7 @@
const messagesEl = document.getElementById('messages');
const inputEl = document.getElementById('input');
const sendBtn = document.getElementById('send');
+ const llmEl = document.getElementById('llm');
const wsStatus = document.getElementById('wsStatus');
const apprEl = document.getElementById('approvals');
const apprBody = document.getElementById('approvalsBody');
@@ -452,6 +454,7 @@
renderCards(data.subagents||[]);
messagesEl.innerHTML=''; (data.chat||[]).forEach(addMsg);
renderLog(data.log||[]);
+ llmEl.innerHTML=(data.llms||[]).map(x=>``).join(''); llmEl.value=data.llm; llmEl.disabled=!llmEl.options.length;
} else if(data.type==='subagents'){ renderCards(data.items||[]); }
else if(data.type==='chat'){ addMsg(data.item); }
else if(data.type==='chat_read'){ markAllRead(); }
@@ -465,6 +468,7 @@
ws.send(JSON.stringify({msg})); inputEl.value=''; inputEl.focus();
}
sendBtn.addEventListener('click', send);
+ llmEl.addEventListener('change', ()=>{ if(ws?.readyState===1) ws.send(JSON.stringify({llm:+llmEl.value})); });
inputEl.addEventListener('keydown', e=>{ if(e.key==='Enter' && !e.shiftKey && !e.isComposing && e.keyCode!==229){ e.preventDefault(); send(); } });
// Input length limit (10000 chars)
diff --git a/frontends/conductor.py b/frontends/conductor.py
index 4086d918..cce8a8af 100644
--- a/frontends/conductor.py
+++ b/frontends/conductor.py
@@ -414,7 +414,6 @@ API: {base};requests,GET /readme查用法,GET /chat读未读对话,GET /
return
def _run(self):
- self.agent = GenericAgent()
self.agent.inc_out = True
start_agent_runner(self.agent, "conductor-agent")
self.started = True
@@ -431,18 +430,19 @@ API: {base};requests,GET /readme查用法,GET /chat读未读对话,GET /
self.inbox.task_done()
except Exception:
break
+ kind = first.get("type"); others = [e for e in events if e.get("type") != kind]; events = [e for e in events if e.get("type") == kind] or [first]
+ for e in others: self.inbox.put(e)
try:
prompt = self._build_prompt(events)
- # Follow the desktop-selected model live: re-read before each task
- # so switching models in the UI takes effect without restarting.
- _apply_desktop_model(self.agent)
dq = self.agent.put_task(prompt, source="conductor")
self._drain(dq, events)
except Exception as e:
print(f"Conductor error: {e}")
add_chat(f"⚠ 回复失败:{e}", role="error")
- def start(self): threading.Thread(target=self._run, name="conductor-loop", daemon=True).start()
+ def start(self):
+ self.agent = GenericAgent(); _apply_desktop_model(self.agent)
+ threading.Thread(target=self._run, name="conductor-loop", daemon=True).start()
conductor = Conductor()
@@ -573,9 +573,12 @@ async def websocket(ws: WebSocket):
ws_clients.add(ws)
try:
running = any(s.status == "running" for s in pool.subagents.values())
- await ws.send_json({"type": "hello", "subagents": pool.snapshot(), "chat": chat_messages, "log": conductor.log, "running": running})
+ await ws.send_json({"type": "hello", "subagents": pool.snapshot(), "chat": chat_messages, "log": conductor.log, "running": running, "llms": conductor.agent.list_llms(), "llm": conductor.agent.llm_no})
while True:
data = await ws.receive_json()
+ if "llm" in data:
+ if type(n := data["llm"]) is int and 0 <= n < len(conductor.agent.list_llms()): conductor.agent.next_llm(n)
+ continue
msg = (data.get("msg") or "").strip()
if not msg: continue
add_chat(msg, role="user", files=data.get("files") or [], images=data.get("images") or [])
diff --git a/frontends/desktop/static/app.js b/frontends/desktop/static/app.js
index 23eb4f07..f3b674e0 100644
--- a/frontends/desktop/static/app.js
+++ b/frontends/desktop/static/app.js
@@ -775,7 +775,17 @@ if (pqEl) pqEl.addEventListener('click', (e) => {
const btn = e.target.closest('.pq-btn[data-provider]');
if (!btn) return;
e.preventDefault(); e.stopPropagation();
- if (btn.dataset.provider === 'ga-token') { bridgeFetch('/subscription-portal', { method: 'POST', body: {} }); return; }
+ if (btn.dataset.provider === 'ga-token') {
+ window.ga.getMykeyContent().then(r => {
+ const base = r?.content || '', t0 = Date.now();
+ bridgeFetch('/subscription-portal', { method: 'POST', body: {} });
+ const timer = setInterval(async () => {
+ const cur = (await window.ga.getMykeyContent().catch(() => null))?.content;
+ if ((cur != null && cur !== base) || Date.now() - t0 > 3e5) { clearInterval(timer); if (cur !== base) await loadModelProfiles(); }
+ }, 3000);
+ });
+ return;
+ }
openAddModelFormForProvider(btn.dataset.provider);
});
// 「快速接入」卡片折叠/展开(向下箭头),状态记忆到 localStorage
diff --git a/frontends/desktop/static/index.html b/frontends/desktop/static/index.html
index bfc98436..385a116b 100644
--- a/frontends/desktop/static/index.html
+++ b/frontends/desktop/static/index.html
@@ -696,6 +696,6 @@
-
+