Merge remote-tracking branch 'origin/main' into addon-i18n

This commit is contained in:
月月
2025-07-02 22:21:58 +08:00
79 changed files with 1631 additions and 830 deletions
+64 -13
View File
@@ -19,14 +19,14 @@ Singleton {
readonly property string interfaceRole: "interface"
readonly property string apiKeyEnvVarName: "API_KEY"
property Component aiMessageComponent: AiMessageData {}
property string systemPrompt: ConfigOptions?.ai?.systemPrompt ?? ""
property string systemPrompt: Config.options?.ai?.systemPrompt ?? ""
property var messages: []
property var messageIDs: []
property var messageByID: ({})
readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {}
readonly property var apiKeysLoaded: KeyringStorage.loaded
property var postResponseHook
property real temperature: PersistentStates?.ai?.temperature ?? 0.5
property real temperature: Persistent.states?.ai?.temperature ?? 0.5
function idForMessage(message) {
// Generate a unique ID using timestamp and random value
@@ -37,6 +37,10 @@ Singleton {
return modelName.replace(/:/g, "_").replace(/\./g, "_")
}
property list<var> defaultPrompts: []
property list<var> userPrompts: []
property list<var> promptFiles: [...defaultPrompts, ...userPrompts]
// Model properties:
// - name: Name of the model
// - icon: Icon name of the model
@@ -203,11 +207,10 @@ Singleton {
},
}
property var modelList: Object.keys(root.models)
property var currentModelId: PersistentStates?.ai?.model || modelList[0]
property var currentModelId: Persistent.states?.ai?.model || modelList[0]
Component.onCompleted: {
setModel(currentModelId, false); // Do necessary setup for model
getOllamaModels.running = true
setModel(currentModelId, false, false); // Do necessary setup for model
}
function guessModelLogo(model) {
@@ -233,6 +236,7 @@ Singleton {
Process {
id: getOllamaModels
running: true
command: ["bash", "-c", `${Directories.config}/quickshell/scripts/ai/show-installed-ollama-models.sh`.replace(/file:\/\//, "")]
stdout: SplitParser {
onRead: data => {
@@ -261,6 +265,54 @@ Singleton {
}
}
Process {
id: getDefaultPrompts
running: true
command: ["ls", "-1", Directories.defaultAiPrompts]
stdout: StdioCollector {
onStreamFinished: {
if (text.length === 0) return;
root.defaultPrompts = text.split("\n")
.filter(fileName => fileName.endsWith(".md") || fileName.endsWith(".txt"))
.map(fileName => `${Directories.defaultAiPrompts}/${fileName}`)
}
}
}
Process {
id: getUserPrompts
running: true
command: ["ls", "-1", Directories.userAiPrompts]
stdout: StdioCollector {
onStreamFinished: {
if (text.length === 0) return;
root.userPrompts = text.split("\n")
.filter(fileName => fileName.endsWith(".md") || fileName.endsWith(".txt"))
.map(fileName => `${Directories.userAiPrompts}/${fileName}`)
}
}
}
FileView {
id: promptLoader
watchChanges: false;
onLoadedChanged: {
if (!promptLoader.loaded) return;
Config.options.ai.systemPrompt = promptLoader.text();
root.addMessage(StringUtils.format("Loaded the following system prompt\n\n---\n\n{0}", Config.options.ai.systemPrompt), root.interfaceRole);
}
}
function printPrompt() {
root.addMessage(StringUtils.format("The current system prompt is\n\n---\n\n{0}", Config.options.ai.systemPrompt), root.interfaceRole);
}
function loadPrompt(filePath) {
promptLoader.path = "" // Unload
promptLoader.path = filePath; // Load
promptLoader.reload();
}
function addMessage(message, role) {
if (message.length === 0) return;
const aiMessage = aiMessageComponent.createObject(root, {
@@ -294,7 +346,7 @@ Singleton {
return models[currentModelId];
}
function setModel(modelId, feedback = true) {
function setModel(modelId, feedback = true, setPersistentState = true) {
if (!modelId) modelId = ""
modelId = modelId.toLowerCase()
if (modelList.indexOf(modelId) !== -1) {
@@ -302,12 +354,12 @@ Singleton {
// Fetch API keys if needed
if (model?.requires_key) KeyringStorage.fetchKeyringData();
// See if policy prevents online models
if (ConfigOptions.policies.ai === 2 && !model.endpoint.includes("localhost")) {
if (Config.options.policies.ai === 2 && !model.endpoint.includes("localhost")) {
root.addMessage(StringUtils.format(StringUtils.format("Online models disallowed\n\nControlled by `policies.ai` config option"), model.name), root.interfaceRole);
return;
}
PersistentStateManager.setState("ai.model", modelId);
if (feedback) root.addMessage(StringUtils.format(StringUtils.format("Model set to {0}"), model.name), root.interfaceRole);
if (setPersistentState) Persistent.states.ai.model = modelId;
if (feedback) root.addMessage(StringUtils.format("Model set to {0}", model.name), root.interfaceRole);
if (model.requires_key) {
// If key not there show advice
if (root.apiKeysLoaded && (!root.apiKeys[model.key_id] || root.apiKeys[model.key_id].length === 0)) {
@@ -328,7 +380,7 @@ Singleton {
root.addMessage(Translation.tr("Temperature must be between 0 and 2"), Ai.interfaceRole);
return;
}
PersistentStateManager.setState("ai.temperature", value);
Persistent.states.ai.temperature = value;
root.temperature = value;
root.addMessage(StringUtils.format(Translation.tr("Temperature set to {0}"), value), Ai.interfaceRole);
}
@@ -705,7 +757,7 @@ Singleton {
addFunctionOutputMessage(name, Translation.tr("Switched to search mode. Continue with the user's request."))
requester.makeRequest();
} else if (name === "get_shell_config") {
const configJson = ObjectUtils.toPlainObject(ConfigOptions)
const configJson = ObjectUtils.toPlainObject(Config.options)
addFunctionOutputMessage(name, JSON.stringify(configJson));
requester.makeRequest();
} else if (name === "set_shell_config") {
@@ -715,8 +767,7 @@ Singleton {
}
const key = args.key;
const value = args.value;
ConfigLoader.setLiveConfigValue(key, value);
ConfigLoader.saveConfig();
Config.setNestedValue(key, value);
}
else root.addMessage(Translation.tr("Unknown function call: {0}"), "assistant");
}