mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-13 17:59:15 -04:00
refactor: Add agents for providers
This commit is contained in:
@@ -125,7 +125,8 @@ void ChatCompressor::startCompression(const QString &chatFilePath, ChatModel *ch
|
||||
std::vector<std::unique_ptr<LLMQore::ContentBlock>> blocks;
|
||||
blocks.push_back(std::make_unique<LLMQore::TextContent>(buildCompressionPrompt()));
|
||||
|
||||
m_currentRequestId = session->send(std::move(blocks), /*toolsOverride=*/false);
|
||||
m_currentRequestId = session->send(
|
||||
std::move(blocks), /*toolsOverride=*/false, /*thinkingOverride=*/false);
|
||||
if (m_currentRequestId.isEmpty()) {
|
||||
handleCompressionError(tr("Failed to start compression request"));
|
||||
return;
|
||||
|
||||
@@ -167,6 +167,16 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
||||
&ChatAgentController::currentAgentChanged,
|
||||
this,
|
||||
&ChatRootView::isThinkingSupportChanged);
|
||||
connect(
|
||||
m_agentController,
|
||||
&ChatAgentController::currentAgentChanged,
|
||||
this,
|
||||
&ChatRootView::useToolsChanged);
|
||||
connect(
|
||||
m_agentController,
|
||||
&ChatAgentController::currentAgentChanged,
|
||||
this,
|
||||
&ChatRootView::useThinkingChanged);
|
||||
|
||||
auto editors = Core::EditorManager::instance();
|
||||
|
||||
@@ -292,7 +302,7 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
||||
if (m_pendingSend.active) {
|
||||
PendingSend p = m_pendingSend;
|
||||
m_pendingSend = {};
|
||||
dispatchSend(p.message, p.attachments, p.linkedFiles, p.useTools, p.useThinking);
|
||||
dispatchSend(p.message, p.attachments, p.linkedFiles);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -305,7 +315,7 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
||||
if (m_pendingSend.active) {
|
||||
PendingSend p = m_pendingSend;
|
||||
m_pendingSend = {};
|
||||
dispatchSend(p.message, p.attachments, p.linkedFiles, p.useTools, p.useThinking);
|
||||
dispatchSend(p.message, p.attachments, p.linkedFiles);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -426,21 +436,17 @@ void ChatRootView::sendMessage(const QString &message)
|
||||
{
|
||||
const QStringList attachments = m_attachmentFiles;
|
||||
const QStringList linkedFiles = m_linkedFiles;
|
||||
const bool tools = useTools();
|
||||
const bool thinking = useThinking();
|
||||
|
||||
if (deferSendForAutoCompress(message, attachments, linkedFiles, tools, thinking))
|
||||
if (deferSendForAutoCompress(message, attachments, linkedFiles))
|
||||
return;
|
||||
|
||||
dispatchSend(message, attachments, linkedFiles, tools, thinking);
|
||||
dispatchSend(message, attachments, linkedFiles);
|
||||
}
|
||||
|
||||
bool ChatRootView::deferSendForAutoCompress(
|
||||
const QString &message,
|
||||
const QStringList &attachments,
|
||||
const QStringList &linkedFiles,
|
||||
bool useToolsArg,
|
||||
bool useThinkingArg)
|
||||
const QStringList &linkedFiles)
|
||||
{
|
||||
auto &settings = Settings::chatAssistantSettings();
|
||||
if (!settings.autoCompress())
|
||||
@@ -466,7 +472,7 @@ bool ChatRootView::deferSendForAutoCompress(
|
||||
.arg(inputTokens)
|
||||
.arg(threshold));
|
||||
|
||||
m_pendingSend = {message, attachments, linkedFiles, useToolsArg, useThinkingArg, true};
|
||||
m_pendingSend = {message, attachments, linkedFiles, true};
|
||||
compressCurrentChat();
|
||||
return true;
|
||||
}
|
||||
@@ -474,9 +480,7 @@ bool ChatRootView::deferSendForAutoCompress(
|
||||
void ChatRootView::dispatchSend(
|
||||
const QString &message,
|
||||
const QStringList &attachments,
|
||||
const QStringList &linkedFiles,
|
||||
bool useToolsArg,
|
||||
bool useThinkingArg)
|
||||
const QStringList &linkedFiles)
|
||||
{
|
||||
if (m_recentFilePath.isEmpty()) {
|
||||
QString filePath = getAutosaveFilePath(message, attachments);
|
||||
@@ -497,7 +501,7 @@ void ChatRootView::dispatchSend(
|
||||
m_clientInterface->setSkillsManager(skillsManager());
|
||||
m_clientInterface->setSessionManager(sessionManager());
|
||||
m_clientInterface->setActiveAgent(currentChatAgent());
|
||||
m_clientInterface->sendMessage(message, attachments, linkedFiles, useToolsArg, useThinkingArg);
|
||||
m_clientInterface->sendMessage(message, attachments, linkedFiles);
|
||||
|
||||
m_fileManager->clearIntermediateStorage();
|
||||
clearAttachmentFiles();
|
||||
@@ -1112,24 +1116,12 @@ QString ChatRootView::lastErrorMessage() const
|
||||
|
||||
bool ChatRootView::useTools() const
|
||||
{
|
||||
return Settings::chatAssistantSettings().enableChatTools();
|
||||
}
|
||||
|
||||
void ChatRootView::setUseTools(bool enabled)
|
||||
{
|
||||
Settings::chatAssistantSettings().enableChatTools.setValue(enabled);
|
||||
Settings::chatAssistantSettings().writeSettings();
|
||||
return m_agentController->currentSupportsTools();
|
||||
}
|
||||
|
||||
bool ChatRootView::useThinking() const
|
||||
{
|
||||
return Settings::chatAssistantSettings().enableThinkingMode();
|
||||
}
|
||||
|
||||
void ChatRootView::setUseThinking(bool enabled)
|
||||
{
|
||||
Settings::chatAssistantSettings().enableThinkingMode.setValue(enabled);
|
||||
Settings::chatAssistantSettings().writeSettings();
|
||||
return m_agentController->currentSupportsThinking();
|
||||
}
|
||||
|
||||
void ChatRootView::applyFileEdit(const QString &editId)
|
||||
|
||||
@@ -48,8 +48,8 @@ class ChatRootView : public QQuickItem
|
||||
Q_PROPERTY(bool isRequestInProgress READ isRequestInProgress NOTIFY isRequestInProgressChanged FINAL)
|
||||
Q_PROPERTY(QString lastErrorMessage READ lastErrorMessage NOTIFY lastErrorMessageChanged FINAL)
|
||||
Q_PROPERTY(QString lastInfoMessage READ lastInfoMessage NOTIFY lastInfoMessageChanged FINAL)
|
||||
Q_PROPERTY(bool useTools READ useTools WRITE setUseTools NOTIFY useToolsChanged FINAL)
|
||||
Q_PROPERTY(bool useThinking READ useThinking WRITE setUseThinking NOTIFY useThinkingChanged FINAL)
|
||||
Q_PROPERTY(bool useTools READ useTools NOTIFY useToolsChanged FINAL)
|
||||
Q_PROPERTY(bool useThinking READ useThinking NOTIFY useThinkingChanged FINAL)
|
||||
Q_PROPERTY(QString sendShortcutText READ sendShortcutText NOTIFY sendShortcutTextChanged FINAL)
|
||||
|
||||
Q_PROPERTY(int currentMessageTotalEdits READ currentMessageTotalEdits NOTIFY currentMessageEditsStatsChanged FINAL)
|
||||
@@ -136,9 +136,7 @@ public:
|
||||
Q_INVOKABLE QVariantList searchSkills(const QString &query) const;
|
||||
|
||||
bool useTools() const;
|
||||
void setUseTools(bool enabled);
|
||||
bool useThinking() const;
|
||||
void setUseThinking(bool enabled);
|
||||
|
||||
Q_INVOKABLE void applyFileEdit(const QString &editId);
|
||||
Q_INVOKABLE void rejectFileEdit(const QString &editId);
|
||||
@@ -229,15 +227,11 @@ private:
|
||||
bool deferSendForAutoCompress(
|
||||
const QString &message,
|
||||
const QStringList &attachments,
|
||||
const QStringList &linkedFiles,
|
||||
bool useTools,
|
||||
bool useThinking);
|
||||
const QStringList &linkedFiles);
|
||||
void dispatchSend(
|
||||
const QString &message,
|
||||
const QStringList &attachments,
|
||||
const QStringList &linkedFiles,
|
||||
bool useTools,
|
||||
bool useThinking);
|
||||
const QStringList &linkedFiles);
|
||||
bool hasImageAttachments(const QStringList &attachments) const;
|
||||
|
||||
SessionFileRegistry *sessionFileRegistry() const;
|
||||
@@ -256,8 +250,6 @@ private:
|
||||
QString message;
|
||||
QStringList attachments;
|
||||
QStringList linkedFiles;
|
||||
bool useTools = false;
|
||||
bool useThinking = false;
|
||||
bool active = false;
|
||||
};
|
||||
PendingSend m_pendingSend;
|
||||
|
||||
@@ -78,12 +78,8 @@ void ClientInterface::setActiveAgent(const QString &agentName)
|
||||
void ClientInterface::sendMessage(
|
||||
const QString &message,
|
||||
const QList<QString> &attachments,
|
||||
const QList<QString> &linkedFiles,
|
||||
bool useTools,
|
||||
bool useThinking)
|
||||
const QList<QString> &linkedFiles)
|
||||
{
|
||||
Q_UNUSED(useThinking)
|
||||
|
||||
if (message.trimmed().isEmpty() && attachments.isEmpty()) {
|
||||
LOG_MESSAGE("Ignoring empty chat message");
|
||||
return;
|
||||
@@ -256,7 +252,7 @@ void ClientInterface::sendMessage(
|
||||
}
|
||||
}
|
||||
|
||||
const LLMQore::RequestID requestId = session->send(std::move(blocks), useTools);
|
||||
const LLMQore::RequestID requestId = session->send(std::move(blocks));
|
||||
if (requestId.isEmpty()) {
|
||||
const QString error = QStringLiteral("Failed to start chat request for agent: %1")
|
||||
.arg(m_activeAgent);
|
||||
|
||||
@@ -41,9 +41,7 @@ public:
|
||||
void sendMessage(
|
||||
const QString &message,
|
||||
const QList<QString> &attachments = {},
|
||||
const QList<QString> &linkedFiles = {},
|
||||
bool useTools = false,
|
||||
bool useThinking = false);
|
||||
const QList<QString> &linkedFiles = {});
|
||||
void clearMessages();
|
||||
void cancelRequest();
|
||||
|
||||
|
||||
@@ -138,19 +138,6 @@ ChatRootView {
|
||||
relocateTooltip.text: (typeof _chatview !== 'undefined')
|
||||
? qsTr("Move this chat to an editor tab")
|
||||
: qsTr("Move this chat to a separate window")
|
||||
toolsButton {
|
||||
checked: root.useTools
|
||||
onCheckedChanged: {
|
||||
root.useTools = toolsButton.checked
|
||||
}
|
||||
}
|
||||
thinkingMode {
|
||||
checked: root.useThinking
|
||||
enabled: root.isThinkingSupport
|
||||
onCheckedChanged: {
|
||||
root.useThinking = thinkingMode.checked
|
||||
}
|
||||
}
|
||||
settingsButton.onClicked: root.openSettings()
|
||||
agentSelector {
|
||||
model: root.availableChatAgents
|
||||
|
||||
@@ -23,8 +23,6 @@ Rectangle {
|
||||
property alias pinButton: pinButtonId
|
||||
property alias relocateButton: relocateButtonId
|
||||
property alias contextButton: contextButtonId
|
||||
property alias toolsButton: toolsButtonId
|
||||
property alias thinkingMode: thinkingModeId
|
||||
property alias settingsButton: settingsButtonId
|
||||
property alias agentSelector: agentSelectorId
|
||||
property alias relocateTooltip: relocateTooltipId
|
||||
@@ -151,62 +149,6 @@ Rectangle {
|
||||
Row {
|
||||
spacing: 10
|
||||
|
||||
QoAButton {
|
||||
id: toolsButtonId
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
checkable: true
|
||||
opacity: enabled ? 1.0 : 0.2
|
||||
|
||||
icon {
|
||||
source: checked ? "qrc:/qt/qml/ChatView/icons/tools-icon-on.svg"
|
||||
: "qrc:/qt/qml/ChatView/icons/tools-icon-off.svg"
|
||||
color: palette.window.hslLightness > 0.5 ? "#000000" : "#FFFFFF"
|
||||
height: 15
|
||||
width: 15
|
||||
}
|
||||
|
||||
QoAToolTip {
|
||||
visible: toolsButtonId.hovered
|
||||
delay: 250
|
||||
text: {
|
||||
if (!toolsButtonId.enabled) {
|
||||
return qsTr("Tools are disabled in General Settings")
|
||||
}
|
||||
return toolsButtonId.checked
|
||||
? qsTr("Tools enabled: AI can use tools to read files, search project, and build code")
|
||||
: qsTr("Tools disabled: Simple conversation without tool access")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QoAButton {
|
||||
id: thinkingModeId
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
checkable: true
|
||||
opacity: enabled ? 1.0 : 0.2
|
||||
|
||||
icon {
|
||||
source: checked ? "qrc:/qt/qml/ChatView/icons/thinking-icon-on.svg"
|
||||
: "qrc:/qt/qml/ChatView/icons/thinking-icon-off.svg"
|
||||
color: palette.window.hslLightness > 0.5 ? "#000000" : "#FFFFFF"
|
||||
height: 15
|
||||
width: 15
|
||||
}
|
||||
|
||||
QoAToolTip {
|
||||
visible: thinkingModeId.hovered
|
||||
delay: 250
|
||||
text: thinkingModeId.enabled
|
||||
? (thinkingModeId.checked ? qsTr("Thinking Mode enabled (Check model list support it)")
|
||||
: qsTr("Thinking Mode disabled"))
|
||||
: qsTr("Thinking Mode is not available for this provider")
|
||||
}
|
||||
}
|
||||
|
||||
QoAButton {
|
||||
id: settingsButtonId
|
||||
|
||||
|
||||
@@ -153,7 +153,8 @@ LLMQore::RequestID Session::sendText(const QString &text)
|
||||
|
||||
LLMQore::RequestID Session::send(
|
||||
std::vector<std::unique_ptr<LLMQore::ContentBlock>> userBlocks,
|
||||
std::optional<bool> toolsOverride)
|
||||
std::optional<bool> toolsOverride,
|
||||
std::optional<bool> thinkingOverride)
|
||||
{
|
||||
if (!isValid() || userBlocks.empty())
|
||||
return {};
|
||||
@@ -168,7 +169,7 @@ LLMQore::RequestID Session::send(
|
||||
msg.appendBlock(std::move(b));
|
||||
m_history->append(std::move(msg));
|
||||
|
||||
return dispatch(toolsOverride);
|
||||
return dispatch(toolsOverride, thinkingOverride);
|
||||
}
|
||||
|
||||
void Session::cancel()
|
||||
@@ -221,7 +222,8 @@ LLMQore::RequestID Session::sendCompletion(Templates::ContextData ctx)
|
||||
return id;
|
||||
}
|
||||
|
||||
LLMQore::RequestID Session::dispatch(std::optional<bool> toolsOverride)
|
||||
LLMQore::RequestID Session::dispatch(
|
||||
std::optional<bool> toolsOverride, std::optional<bool> thinkingOverride)
|
||||
{
|
||||
auto *provider = m_agent->provider();
|
||||
auto *tmpl = m_agent->promptTemplate();
|
||||
@@ -237,7 +239,8 @@ LLMQore::RequestID Session::dispatch(std::optional<bool> toolsOverride)
|
||||
QJsonObject payload{{QStringLiteral("model"), cfg.model}};
|
||||
|
||||
const bool tools = toolsOverride.value_or(cfg.enableTools);
|
||||
if (!provider->prepareRequest(payload, tmpl, ctx, tools, cfg.enableThinking))
|
||||
const bool thinking = thinkingOverride.value_or(cfg.enableThinking);
|
||||
if (!provider->prepareRequest(payload, tmpl, ctx, tools, thinking))
|
||||
return {};
|
||||
|
||||
const auto id = provider->sendRequest(QUrl(provider->url()), payload, cfg.endpoint);
|
||||
@@ -285,6 +288,9 @@ Templates::ContextData Session::buildLegacyContext(
|
||||
QVector<LegacyMessage> hist;
|
||||
|
||||
for (const auto &m : history) {
|
||||
if (m.role() == Message::Role::System)
|
||||
continue;
|
||||
|
||||
QVector<ContentBlockEntry> blockEntries;
|
||||
|
||||
for (const auto &blockPtr : m.blocks()) {
|
||||
@@ -329,12 +335,17 @@ Templates::ContextData Session::buildLegacyContext(
|
||||
.arg(sa->fileName(), text);
|
||||
blockEntries.append(std::move(e));
|
||||
} else if (auto *th = dynamic_cast<LLMQore::ThinkingContent *>(block)) {
|
||||
// Claude rejects thinking blocks replayed without a signature.
|
||||
if (th->signature().isEmpty())
|
||||
continue;
|
||||
ContentBlockEntry e;
|
||||
e.kind = ContentBlockEntry::Kind::Thinking;
|
||||
e.thinking = th->thinking();
|
||||
e.signature = th->signature();
|
||||
blockEntries.append(std::move(e));
|
||||
} else if (auto *rth = dynamic_cast<LLMQore::RedactedThinkingContent *>(block)) {
|
||||
if (rth->signature().isEmpty())
|
||||
continue;
|
||||
ContentBlockEntry e;
|
||||
e.kind = ContentBlockEntry::Kind::RedactedThinking;
|
||||
e.signature = rth->signature();
|
||||
|
||||
@@ -64,7 +64,8 @@ public:
|
||||
|
||||
LLMQore::RequestID send(
|
||||
std::vector<std::unique_ptr<LLMQore::ContentBlock>> userBlocks,
|
||||
std::optional<bool> toolsOverride = std::nullopt);
|
||||
std::optional<bool> toolsOverride = std::nullopt,
|
||||
std::optional<bool> thinkingOverride = std::nullopt);
|
||||
|
||||
LLMQore::RequestID sendText(const QString &text);
|
||||
|
||||
@@ -83,7 +84,9 @@ private slots:
|
||||
void onRouterEvent(const QodeAssist::ResponseEvent &ev);
|
||||
|
||||
private:
|
||||
LLMQore::RequestID dispatch(std::optional<bool> toolsOverride = std::nullopt);
|
||||
LLMQore::RequestID dispatch(
|
||||
std::optional<bool> toolsOverride = std::nullopt,
|
||||
std::optional<bool> thinkingOverride = std::nullopt);
|
||||
Templates::ContextData toLegacyContext() const;
|
||||
|
||||
Agent *m_agent = nullptr; // child if non-null
|
||||
|
||||
@@ -5,5 +5,7 @@
|
||||
<file>ollama_gemma4_e4b_chat.toml</file>
|
||||
<file>ollama_codellama_7b_code_fim.toml</file>
|
||||
<file>ollama_codellama_13b_qml_fim.toml</file>
|
||||
<file>claude_sonnet_chat.toml</file>
|
||||
<file>google_gemini_chat.toml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
77
sources/agents/claude_sonnet_chat.toml
Normal file
77
sources/agents/claude_sonnet_chat.toml
Normal file
@@ -0,0 +1,77 @@
|
||||
schema_version = 1
|
||||
|
||||
name = "Claude Sonnet Chat"
|
||||
description = "Anthropic Claude (Messages API) — coding chat assistant via the hosted Claude provider."
|
||||
|
||||
provider_instance = "Claude"
|
||||
endpoint = "/v1/messages"
|
||||
|
||||
model = "claude-sonnet-4-6"
|
||||
|
||||
role = """
|
||||
You are a helpful coding assistant integrated into Qt Creator.
|
||||
Answer concisely. When the user shares code, prefer concrete diffs or
|
||||
minimal patches over rewriting whole files. Use markdown code blocks
|
||||
with language tags so the IDE can render them.
|
||||
"""
|
||||
|
||||
enable_thinking = true
|
||||
enable_tools = true
|
||||
|
||||
tags = ["chat", "claude", "anthropic", "cloud"]
|
||||
|
||||
context = """
|
||||
{%- set readme = read_file("${PROJECT_DIR}/README.md") -%}
|
||||
{%- if length(readme) > 0 %}
|
||||
## Project README.md
|
||||
{{ readme }}
|
||||
{%- endif %}
|
||||
"""
|
||||
|
||||
[template]
|
||||
message_format = """
|
||||
{
|
||||
{%- if existsIn(ctx, "system_prompt") %}
|
||||
"system": {{ tojson(ctx.system_prompt) }},
|
||||
{%- endif %}
|
||||
"messages": [
|
||||
{%- for msg in ctx.history %}
|
||||
{
|
||||
"role": {{ tojson(msg.role) }},
|
||||
"content": [
|
||||
{%- for b in msg.content_blocks %}
|
||||
{%- if b.type == "image" %}
|
||||
{
|
||||
"type": "image",
|
||||
"source": {
|
||||
{%- if b.is_url %}
|
||||
"type": "url",
|
||||
"url": {{ tojson(b.data) }}
|
||||
{%- else %}
|
||||
"type": "base64",
|
||||
"media_type": {{ tojson(b.media_type) }},
|
||||
"data": {{ tojson(b.data) }}
|
||||
{%- endif %}
|
||||
}
|
||||
}{% if not loop.is_last %},{% endif %}
|
||||
{%- else %}
|
||||
{{ tojson(b) }}{% if not loop.is_last %},{% endif %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
]
|
||||
}{% if not loop.is_last %},{% endif %}
|
||||
{%- endfor %}
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
[template.sampling]
|
||||
max_tokens = 8192
|
||||
temperature = 1
|
||||
|
||||
[template.thinking.overrides]
|
||||
temperature = 1
|
||||
|
||||
[template.thinking.request_block.thinking]
|
||||
type = "enabled"
|
||||
budget_tokens = 4096
|
||||
79
sources/agents/google_gemini_chat.toml
Normal file
79
sources/agents/google_gemini_chat.toml
Normal file
@@ -0,0 +1,79 @@
|
||||
schema_version = 1
|
||||
|
||||
name = "Gemini Chat"
|
||||
description = "Google Gemini (generateContent API) — coding chat assistant via the hosted Google AI provider."
|
||||
|
||||
provider_instance = "Google AI"
|
||||
endpoint = "/models/gemini-2.5-flash:streamGenerateContent?alt=sse"
|
||||
|
||||
model = "gemini-2.5-flash"
|
||||
|
||||
role = """
|
||||
You are a helpful coding assistant integrated into Qt Creator.
|
||||
Answer concisely. When the user shares code, prefer concrete diffs or
|
||||
minimal patches over rewriting whole files. Use markdown code blocks
|
||||
with language tags so the IDE can render them.
|
||||
"""
|
||||
|
||||
enable_thinking = true
|
||||
enable_tools = true
|
||||
|
||||
tags = ["chat", "gemini", "google", "cloud"]
|
||||
|
||||
context = """
|
||||
{%- set readme = read_file("${PROJECT_DIR}/README.md") -%}
|
||||
{%- if length(readme) > 0 %}
|
||||
## Project README.md
|
||||
{{ readme }}
|
||||
{%- endif %}
|
||||
"""
|
||||
|
||||
[template]
|
||||
message_format = """
|
||||
{
|
||||
{%- if existsIn(ctx, "system_prompt") %}
|
||||
"system_instruction": { "parts": [{ "text": {{ tojson(ctx.system_prompt) }} }] },
|
||||
{%- endif %}
|
||||
"contents": [
|
||||
{%- for msg in ctx.history %}
|
||||
{
|
||||
"role": {% if msg.role == "assistant" %}"model"{% else %}"user"{% endif %},
|
||||
"parts": [
|
||||
{%- for b in msg.content_blocks %}
|
||||
{%- if b.type == "text" %}
|
||||
{ "text": {{ tojson(b.text) }} }
|
||||
{%- else if b.type == "thinking" %}
|
||||
{ "text": {{ tojson(b.thinking) }}, "thought": true, "thoughtSignature": {{ tojson(b.signature) }} }
|
||||
{%- else if b.type == "tool_use" %}
|
||||
{ "functionCall": { "name": {{ tojson(b.name) }}, "args": {{ tojson(b.input) }} } }
|
||||
{%- else if b.type == "tool_result" %}
|
||||
{ "functionResponse": { "name": {{ tojson(b.name) }}, "response": { "result": {{ tojson(b.content) }} } } }
|
||||
{%- else if b.type == "image" %}
|
||||
{%- if b.is_url %}
|
||||
{ "file_data": { "mime_type": {{ tojson(b.media_type) }}, "file_uri": {{ tojson(b.data) }} } }
|
||||
{%- else %}
|
||||
{ "inline_data": { "mime_type": {{ tojson(b.media_type) }}, "data": {{ tojson(b.data) }} } }
|
||||
{%- endif %}
|
||||
{%- else %}
|
||||
{ "text": "" }
|
||||
{%- endif %}
|
||||
{% if not loop.is_last %},{% endif %}
|
||||
{%- endfor %}
|
||||
]
|
||||
}{% if not loop.is_last %},{% endif %}
|
||||
{%- endfor %}
|
||||
]
|
||||
}
|
||||
"""
|
||||
|
||||
[template.sampling.generationConfig]
|
||||
maxOutputTokens = 8192
|
||||
temperature = 1
|
||||
|
||||
[template.thinking.request_block.generationConfig]
|
||||
temperature = 1
|
||||
maxOutputTokens = 16000
|
||||
|
||||
[template.thinking.request_block.generationConfig.thinkingConfig]
|
||||
includeThoughts = true
|
||||
thinkingBudget = 8192
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <LLMQore/BaseClient.hpp>
|
||||
#include <LLMQore/ClaudeClient.hpp>
|
||||
#include <LLMQore/GoogleAIClient.hpp>
|
||||
@@ -58,6 +60,20 @@ QFuture<QList<QString>> GenericProvider::getInstalledModels(const QString &url)
|
||||
return m_client->listModels();
|
||||
}
|
||||
|
||||
RequestID GenericProvider::sendRequest(
|
||||
const QUrl &url, const QJsonObject &payload, const QString &endpoint)
|
||||
{
|
||||
// Gemini carries the model in the URL and rejects unknown body fields, so
|
||||
// the model/stream keys injected by the generic pipeline must be dropped.
|
||||
if (m_id == ProviderID::GoogleAI) {
|
||||
QJsonObject cleaned = payload;
|
||||
cleaned.remove("model");
|
||||
cleaned.remove("stream");
|
||||
return Provider::sendRequest(url, cleaned, endpoint);
|
||||
}
|
||||
return Provider::sendRequest(url, payload, endpoint);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using Cap = ProviderCapability;
|
||||
|
||||
@@ -36,6 +36,9 @@ public:
|
||||
ProviderCapabilities capabilities() const override;
|
||||
::LLMQore::BaseClient *client() const override;
|
||||
|
||||
RequestID sendRequest(
|
||||
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
ProviderID m_id;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "JsonPromptTemplate.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QHash>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
@@ -42,6 +43,16 @@ nlohmann::json buildContextJson(const ContextData &context)
|
||||
ctx["files_metadata"] = std::move(files);
|
||||
}
|
||||
|
||||
// tool_result blocks only carry the tool_use_id; resolve the originating
|
||||
// tool name so templates (e.g. Google's functionResponse.name) can emit it.
|
||||
QHash<QString, QString> toolNameById;
|
||||
if (context.history) {
|
||||
for (const auto &msg : context.history.value())
|
||||
for (const auto &b : msg.blocks)
|
||||
if (b.kind == ContentBlockEntry::Kind::ToolUse)
|
||||
toolNameById.insert(b.toolUseId, b.toolName);
|
||||
}
|
||||
|
||||
nlohmann::json history = nlohmann::json::array();
|
||||
if (context.history) {
|
||||
for (const auto &msg : context.history.value()) {
|
||||
@@ -93,6 +104,7 @@ nlohmann::json buildContextJson(const ContextData &context)
|
||||
bj["type"] = "tool_result";
|
||||
bj["tool_use_id"] = b.toolUseId.toStdString();
|
||||
bj["content"] = b.result.toStdString();
|
||||
bj["name"] = toolNameById.value(b.toolUseId).toStdString();
|
||||
break;
|
||||
case ContentBlockEntry::Kind::Image:
|
||||
bj["type"] = "image";
|
||||
|
||||
Reference in New Issue
Block a user