forked from Shinonome/dots-hyprland
ai: separate model and tool selection
This commit is contained in:
@@ -61,6 +61,7 @@ Singleton {
|
|||||||
|
|
||||||
property JsonObject ai: JsonObject {
|
property JsonObject ai: JsonObject {
|
||||||
property string systemPrompt: "## Style\n- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question\n\n## Presentation\n- Use Markdown features in your response: \n - **Bold** text to **highlight keywords** in your response\n - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n\nThanks!\n\n## Tools\nMay or may not be available depending on the user's settings. If they're available, follow these guidelines:\n\n### Search\n- When user asks for information that might benefit from up-to-date information, use this to get search access\n\n### Shell configuration\n- Always fetch the config options to see the available keys before setting\n- Avoid unnecessarily asking the user to confirm the changes they explicitly asked for, just do it\n"
|
property string systemPrompt: "## Style\n- Use casual tone, don't be formal! Make sure you answer precisely without hallucination and prefer bullet points over walls of text. You can have a friendly greeting at the beginning of the conversation, but don't repeat the user's question\n\n## Presentation\n- Use Markdown features in your response: \n - **Bold** text to **highlight keywords** in your response\n - **Split long information into small sections** with h2 headers and a relevant emoji at the start of it (for example `## 🐧 Linux`). Bullet points are preferred over long paragraphs, unless you're offering writing support or instructed otherwise by the user.\n- Asked to compare different options? You should firstly use a table to compare the main aspects, then elaborate or include relevant comments from online forums *after* the table. Make sure to provide a final recommendation for the user's use case!\n- Use LaTeX formatting for mathematical and scientific notations whenever appropriate. Enclose all LaTeX '$$' delimiters. NEVER generate LaTeX code in a latex block unless the user explicitly asks for it. DO NOT use LaTeX for regular documents (resumes, letters, essays, CVs, etc.).\n\nThanks!\n\n## Tools\nMay or may not be available depending on the user's settings. If they're available, follow these guidelines:\n\n### Search\n- When user asks for information that might benefit from up-to-date information, use this to get search access\n\n### Shell configuration\n- Always fetch the config options to see the available keys before setting\n- Avoid unnecessarily asking the user to confirm the changes they explicitly asked for, just do it\n"
|
||||||
|
property string tool: "functions" // search, functions, or none
|
||||||
}
|
}
|
||||||
|
|
||||||
property JsonObject appearance: JsonObject {
|
property JsonObject appearance: JsonObject {
|
||||||
|
|||||||
@@ -45,6 +45,22 @@ Item {
|
|||||||
Ai.setModel(args[0]);
|
Ai.setModel(args[0]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "tool",
|
||||||
|
description: Translation.tr("Set the tool to use for the model."),
|
||||||
|
execute: (args) => {
|
||||||
|
// console.log(args)
|
||||||
|
if (args.length == 0 || args[0] == "get") {
|
||||||
|
Ai.addMessage(Translation.tr("Usage: %1tool TOOL_NAME").arg(root.commandPrefix), Ai.interfaceRole);
|
||||||
|
} else {
|
||||||
|
const tool = args[0];
|
||||||
|
const switched = Ai.setTool(tool);
|
||||||
|
if (switched) {
|
||||||
|
Ai.addMessage(Translation.tr("Tool set to %1").arg(tool), Ai.interfaceRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "prompt",
|
name: "prompt",
|
||||||
description: Translation.tr("Set the system prompt for the model."),
|
description: Translation.tr("Set the system prompt for the model."),
|
||||||
@@ -73,7 +89,7 @@ Item {
|
|||||||
execute: (args) => {
|
execute: (args) => {
|
||||||
const joinedArgs = args.join(" ")
|
const joinedArgs = args.join(" ")
|
||||||
if (joinedArgs.trim().length == 0) {
|
if (joinedArgs.trim().length == 0) {
|
||||||
Ai.addMessage(`Usage: ${root.commandPrefix}save CHAT_NAME`, Ai.interfaceRole);
|
Ai.addMessage(Translation.tr("Usage: %1save CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ai.saveChat(joinedArgs)
|
Ai.saveChat(joinedArgs)
|
||||||
@@ -85,7 +101,7 @@ Item {
|
|||||||
execute: (args) => {
|
execute: (args) => {
|
||||||
const joinedArgs = args.join(" ")
|
const joinedArgs = args.join(" ")
|
||||||
if (joinedArgs.trim().length == 0) {
|
if (joinedArgs.trim().length == 0) {
|
||||||
Ai.addMessage(`Usage: ${root.commandPrefix}load CHAT_NAME`, Ai.interfaceRole);
|
Ai.addMessage(Translation.tr("Usage: %1load CHAT_NAME").arg(root.commandPrefix), Ai.interfaceRole);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ai.loadChat(joinedArgs)
|
Ai.loadChat(joinedArgs)
|
||||||
@@ -606,8 +622,9 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
|||||||
|
|
||||||
property var commandsShown: [
|
property var commandsShown: [
|
||||||
{
|
{
|
||||||
name: "model",
|
name: "",
|
||||||
sendDirectly: false,
|
sendDirectly: false,
|
||||||
|
dontAddSpace: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "clear",
|
name: "clear",
|
||||||
@@ -615,45 +632,25 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
Item {
|
ApiInputBoxIndicator { // Model indicator
|
||||||
implicitHeight: providerRowLayout.implicitHeight + 5 * 2
|
icon: "api"
|
||||||
implicitWidth: providerRowLayout.implicitWidth + 10 * 2
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: providerRowLayout
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
text: "api"
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
id: providerName
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
|
||||||
color: Appearance.m3colors.m3onSurface
|
|
||||||
elide: Text.ElideRight
|
|
||||||
text: Ai.getModel().name
|
text: Ai.getModel().name
|
||||||
}
|
tooltipText: Translation.tr("Current model: %1\nSet it with %2model MODEL")
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
id: toolTip
|
|
||||||
extraVisibleCondition: false
|
|
||||||
alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered
|
|
||||||
content: Translation.tr("Current model: %1\nSet it with %2model MODEL")
|
|
||||||
.arg(Ai.getModel().name)
|
.arg(Ai.getModel().name)
|
||||||
.arg(root.commandPrefix)
|
.arg(root.commandPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
ApiInputBoxIndicator { // Tool indicator
|
||||||
id: mouseArea
|
icon: "service_toolbox"
|
||||||
anchors.fill: parent
|
text: Ai.currentTool.charAt(0).toUpperCase() + Ai.currentTool.slice(1)
|
||||||
hoverEnabled: true
|
tooltipText: Translation.tr("Current tool: %1\nSet it with %2tool TOOL")
|
||||||
}
|
.arg(Ai.currentTool)
|
||||||
|
.arg(root.commandPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
|
|
||||||
ButtonGroup {
|
ButtonGroup { // Command buttons
|
||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
Repeater { // Command buttons
|
Repeater { // Command buttons
|
||||||
@@ -665,7 +662,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
|||||||
if(modelData.sendDirectly) {
|
if(modelData.sendDirectly) {
|
||||||
root.handleInput(commandRepresentation)
|
root.handleInput(commandRepresentation)
|
||||||
} else {
|
} else {
|
||||||
messageInputField.text = commandRepresentation + " "
|
messageInputField.text = commandRepresentation + (modelData.dontAddSpace ? "" : " ")
|
||||||
messageInputField.cursorPosition = messageInputField.text.length
|
messageInputField.cursorPosition = messageInputField.text.length
|
||||||
messageInputField.forceActiveFocus()
|
messageInputField.forceActiveFocus()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,42 +492,14 @@ Item {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
Item {
|
ApiInputBoxIndicator { // Tool indicator
|
||||||
implicitHeight: providerRowLayout.implicitHeight + 5 * 2
|
icon: "api"
|
||||||
implicitWidth: providerRowLayout.implicitWidth + 10 * 2
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: providerRowLayout
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
MaterialSymbol {
|
|
||||||
text: "api"
|
|
||||||
iconSize: Appearance.font.pixelSize.large
|
|
||||||
}
|
|
||||||
StyledText {
|
|
||||||
id: providerName
|
|
||||||
font.pixelSize: Appearance.font.pixelSize.small
|
|
||||||
color: Appearance.m3colors.m3onSurface
|
|
||||||
text: Booru.providers[Booru.currentProvider].name
|
text: Booru.providers[Booru.currentProvider].name
|
||||||
}
|
tooltipText: Translation.tr("Current API endpoint: %1\nSet it with %2mode PROVIDER")
|
||||||
}
|
|
||||||
StyledToolTip {
|
|
||||||
id: toolTip
|
|
||||||
extraVisibleCondition: false
|
|
||||||
alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered
|
|
||||||
// content: Translation.tr("The current API used. Endpoint: ") + Booru.providers[Booru.currentProvider].url + Translation.tr("\nSet with /mode PROVIDER")
|
|
||||||
content: Translation.tr("Current API endpoint: %1\nSet it with %2mode PROVIDER")
|
|
||||||
.arg(Booru.providers[Booru.currentProvider].url)
|
.arg(Booru.providers[Booru.currentProvider].url)
|
||||||
.arg(root.commandPrefix)
|
.arg(root.commandPrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
font.pixelSize: Appearance.font.pixelSize.large
|
font.pixelSize: Appearance.font.pixelSize.large
|
||||||
color: Appearance.colors.colOnLayer1
|
color: Appearance.colors.colOnLayer1
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.services
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
GroupButton {
|
GroupButton {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.services
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
Item { // Model indicator
|
||||||
|
id: root
|
||||||
|
property string icon: "api"
|
||||||
|
property string text: ""
|
||||||
|
property string tooltipText: ""
|
||||||
|
implicitHeight: rowLayout.implicitHeight + 5 * 2
|
||||||
|
implicitWidth: rowLayout.implicitWidth + 10 * 2
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: rowLayout
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
text: root.icon
|
||||||
|
iconSize: Appearance.font.pixelSize.normal
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
id: providerName
|
||||||
|
font.pixelSize: Appearance.font.pixelSize.smaller
|
||||||
|
color: Appearance.m3colors.m3onSurface
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: root.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
active: root.tooltipText?.length > 0
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceComponent: MouseArea {
|
||||||
|
id: mouseArea
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
StyledToolTip {
|
||||||
|
id: toolTip
|
||||||
|
extraVisibleCondition: false
|
||||||
|
alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered
|
||||||
|
content: root.tooltipText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,8 +62,10 @@ Singleton {
|
|||||||
|
|
||||||
// Gemini: https://ai.google.dev/gemini-api/docs/function-calling
|
// Gemini: https://ai.google.dev/gemini-api/docs/function-calling
|
||||||
// OpenAI: https://platform.openai.com/docs/guides/function-calling
|
// OpenAI: https://platform.openai.com/docs/guides/function-calling
|
||||||
|
property string currentTool: Config?.options.ai.tool ?? "search"
|
||||||
property var tools: {
|
property var tools: {
|
||||||
"gemini": [{"functionDeclarations": [
|
"gemini": {
|
||||||
|
"functions": [{"functionDeclarations": [
|
||||||
{
|
{
|
||||||
"name": "switch_to_search_mode",
|
"name": "switch_to_search_mode",
|
||||||
"description": "Search the web",
|
"description": "Search the web",
|
||||||
@@ -105,7 +107,13 @@ Singleton {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
]}],
|
]}],
|
||||||
"openai": [
|
"search": [{
|
||||||
|
"google_search": {}
|
||||||
|
}],
|
||||||
|
"none": []
|
||||||
|
},
|
||||||
|
"openai": {
|
||||||
|
"functions": [
|
||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"name": "get_shell_config",
|
"name": "get_shell_config",
|
||||||
@@ -131,7 +139,10 @@ Singleton {
|
|||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"search": [],
|
||||||
|
"none": [],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Model properties:
|
// Model properties:
|
||||||
@@ -145,13 +156,12 @@ Singleton {
|
|||||||
// - key_get_link: Link to get an API key
|
// - key_get_link: Link to get an API key
|
||||||
// - key_get_description: Description of pricing and how to get an API key
|
// - key_get_description: Description of pricing and how to get an API key
|
||||||
// - api_format: The API format of the model. Can be "openai" or "gemini". Default is "openai".
|
// - api_format: The API format of the model. Can be "openai" or "gemini". Default is "openai".
|
||||||
// - tools: List of tools that the model can use. Each tool is an object with the tool name as the key and an empty object as the value.
|
|
||||||
// - extraParams: Extra parameters to be passed to the model. This is a JSON object.
|
// - extraParams: Extra parameters to be passed to the model. This is a JSON object.
|
||||||
property var models: {
|
property var models: {
|
||||||
"gemini-2.0-flash-search": aiModelComponent.createObject(this, {
|
"gemini-2.0-flash": aiModelComponent.createObject(this, {
|
||||||
"name": "Gemini 2.0 Flash (Search)",
|
"name": "Gemini 2.0 Flash",
|
||||||
"icon": "google-gemini-symbolic",
|
"icon": "google-gemini-symbolic",
|
||||||
"description": Translation.tr("Online | Google's model\nGives up-to-date information with search."),
|
"description": Translation.tr("Online | Google's model\nFast, can perform searches for up-to-date information"),
|
||||||
"homepage": "https://aistudio.google.com",
|
"homepage": "https://aistudio.google.com",
|
||||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent",
|
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent",
|
||||||
"model": "gemini-2.0-flash",
|
"model": "gemini-2.0-flash",
|
||||||
@@ -160,28 +170,11 @@ Singleton {
|
|||||||
"key_get_link": "https://aistudio.google.com/app/apikey",
|
"key_get_link": "https://aistudio.google.com/app/apikey",
|
||||||
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
||||||
"api_format": "gemini",
|
"api_format": "gemini",
|
||||||
"tools": [{
|
|
||||||
"google_search": {}
|
|
||||||
}]
|
|
||||||
}),
|
}),
|
||||||
"gemini-2.0-flash-tools": aiModelComponent.createObject(this, {
|
"gemini-2.5-flash": aiModelComponent.createObject(this, {
|
||||||
"name": "Gemini 2.0 Flash (Tools)",
|
"name": "Gemini 2.5 Flash",
|
||||||
"icon": "google-gemini-symbolic",
|
"icon": "google-gemini-symbolic",
|
||||||
"description": Translation.tr("Experimental | Online | Google's model\nCan do a little more but takes an extra turn to perform search"),
|
"description": Translation.tr("Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers"),
|
||||||
"homepage": "https://aistudio.google.com",
|
|
||||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent",
|
|
||||||
"model": "gemini-2.0-flash",
|
|
||||||
"requires_key": true,
|
|
||||||
"key_id": "gemini",
|
|
||||||
"key_get_link": "https://aistudio.google.com/app/apikey",
|
|
||||||
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
|
||||||
"api_format": "gemini",
|
|
||||||
"tools": root.tools["gemini"],
|
|
||||||
}),
|
|
||||||
"gemini-2.5-flash-search": aiModelComponent.createObject(this, {
|
|
||||||
"name": "Gemini 2.5 Flash (Search)",
|
|
||||||
"icon": "google-gemini-symbolic",
|
|
||||||
"description": Translation.tr("Online | Google's model\nGives up-to-date information with search."),
|
|
||||||
"homepage": "https://aistudio.google.com",
|
"homepage": "https://aistudio.google.com",
|
||||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent",
|
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent",
|
||||||
"model": "gemini-2.5-flash",
|
"model": "gemini-2.5-flash",
|
||||||
@@ -190,44 +183,11 @@ Singleton {
|
|||||||
"key_get_link": "https://aistudio.google.com/app/apikey",
|
"key_get_link": "https://aistudio.google.com/app/apikey",
|
||||||
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
||||||
"api_format": "gemini",
|
"api_format": "gemini",
|
||||||
"tools": [{
|
|
||||||
"google_search": {}
|
|
||||||
}]
|
|
||||||
}),
|
|
||||||
"gemini-2.5-flash-tools": aiModelComponent.createObject(this, {
|
|
||||||
"name": "Gemini 2.5 Flash (Tools)",
|
|
||||||
"icon": "google-gemini-symbolic",
|
|
||||||
"description": Translation.tr("Experimental | Online | Google's model\nCan do a little more but takes an extra turn to perform search"),
|
|
||||||
"homepage": "https://aistudio.google.com",
|
|
||||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:streamGenerateContent",
|
|
||||||
"model": "gemini-2.5-flash",
|
|
||||||
"requires_key": true,
|
|
||||||
"key_id": "gemini",
|
|
||||||
"key_get_link": "https://aistudio.google.com/app/apikey",
|
|
||||||
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
|
||||||
"api_format": "gemini",
|
|
||||||
"tools": root.tools["gemini"],
|
|
||||||
}),
|
|
||||||
"gemini-2.5-flash-lite-search": aiModelComponent.createObject(this, {
|
|
||||||
"name": "Gemini 2.5 Flash-Lite (Search)",
|
|
||||||
"icon": "google-gemini-symbolic",
|
|
||||||
"description": Translation.tr("Experimental | Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput."),
|
|
||||||
"homepage": "https://aistudio.google.com",
|
|
||||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:streamGenerateContent",
|
|
||||||
"model": "gemini-2.5-flash-lite",
|
|
||||||
"requires_key": true,
|
|
||||||
"key_id": "gemini",
|
|
||||||
"key_get_link": "https://aistudio.google.com/app/apikey",
|
|
||||||
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
|
||||||
"api_format": "gemini",
|
|
||||||
"tools": [{
|
|
||||||
"google_search": {}
|
|
||||||
}]
|
|
||||||
}),
|
}),
|
||||||
"gemini-2.5-flash-lite": aiModelComponent.createObject(this, {
|
"gemini-2.5-flash-lite": aiModelComponent.createObject(this, {
|
||||||
"name": "Gemini 2.5 Flash-Lite",
|
"name": "Gemini 2.5 Flash-Lite",
|
||||||
"icon": "google-gemini-symbolic",
|
"icon": "google-gemini-symbolic",
|
||||||
"description": Translation.tr("Experimental | Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput."),
|
"description": Translation.tr("Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput."),
|
||||||
"homepage": "https://aistudio.google.com",
|
"homepage": "https://aistudio.google.com",
|
||||||
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:streamGenerateContent",
|
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:streamGenerateContent",
|
||||||
"model": "gemini-2.5-flash-lite",
|
"model": "gemini-2.5-flash-lite",
|
||||||
@@ -236,19 +196,6 @@ Singleton {
|
|||||||
"key_get_link": "https://aistudio.google.com/app/apikey",
|
"key_get_link": "https://aistudio.google.com/app/apikey",
|
||||||
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
"key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"),
|
||||||
"api_format": "gemini",
|
"api_format": "gemini",
|
||||||
"tools": root.tools["gemini"],
|
|
||||||
}),
|
|
||||||
"openrouter-llama4-maverick": aiModelComponent.createObject(this, {
|
|
||||||
"name": "Llama 4 Maverick",
|
|
||||||
"icon": "ollama-symbolic",
|
|
||||||
"description": Translation.tr("Online via %1 | %2's model").arg("OpenRouter").arg("Meta"),
|
|
||||||
"homepage": "https://openrouter.ai/meta-llama/llama-4-maverick:free",
|
|
||||||
"endpoint": "https://openrouter.ai/api/v1/chat/completions",
|
|
||||||
"model": "meta-llama/llama-4-maverick:free",
|
|
||||||
"requires_key": true,
|
|
||||||
"key_id": "openrouter",
|
|
||||||
"key_get_link": "https://openrouter.ai/settings/keys",
|
|
||||||
"key_get_description": Translation.tr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"),
|
|
||||||
}),
|
}),
|
||||||
"openrouter-deepseek-r1": aiModelComponent.createObject(this, {
|
"openrouter-deepseek-r1": aiModelComponent.createObject(this, {
|
||||||
"name": "DeepSeek R1",
|
"name": "DeepSeek R1",
|
||||||
@@ -453,6 +400,15 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setTool(tool) {
|
||||||
|
if (!root.tools[models[currentModelId]?.api_format] || !(tool in root.tools[models[currentModelId]?.api_format])) {
|
||||||
|
root.addMessage(Translation.tr("Invalid tool. Supported tools:\n- %1").arg(Object.keys(root.tools[models[currentModelId]?.api_format]).join("\n- ")), root.interfaceRole);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Config.options.ai.tool = tool;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function getTemperature() {
|
function getTemperature() {
|
||||||
return root.temperature;
|
return root.temperature;
|
||||||
}
|
}
|
||||||
@@ -535,7 +491,7 @@ Singleton {
|
|||||||
const endpoint = root.currentApiStrategy.buildEndpoint(model);
|
const endpoint = root.currentApiStrategy.buildEndpoint(model);
|
||||||
const messageArray = root.messageIDs.map(id => root.messageByID[id]);
|
const messageArray = root.messageIDs.map(id => root.messageByID[id]);
|
||||||
const filteredMessageArray = messageArray.filter(message => message.role !== Ai.interfaceRole);
|
const filteredMessageArray = messageArray.filter(message => message.role !== Ai.interfaceRole);
|
||||||
const data = root.currentApiStrategy.buildRequestData(model, filteredMessageArray, root.systemPrompt, root.temperature);
|
const data = root.currentApiStrategy.buildRequestData(model, filteredMessageArray, root.systemPrompt, root.temperature, root.tools[model.api_format][root.currentTool]);
|
||||||
// console.log("[Ai] Request data: ", JSON.stringify(data, null, 2));
|
// console.log("[Ai] Request data: ", JSON.stringify(data, null, 2));
|
||||||
|
|
||||||
let requestHeaders = {
|
let requestHeaders = {
|
||||||
@@ -580,9 +536,9 @@ Singleton {
|
|||||||
|
|
||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
onRead: data => {
|
onRead: data => {
|
||||||
// console.log("[Ai] Raw response line: ", data);
|
|
||||||
if (data.length === 0) return;
|
if (data.length === 0) return;
|
||||||
if (requester.message.thinking) requester.message.thinking = false;
|
if (requester.message.thinking) requester.message.thinking = false;
|
||||||
|
// console.log("[Ai] Raw response line: ", data);
|
||||||
|
|
||||||
// Handle response line
|
// Handle response line
|
||||||
try {
|
try {
|
||||||
@@ -696,18 +652,8 @@ Singleton {
|
|||||||
function handleFunctionCall(name, args: var, message: AiMessageData) {
|
function handleFunctionCall(name, args: var, message: AiMessageData) {
|
||||||
if (name === "switch_to_search_mode") {
|
if (name === "switch_to_search_mode") {
|
||||||
const modelId = root.currentModelId;
|
const modelId = root.currentModelId;
|
||||||
if (modelId.endsWith("-tools")) {
|
root.currentTool = "search"
|
||||||
const searchModelId = modelId.replace(/-tools$/, "-search");
|
root.postResponseHook = () => { root.currentTool = "functions" }
|
||||||
if (root.modelList.indexOf(searchModelId) !== -1) {
|
|
||||||
root.setModel(searchModelId, false);
|
|
||||||
root.postResponseHook = () => root.setModel(modelId, false);
|
|
||||||
} else {
|
|
||||||
root.addMessage(Translation.tr("No corresponding search model found for %1").arg(modelId), Ai.interfaceRole);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root.addMessage(Translation.tr("Cannot switch to search mode from %1").arg(root.currentModelId), Ai.interfaceRole);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addFunctionOutputMessage(name, Translation.tr("Switched to search mode. Continue with the user's request."))
|
addFunctionOutputMessage(name, Translation.tr("Switched to search mode. Continue with the user's request."))
|
||||||
requester.makeRequest();
|
requester.makeRequest();
|
||||||
} else if (name === "get_shell_config") {
|
} else if (name === "get_shell_config") {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import QtQuick;
|
|||||||
* - key_get_link: Link to get an API key
|
* - key_get_link: Link to get an API key
|
||||||
* - key_get_description: Description of pricing and how to get an API key
|
* - key_get_description: Description of pricing and how to get an API key
|
||||||
* - api_format: The API format of the model. Can be "openai" or "gemini". Default is "openai".
|
* - api_format: The API format of the model. Can be "openai" or "gemini". Default is "openai".
|
||||||
* - tools: List of tools that the model can use.
|
|
||||||
* - extraParams: Extra parameters to be passed to the model. This is a JSON object.
|
* - extraParams: Extra parameters to be passed to the model. This is a JSON object.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import QtQuick
|
|||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
function buildEndpoint(model: AiModel): string { throw new Error("Not implemented") }
|
function buildEndpoint(model: AiModel): string { throw new Error("Not implemented") }
|
||||||
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real) { throw new Error("Not implemented") }
|
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real, tools: list<var>) { throw new Error("Not implemented") }
|
||||||
function buildAuthorizationHeader(apiKeyEnvVarName: string): string { throw new Error("Not implemented") }
|
function buildAuthorizationHeader(apiKeyEnvVarName: string): string { throw new Error("Not implemented") }
|
||||||
function parseResponseLine(line: string, message: AiMessageData) { throw new Error("Not implemented") }
|
function parseResponseLine(line: string, message: AiMessageData) { throw new Error("Not implemented") }
|
||||||
function onRequestFinished(message: AiMessageData): var { return {} } // Default: no special handling
|
function onRequestFinished(message: AiMessageData): var { return {} } // Default: no special handling
|
||||||
|
|||||||
@@ -9,13 +9,12 @@ ApiStrategy {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real) {
|
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real, tools: list<var>) {
|
||||||
const tools = model.tools ?? [];
|
|
||||||
let baseData = {
|
let baseData = {
|
||||||
"contents": messages.map(message => {
|
"contents": messages.map(message => {
|
||||||
const geminiApiRoleName = (message.role === "assistant") ? "model" : message.role;
|
const geminiApiRoleName = (message.role === "assistant") ? "model" : message.role;
|
||||||
const usingSearch = tools[0].google_search != undefined
|
const usingSearch = tools[0]?.google_search !== undefined
|
||||||
if (!usingSearch && message.functionCall != undefined && message.functionCall.length > 0) {
|
if (!usingSearch && message.functionCall != undefined && message.functionName.length > 0) {
|
||||||
return {
|
return {
|
||||||
"role": geminiApiRoleName,
|
"role": geminiApiRoleName,
|
||||||
"parts": [{
|
"parts": [{
|
||||||
@@ -25,7 +24,7 @@ ApiStrategy {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!usingSearch && message.functionResponse != undefined && message.functionResponse.length > 0) {
|
if (!usingSearch && message.functionResponse != undefined && message.functionName.length > 0) {
|
||||||
return {
|
return {
|
||||||
"role": geminiApiRoleName,
|
"role": geminiApiRoleName,
|
||||||
"parts": [{
|
"parts": [{
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ ApiStrategy {
|
|||||||
return model.endpoint;
|
return model.endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real) {
|
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real, tools: list<var>) {
|
||||||
let baseData = {
|
let baseData = {
|
||||||
"model": model.model,
|
"model": model.model,
|
||||||
"messages": [
|
"messages": [
|
||||||
|
|||||||
Reference in New Issue
Block a user