aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/assistant
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-23 17:09:58 +0200
committerDanilo M. <danix@danix.xyz>2026-10-23 17:09:58 +0200
commite7de6f62f438acfa723961000c558b4d9f9da35f (patch)
tree16fece6b35aedc628ede244367b7af38621f278b /desktop/modules/assistant
parent4f133813fd0f677d5a4f9cac496bd3d3738cd276 (diff)
downloadquickshell-e7de6f62f438acfa723961000c558b4d9f9da35f.tar.gz
quickshell-e7de6f62f438acfa723961000c558b4d9f9da35f.zip
assistant settings: a Cloud section, and Prefer for cloud models
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules/assistant')
-rw-r--r--desktop/modules/assistant/AssistantPage.qml185
1 files changed, 182 insertions, 3 deletions
diff --git a/desktop/modules/assistant/AssistantPage.qml b/desktop/modules/assistant/AssistantPage.qml
index e088241..5878b3b 100644
--- a/desktop/modules/assistant/AssistantPage.qml
+++ b/desktop/modules/assistant/AssistantPage.qml
@@ -166,9 +166,9 @@ Column {
Small {
width: page.width
wrapMode: Text.Wrap
- text: "The assistant answers with whichever model llama-server has loaded. Opening it loads "
- + "the preferred model if another one is in. Load swaps a model in now, for every client, "
- + "without changing the preference."
+ text: "Each chat uses the model picked in the canvas header, else the preferred one. "
+ + "Opening the canvas loads that model if it is a local one and another is in. "
+ + "Load swaps a model in now, for every client, without changing the preference."
}
Small {
@@ -231,6 +231,50 @@ Column {
}
}
+ // The saved cloud models: Prefer only, there is nothing to load.
+ Repeater {
+ model: page.asst.savedClouds.flatMap(c => c.models.map(m => c.name + ":" + m))
+
+ delegate: Rectangle {
+ id: cloudRow
+ required property string modelData
+ readonly property bool preferred: Prefs.assistantModel === modelData
+ objectName: "model:" + modelData
+ width: page.width
+ height: 40
+ radius: 8
+ color: Qt.alpha(Theme.surface, 0.35)
+
+ // nf-fa-cloud, present in Inconsolata Nerd Font.
+ Text {
+ id: cloudIcon
+ anchors { left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter }
+ text: ""
+ color: Theme.subtext
+ font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 4 }
+ }
+ Text {
+ id: cloudName
+ anchors { left: cloudIcon.right; leftMargin: 8; verticalCenter: parent.verticalCenter }
+ text: cloudRow.modelData
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 }
+ }
+ Small {
+ anchors { left: cloudName.right; leftMargin: 10; verticalCenter: parent.verticalCenter }
+ visible: cloudRow.preferred
+ text: "preferred"
+ color: Theme.accent
+ }
+ Button {
+ anchors { right: parent.right; rightMargin: 6; verticalCenter: parent.verticalCenter }
+ text: cloudRow.preferred ? "Preferred" : "Prefer"
+ enabled: !cloudRow.preferred
+ onClicked: Prefs.setAssistantModel(cloudRow.modelData)
+ }
+ }
+ }
+
Small {
width: page.width
visible: page.asst.modelError !== ""
@@ -368,6 +412,141 @@ Column {
// --- env ---
+ // --- cloud ---
+
+ Heading { text: "Cloud" }
+
+ Small {
+ width: page.width
+ wrapMode: Text.Wrap
+ text: "Providers with an OpenAI-compatible API, offered in the canvas's model menu. A cloud "
+ + "chat is sent its history, attachments and the results of the tools marked "
+ + "cloud = true; voice is still transcribed here. The key command prints the API key: "
+ + "the assistant runs it at startup and on Fetch, which saves this page first."
+ }
+
+ Small {
+ width: page.width
+ visible: page.asst.clouds === null
+ wrapMode: Text.Wrap
+ text: "config.toml has a [[cloud]] table this page cannot read; edit it by hand."
+ color: Theme.red
+ }
+
+ Repeater {
+ model: page.asst.clouds === null ? 0 : page.asst.cloudCount
+
+ delegate: Rectangle {
+ id: prov
+ required property int index
+ readonly property var c: page.asst.clouds?.[index] ?? { name: "", url: "", key_cmd: "", models: [] }
+ readonly property var got: page.asst.fetched[prov.c.name]
+ objectName: "cloud:" + index
+ width: page.width
+ height: provCol.implicitHeight + 20
+ radius: 8
+ color: Qt.alpha(Theme.surface, 0.35)
+
+ Column {
+ id: provCol
+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: 10 }
+ spacing: 6
+
+ Labelled {
+ width: provCol.width
+ label: "Name"
+ Field {
+ anchors.fill: parent
+ value: prov.c.name
+ placeholder: "openrouter"
+ onEdited: t => page.asst.editCloud(prov.index, { name: t.trim() })
+ }
+ }
+ Labelled {
+ width: provCol.width
+ label: "URL"
+ Field {
+ anchors.fill: parent
+ value: prov.c.url
+ placeholder: "https://openrouter.ai/api/v1"
+ onEdited: t => page.asst.editCloud(prov.index, { url: t.trim() })
+ }
+ }
+ Labelled {
+ width: provCol.width
+ label: "Key command"
+ Field {
+ anchors.fill: parent
+ value: prov.c.key_cmd
+ placeholder: "pass show llm/openrouter"
+ onEdited: t => page.asst.editCloud(prov.index, { key_cmd: t.trim() })
+ }
+ }
+ Row {
+ spacing: 6
+ Button {
+ text: page.asst.fetching === prov.c.name ? "Fetching…" : "Fetch models"
+ enabled: page.asst.fetching === "" && page.asst.valid && prov.c.name !== ""
+ onClicked: page.asst.fetchModels(prov.c.name)
+ }
+ Button {
+ text: "Remove"
+ danger: true
+ onClicked: page.asst.removeCloud(prov.index)
+ }
+ }
+ Small {
+ width: provCol.width
+ visible: typeof prov.got === "string"
+ wrapMode: Text.Wrap
+ text: typeof prov.got === "string" ? prov.got : ""
+ color: Theme.red
+ }
+ // The fetched ids and any kept from before, each switched
+ // into models or out of it.
+ Repeater {
+ model: Array.isArray(prov.got)
+ ? [...new Set(prov.got.concat(prov.c.models))].sort() : prov.c.models
+
+ delegate: Item {
+ id: idRow
+ required property string modelData
+ width: provCol.width
+ height: 28
+ Switch {
+ id: idOn
+ anchors { left: parent.left; verticalCenter: parent.verticalCenter }
+ checked: prov.c.models.includes(idRow.modelData)
+ onToggled: on => page.asst.editCloud(prov.index, { models: on
+ ? prov.c.models.concat([idRow.modelData])
+ : prov.c.models.filter(m => m !== idRow.modelData) })
+ }
+ Text {
+ anchors { left: idOn.right; leftMargin: 10; verticalCenter: parent.verticalCenter }
+ text: idRow.modelData
+ color: Theme.text
+ font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Small {
+ width: page.width
+ visible: page.asst.cloudError !== ""
+ wrapMode: Text.Wrap
+ text: page.asst.cloudError
+ color: Theme.red
+ }
+
+ Button {
+ text: "Add provider"
+ enabled: page.asst.clouds !== null
+ onClicked: page.asst.editCloud(page.asst.cloudCount, {})
+ }
+
Heading { text: "Tool environment" }
Small {
width: page.width