aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/assistant/Assistant.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/assistant/Assistant.qml')
-rw-r--r--desktop/assistant/Assistant.qml46
1 files changed, 42 insertions, 4 deletions
diff --git a/desktop/assistant/Assistant.qml b/desktop/assistant/Assistant.qml
index 2641a5e..0b7026d 100644
--- a/desktop/assistant/Assistant.qml
+++ b/desktop/assistant/Assistant.qml
@@ -66,6 +66,18 @@ Scope {
// The preferred model being loaded on open, shown in the header until
// the router reports it loaded. Set from shell.qml.
property string swapping: ""
+ // The model this chat is asked with: its stored pick (hello.chat_model,
+ // conversation.model), "" for none, then the preferred one (set from
+ // shell.qml). "" for both: the daemon uses whatever the router has
+ // loaded. Spec: desktop-assistant,
+ // docs/superpowers/specs/2026-10-23-cloud-models-design.md.
+ property string chatModel: ""
+ property string preferred: ""
+ readonly property string wanted: root.chatModel || root.preferred
+ // The daemon's models event: [{id, status, cloud: false} | {id, provider, cloud: true}].
+ property var models: []
+ readonly property bool cloud: root.models.some(m => m.cloud && m.id === root.wanted)
+ property bool modelsOpen: false
property int ctxUsed: 0
property int ctxMax: 0
property var convId: null
@@ -113,11 +125,12 @@ Scope {
function close() {
root.endRecording(false);
- root.open = false; root.historyOpen = false; root.pickerOpen = false;
+ root.open = false; root.historyOpen = false; root.pickerOpen = false; root.modelsOpen = false;
}
// Escape: a recording, the picker, then the history panel, then the card.
function back() {
if (root.recording) root.endRecording(false);
+ else if (root.modelsOpen) root.modelsOpen = false;
else if (root.pickerOpen) root.pickerOpen = false;
else if (root.historyOpen) root.historyOpen = false;
else root.close();
@@ -258,8 +271,8 @@ Scope {
root.notice = "recording failed";
} else if (rec.send && root.connected) {
root.chatting = true;
- root.send({ type: "ask", text: "", audio: root.micPath,
- files: root.staged.map(f => f.path) });
+ root.send(root.withModel({ type: "ask", text: "", audio: root.micPath,
+ files: root.staged.map(f => f.path) }));
}
rec.destroy();
}
@@ -325,16 +338,37 @@ Scope {
|| (text.trim() === "" && !root.staged.length)) return false;
root.notice = "";
root.chatting = true;
- root.send({ type: "ask", text: text, files: root.staged.map(f => f.path) });
+ root.send(root.withModel({ type: "ask", text: text, files: root.staged.map(f => f.path) }));
return true;
}
function showHistory() {
root.pickerOpen = false;
+ root.modelsOpen = false;
root.historyOpen = true;
root.send({ type: "list" });
}
+ function showModels() {
+ root.historyOpen = root.pickerOpen = false;
+ root.modelsOpen = true;
+ root.send({ type: "models" });
+ }
+ // Applies from the next ask; the daemon stores it with the chat then.
+ // Moving a chat with turns to the cloud sends its history there, so
+ // that is said once, on the switch.
+ function pickModel(id) {
+ const m = root.models.find(x => x.id === id);
+ if (m && m.cloud && !root.cloud && turns.count)
+ root.notice = `History in this chat goes to ${m.provider}.`;
+ root.chatModel = id;
+ root.modelsOpen = false;
+ }
+ function withModel(cmd) {
+ if (root.wanted) cmd.model = root.wanted;
+ return cmd;
+ }
+
function row(t, live) {
return { user: t.user ?? "", reply: t.reply ?? "", think: t.think ?? "",
tools: (t.tools ?? []).join(","), error: "", live: live,
@@ -371,15 +405,18 @@ Scope {
root.state = e.state;
root.runningTool = e.state === "tool" ? e.tool : "";
root.model = e.model ?? "";
+ root.chatModel = e.chat_model ?? "";
root.ctxUsed = e.ctx_used;
root.ctxMax = e.ctx_max;
root.load(e.id, e.turns, e.pending);
// A restart or reload counts as active: the conversation it
// reconnects to is not stale just because this process is new.
if (e.pending || e.turns.length) root.lastActive = Date.now();
+ root.send({ type: "models" });
break;
case "conversation":
root.load(e.id, e.turns, null);
+ root.chatModel = e.model ?? "";
// Opened from History on purpose: it counts as active now.
if (e.turns.length) root.lastActive = Date.now();
// Emptied (New, or the current one deleted) with nothing staged:
@@ -445,6 +482,7 @@ Scope {
case "ctx": root.ctxUsed = e.used; root.ctxMax = e.max; if (e.model) root.model = e.model; break;
case "conversations": root.convs = e.items; break;
case "results": root.results = e.items; break;
+ case "models": root.models = e.items; break;
}
}