forked from Shinonome/dots-hyprland
98 lines
3.2 KiB
QML
98 lines
3.2 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 => {
|
|
return {
|
|
"role": message.role,
|
|
"content": message.rawContent,
|
|
}
|
|
}),
|
|
],
|
|
"stream": true,
|
|
"temperature": temperature,
|
|
};
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
message.content += newContent;
|
|
message.rawContent += newContent;
|
|
|
|
if (dataJson.done) {
|
|
return { finished: true };
|
|
}
|
|
|
|
} catch (e) {
|
|
console.log("[AI] OpenAI: Could not parse line: ", e);
|
|
message.rawContent += line;
|
|
message.content += line;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
function onRequestFinished(message) {
|
|
// OpenAI format doesn't need special finish handling
|
|
return {};
|
|
}
|
|
|
|
function reset() {
|
|
isReasoning = false;
|
|
}
|
|
|
|
}
|