mirror of
https://github.com/danbulant/dots-hyprland
synced 2026-05-24 12:22:09 +00:00
124 lines
4.7 KiB
QML
124 lines
4.7 KiB
QML
import QtQuick
|
|
|
|
ApiStrategy {
|
|
property bool isReasoning: false
|
|
|
|
function buildEndpoint(model: AiModel): string {
|
|
// console.log("[AI] Endpoint: " + model.endpoint);
|
|
return model.endpoint;
|
|
}
|
|
|
|
function buildRequestData(model: AiModel, messages, systemPrompt: string, temperature: real, tools: list<var>) {
|
|
let baseData = {
|
|
"model": model.model,
|
|
"messages": [
|
|
{role: "system", content: systemPrompt},
|
|
...messages.map(message => {
|
|
const hasFunctionCall = message.functionCall != undefined && message.functionName.length > 0
|
|
let messageData = {
|
|
"role": message.role,
|
|
"content": message.rawContent,
|
|
}
|
|
if (hasFunctionCall) {
|
|
if (message.functionResponse?.length > 0) {
|
|
messageData.name = message.functionName; // Does the func call also need this name? or just the func output?
|
|
messageData.role = "tool";
|
|
messageData.content = message.functionResponse;
|
|
messageData.tool_call_id = message.functionCall.id
|
|
}
|
|
}
|
|
return messageData
|
|
}),
|
|
],
|
|
"stream": true,
|
|
"temperature": temperature,
|
|
"tools": tools,
|
|
};
|
|
// console.log("[AI] Request data: ", JSON.stringify(baseData, null, 2));
|
|
return model.extraParams ? Object.assign({}, baseData, model.extraParams) : baseData;
|
|
}
|
|
|
|
function buildAuthorizationHeader(apiKeyEnvVarName: string): string {
|
|
return `-H "Authorization: Bearer \$\{${apiKeyEnvVarName}\}"`;
|
|
}
|
|
|
|
function parseResponseLine(line, message) {
|
|
// Remove 'data: ' prefix if present and trim whitespace
|
|
let cleanData = line.trim();
|
|
if (cleanData.startsWith("data:")) {
|
|
cleanData = cleanData.slice(5).trim();
|
|
}
|
|
|
|
// Handle special cases
|
|
if (!cleanData || cleanData.startsWith(":")) return {};
|
|
if (cleanData === "[DONE]") {
|
|
return { finished: true };
|
|
}
|
|
|
|
// Real stuff
|
|
try {
|
|
const dataJson = JSON.parse(cleanData);
|
|
let newContent = "";
|
|
|
|
const responseContent = dataJson.choices[0]?.delta?.content || dataJson.message?.content;
|
|
const responseReasoning = dataJson.choices[0]?.delta?.reasoning || dataJson.choices[0]?.delta?.reasoning_content;
|
|
|
|
// Function call
|
|
if (dataJson.choices[0]?.delta?.tool_calls) {
|
|
const functionCall = dataJson.choices[0].delta.tool_calls[0];
|
|
const functionName = functionCall.function.name;
|
|
const functionArgs = JSON.parse(functionCall.function.arguments) || {}; // Args are given as string???
|
|
const functionId = functionCall.id;
|
|
const newContent = `\n\n[[ Function: ${functionName}(${JSON.stringify(functionArgs, null, 2)}) ]]\n`;
|
|
message.rawContent += newContent;
|
|
message.content += newContent;
|
|
message.functionName = functionName;
|
|
message.functionCall = functionName;
|
|
return { functionCall: { name: functionName, args: functionArgs, id: functionId } };
|
|
}
|
|
|
|
// Thinking?
|
|
if (responseContent && responseContent.length > 0) {
|
|
if (isReasoning) {
|
|
isReasoning = false;
|
|
const endBlock = "\n\n</think>\n\n";
|
|
message.content += endBlock;
|
|
message.rawContent += endBlock;
|
|
}
|
|
newContent = responseContent;
|
|
} else if (responseReasoning && responseReasoning.length > 0) {
|
|
if (!isReasoning) {
|
|
isReasoning = true;
|
|
const startBlock = "\n\n<think>\n\n";
|
|
message.rawContent += startBlock;
|
|
message.content += startBlock;
|
|
}
|
|
newContent = responseReasoning;
|
|
}
|
|
|
|
// Text
|
|
message.content += newContent;
|
|
message.rawContent += newContent;
|
|
|
|
if (`dataJson`.done) {
|
|
return { finished: true };
|
|
}
|
|
|
|
} catch (e) {
|
|
console.log("[AI] Mistral: Could not parse line: ", e);
|
|
message.rawContent += line;
|
|
message.content += line;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
function onRequestFinished(message) {
|
|
return {};
|
|
}
|
|
|
|
function reset() {
|
|
isReasoning = false;
|
|
}
|
|
|
|
}
|