mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-06-15 02:39:19 -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;
|
std::vector<std::unique_ptr<LLMQore::ContentBlock>> blocks;
|
||||||
blocks.push_back(std::make_unique<LLMQore::TextContent>(buildCompressionPrompt()));
|
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()) {
|
if (m_currentRequestId.isEmpty()) {
|
||||||
handleCompressionError(tr("Failed to start compression request"));
|
handleCompressionError(tr("Failed to start compression request"));
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -167,6 +167,16 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
|||||||
&ChatAgentController::currentAgentChanged,
|
&ChatAgentController::currentAgentChanged,
|
||||||
this,
|
this,
|
||||||
&ChatRootView::isThinkingSupportChanged);
|
&ChatRootView::isThinkingSupportChanged);
|
||||||
|
connect(
|
||||||
|
m_agentController,
|
||||||
|
&ChatAgentController::currentAgentChanged,
|
||||||
|
this,
|
||||||
|
&ChatRootView::useToolsChanged);
|
||||||
|
connect(
|
||||||
|
m_agentController,
|
||||||
|
&ChatAgentController::currentAgentChanged,
|
||||||
|
this,
|
||||||
|
&ChatRootView::useThinkingChanged);
|
||||||
|
|
||||||
auto editors = Core::EditorManager::instance();
|
auto editors = Core::EditorManager::instance();
|
||||||
|
|
||||||
@@ -292,7 +302,7 @@ ChatRootView::ChatRootView(QQuickItem *parent)
|
|||||||
if (m_pendingSend.active) {
|
if (m_pendingSend.active) {
|
||||||
PendingSend p = m_pendingSend;
|
PendingSend p = m_pendingSend;
|
||||||
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) {
|
if (m_pendingSend.active) {
|
||||||
PendingSend p = m_pendingSend;
|
PendingSend p = m_pendingSend;
|
||||||
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 attachments = m_attachmentFiles;
|
||||||
const QStringList linkedFiles = m_linkedFiles;
|
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;
|
return;
|
||||||
|
|
||||||
dispatchSend(message, attachments, linkedFiles, tools, thinking);
|
dispatchSend(message, attachments, linkedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatRootView::deferSendForAutoCompress(
|
bool ChatRootView::deferSendForAutoCompress(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QStringList &attachments,
|
const QStringList &attachments,
|
||||||
const QStringList &linkedFiles,
|
const QStringList &linkedFiles)
|
||||||
bool useToolsArg,
|
|
||||||
bool useThinkingArg)
|
|
||||||
{
|
{
|
||||||
auto &settings = Settings::chatAssistantSettings();
|
auto &settings = Settings::chatAssistantSettings();
|
||||||
if (!settings.autoCompress())
|
if (!settings.autoCompress())
|
||||||
@@ -466,7 +472,7 @@ bool ChatRootView::deferSendForAutoCompress(
|
|||||||
.arg(inputTokens)
|
.arg(inputTokens)
|
||||||
.arg(threshold));
|
.arg(threshold));
|
||||||
|
|
||||||
m_pendingSend = {message, attachments, linkedFiles, useToolsArg, useThinkingArg, true};
|
m_pendingSend = {message, attachments, linkedFiles, true};
|
||||||
compressCurrentChat();
|
compressCurrentChat();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -474,9 +480,7 @@ bool ChatRootView::deferSendForAutoCompress(
|
|||||||
void ChatRootView::dispatchSend(
|
void ChatRootView::dispatchSend(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QStringList &attachments,
|
const QStringList &attachments,
|
||||||
const QStringList &linkedFiles,
|
const QStringList &linkedFiles)
|
||||||
bool useToolsArg,
|
|
||||||
bool useThinkingArg)
|
|
||||||
{
|
{
|
||||||
if (m_recentFilePath.isEmpty()) {
|
if (m_recentFilePath.isEmpty()) {
|
||||||
QString filePath = getAutosaveFilePath(message, attachments);
|
QString filePath = getAutosaveFilePath(message, attachments);
|
||||||
@@ -497,7 +501,7 @@ void ChatRootView::dispatchSend(
|
|||||||
m_clientInterface->setSkillsManager(skillsManager());
|
m_clientInterface->setSkillsManager(skillsManager());
|
||||||
m_clientInterface->setSessionManager(sessionManager());
|
m_clientInterface->setSessionManager(sessionManager());
|
||||||
m_clientInterface->setActiveAgent(currentChatAgent());
|
m_clientInterface->setActiveAgent(currentChatAgent());
|
||||||
m_clientInterface->sendMessage(message, attachments, linkedFiles, useToolsArg, useThinkingArg);
|
m_clientInterface->sendMessage(message, attachments, linkedFiles);
|
||||||
|
|
||||||
m_fileManager->clearIntermediateStorage();
|
m_fileManager->clearIntermediateStorage();
|
||||||
clearAttachmentFiles();
|
clearAttachmentFiles();
|
||||||
@@ -1112,24 +1116,12 @@ QString ChatRootView::lastErrorMessage() const
|
|||||||
|
|
||||||
bool ChatRootView::useTools() const
|
bool ChatRootView::useTools() const
|
||||||
{
|
{
|
||||||
return Settings::chatAssistantSettings().enableChatTools();
|
return m_agentController->currentSupportsTools();
|
||||||
}
|
|
||||||
|
|
||||||
void ChatRootView::setUseTools(bool enabled)
|
|
||||||
{
|
|
||||||
Settings::chatAssistantSettings().enableChatTools.setValue(enabled);
|
|
||||||
Settings::chatAssistantSettings().writeSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChatRootView::useThinking() const
|
bool ChatRootView::useThinking() const
|
||||||
{
|
{
|
||||||
return Settings::chatAssistantSettings().enableThinkingMode();
|
return m_agentController->currentSupportsThinking();
|
||||||
}
|
|
||||||
|
|
||||||
void ChatRootView::setUseThinking(bool enabled)
|
|
||||||
{
|
|
||||||
Settings::chatAssistantSettings().enableThinkingMode.setValue(enabled);
|
|
||||||
Settings::chatAssistantSettings().writeSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatRootView::applyFileEdit(const QString &editId)
|
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(bool isRequestInProgress READ isRequestInProgress NOTIFY isRequestInProgressChanged FINAL)
|
||||||
Q_PROPERTY(QString lastErrorMessage READ lastErrorMessage NOTIFY lastErrorMessageChanged FINAL)
|
Q_PROPERTY(QString lastErrorMessage READ lastErrorMessage NOTIFY lastErrorMessageChanged FINAL)
|
||||||
Q_PROPERTY(QString lastInfoMessage READ lastInfoMessage NOTIFY lastInfoMessageChanged FINAL)
|
Q_PROPERTY(QString lastInfoMessage READ lastInfoMessage NOTIFY lastInfoMessageChanged FINAL)
|
||||||
Q_PROPERTY(bool useTools READ useTools WRITE setUseTools NOTIFY useToolsChanged FINAL)
|
Q_PROPERTY(bool useTools READ useTools NOTIFY useToolsChanged FINAL)
|
||||||
Q_PROPERTY(bool useThinking READ useThinking WRITE setUseThinking NOTIFY useThinkingChanged FINAL)
|
Q_PROPERTY(bool useThinking READ useThinking NOTIFY useThinkingChanged FINAL)
|
||||||
Q_PROPERTY(QString sendShortcutText READ sendShortcutText NOTIFY sendShortcutTextChanged FINAL)
|
Q_PROPERTY(QString sendShortcutText READ sendShortcutText NOTIFY sendShortcutTextChanged FINAL)
|
||||||
|
|
||||||
Q_PROPERTY(int currentMessageTotalEdits READ currentMessageTotalEdits NOTIFY currentMessageEditsStatsChanged FINAL)
|
Q_PROPERTY(int currentMessageTotalEdits READ currentMessageTotalEdits NOTIFY currentMessageEditsStatsChanged FINAL)
|
||||||
@@ -136,9 +136,7 @@ public:
|
|||||||
Q_INVOKABLE QVariantList searchSkills(const QString &query) const;
|
Q_INVOKABLE QVariantList searchSkills(const QString &query) const;
|
||||||
|
|
||||||
bool useTools() const;
|
bool useTools() const;
|
||||||
void setUseTools(bool enabled);
|
|
||||||
bool useThinking() const;
|
bool useThinking() const;
|
||||||
void setUseThinking(bool enabled);
|
|
||||||
|
|
||||||
Q_INVOKABLE void applyFileEdit(const QString &editId);
|
Q_INVOKABLE void applyFileEdit(const QString &editId);
|
||||||
Q_INVOKABLE void rejectFileEdit(const QString &editId);
|
Q_INVOKABLE void rejectFileEdit(const QString &editId);
|
||||||
@@ -229,15 +227,11 @@ private:
|
|||||||
bool deferSendForAutoCompress(
|
bool deferSendForAutoCompress(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QStringList &attachments,
|
const QStringList &attachments,
|
||||||
const QStringList &linkedFiles,
|
const QStringList &linkedFiles);
|
||||||
bool useTools,
|
|
||||||
bool useThinking);
|
|
||||||
void dispatchSend(
|
void dispatchSend(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QStringList &attachments,
|
const QStringList &attachments,
|
||||||
const QStringList &linkedFiles,
|
const QStringList &linkedFiles);
|
||||||
bool useTools,
|
|
||||||
bool useThinking);
|
|
||||||
bool hasImageAttachments(const QStringList &attachments) const;
|
bool hasImageAttachments(const QStringList &attachments) const;
|
||||||
|
|
||||||
SessionFileRegistry *sessionFileRegistry() const;
|
SessionFileRegistry *sessionFileRegistry() const;
|
||||||
@@ -256,8 +250,6 @@ private:
|
|||||||
QString message;
|
QString message;
|
||||||
QStringList attachments;
|
QStringList attachments;
|
||||||
QStringList linkedFiles;
|
QStringList linkedFiles;
|
||||||
bool useTools = false;
|
|
||||||
bool useThinking = false;
|
|
||||||
bool active = false;
|
bool active = false;
|
||||||
};
|
};
|
||||||
PendingSend m_pendingSend;
|
PendingSend m_pendingSend;
|
||||||
|
|||||||
@@ -78,12 +78,8 @@ void ClientInterface::setActiveAgent(const QString &agentName)
|
|||||||
void ClientInterface::sendMessage(
|
void ClientInterface::sendMessage(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QList<QString> &attachments,
|
const QList<QString> &attachments,
|
||||||
const QList<QString> &linkedFiles,
|
const QList<QString> &linkedFiles)
|
||||||
bool useTools,
|
|
||||||
bool useThinking)
|
|
||||||
{
|
{
|
||||||
Q_UNUSED(useThinking)
|
|
||||||
|
|
||||||
if (message.trimmed().isEmpty() && attachments.isEmpty()) {
|
if (message.trimmed().isEmpty() && attachments.isEmpty()) {
|
||||||
LOG_MESSAGE("Ignoring empty chat message");
|
LOG_MESSAGE("Ignoring empty chat message");
|
||||||
return;
|
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()) {
|
if (requestId.isEmpty()) {
|
||||||
const QString error = QStringLiteral("Failed to start chat request for agent: %1")
|
const QString error = QStringLiteral("Failed to start chat request for agent: %1")
|
||||||
.arg(m_activeAgent);
|
.arg(m_activeAgent);
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ public:
|
|||||||
void sendMessage(
|
void sendMessage(
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QList<QString> &attachments = {},
|
const QList<QString> &attachments = {},
|
||||||
const QList<QString> &linkedFiles = {},
|
const QList<QString> &linkedFiles = {});
|
||||||
bool useTools = false,
|
|
||||||
bool useThinking = false);
|
|
||||||
void clearMessages();
|
void clearMessages();
|
||||||
void cancelRequest();
|
void cancelRequest();
|
||||||
|
|
||||||
|
|||||||
@@ -138,19 +138,6 @@ ChatRootView {
|
|||||||
relocateTooltip.text: (typeof _chatview !== 'undefined')
|
relocateTooltip.text: (typeof _chatview !== 'undefined')
|
||||||
? qsTr("Move this chat to an editor tab")
|
? qsTr("Move this chat to an editor tab")
|
||||||
: qsTr("Move this chat to a separate window")
|
: 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()
|
settingsButton.onClicked: root.openSettings()
|
||||||
agentSelector {
|
agentSelector {
|
||||||
model: root.availableChatAgents
|
model: root.availableChatAgents
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ Rectangle {
|
|||||||
property alias pinButton: pinButtonId
|
property alias pinButton: pinButtonId
|
||||||
property alias relocateButton: relocateButtonId
|
property alias relocateButton: relocateButtonId
|
||||||
property alias contextButton: contextButtonId
|
property alias contextButton: contextButtonId
|
||||||
property alias toolsButton: toolsButtonId
|
|
||||||
property alias thinkingMode: thinkingModeId
|
|
||||||
property alias settingsButton: settingsButtonId
|
property alias settingsButton: settingsButtonId
|
||||||
property alias agentSelector: agentSelectorId
|
property alias agentSelector: agentSelectorId
|
||||||
property alias relocateTooltip: relocateTooltipId
|
property alias relocateTooltip: relocateTooltipId
|
||||||
@@ -151,62 +149,6 @@ Rectangle {
|
|||||||
Row {
|
Row {
|
||||||
spacing: 10
|
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 {
|
QoAButton {
|
||||||
id: settingsButtonId
|
id: settingsButtonId
|
||||||
|
|
||||||
|
|||||||
@@ -153,7 +153,8 @@ LLMQore::RequestID Session::sendText(const QString &text)
|
|||||||
|
|
||||||
LLMQore::RequestID Session::send(
|
LLMQore::RequestID Session::send(
|
||||||
std::vector<std::unique_ptr<LLMQore::ContentBlock>> userBlocks,
|
std::vector<std::unique_ptr<LLMQore::ContentBlock>> userBlocks,
|
||||||
std::optional<bool> toolsOverride)
|
std::optional<bool> toolsOverride,
|
||||||
|
std::optional<bool> thinkingOverride)
|
||||||
{
|
{
|
||||||
if (!isValid() || userBlocks.empty())
|
if (!isValid() || userBlocks.empty())
|
||||||
return {};
|
return {};
|
||||||
@@ -168,7 +169,7 @@ LLMQore::RequestID Session::send(
|
|||||||
msg.appendBlock(std::move(b));
|
msg.appendBlock(std::move(b));
|
||||||
m_history->append(std::move(msg));
|
m_history->append(std::move(msg));
|
||||||
|
|
||||||
return dispatch(toolsOverride);
|
return dispatch(toolsOverride, thinkingOverride);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::cancel()
|
void Session::cancel()
|
||||||
@@ -221,7 +222,8 @@ LLMQore::RequestID Session::sendCompletion(Templates::ContextData ctx)
|
|||||||
return id;
|
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 *provider = m_agent->provider();
|
||||||
auto *tmpl = m_agent->promptTemplate();
|
auto *tmpl = m_agent->promptTemplate();
|
||||||
@@ -237,7 +239,8 @@ LLMQore::RequestID Session::dispatch(std::optional<bool> toolsOverride)
|
|||||||
QJsonObject payload{{QStringLiteral("model"), cfg.model}};
|
QJsonObject payload{{QStringLiteral("model"), cfg.model}};
|
||||||
|
|
||||||
const bool tools = toolsOverride.value_or(cfg.enableTools);
|
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 {};
|
return {};
|
||||||
|
|
||||||
const auto id = provider->sendRequest(QUrl(provider->url()), payload, cfg.endpoint);
|
const auto id = provider->sendRequest(QUrl(provider->url()), payload, cfg.endpoint);
|
||||||
@@ -285,6 +288,9 @@ Templates::ContextData Session::buildLegacyContext(
|
|||||||
QVector<LegacyMessage> hist;
|
QVector<LegacyMessage> hist;
|
||||||
|
|
||||||
for (const auto &m : history) {
|
for (const auto &m : history) {
|
||||||
|
if (m.role() == Message::Role::System)
|
||||||
|
continue;
|
||||||
|
|
||||||
QVector<ContentBlockEntry> blockEntries;
|
QVector<ContentBlockEntry> blockEntries;
|
||||||
|
|
||||||
for (const auto &blockPtr : m.blocks()) {
|
for (const auto &blockPtr : m.blocks()) {
|
||||||
@@ -329,12 +335,17 @@ Templates::ContextData Session::buildLegacyContext(
|
|||||||
.arg(sa->fileName(), text);
|
.arg(sa->fileName(), text);
|
||||||
blockEntries.append(std::move(e));
|
blockEntries.append(std::move(e));
|
||||||
} else if (auto *th = dynamic_cast<LLMQore::ThinkingContent *>(block)) {
|
} else if (auto *th = dynamic_cast<LLMQore::ThinkingContent *>(block)) {
|
||||||
|
// Claude rejects thinking blocks replayed without a signature.
|
||||||
|
if (th->signature().isEmpty())
|
||||||
|
continue;
|
||||||
ContentBlockEntry e;
|
ContentBlockEntry e;
|
||||||
e.kind = ContentBlockEntry::Kind::Thinking;
|
e.kind = ContentBlockEntry::Kind::Thinking;
|
||||||
e.thinking = th->thinking();
|
e.thinking = th->thinking();
|
||||||
e.signature = th->signature();
|
e.signature = th->signature();
|
||||||
blockEntries.append(std::move(e));
|
blockEntries.append(std::move(e));
|
||||||
} else if (auto *rth = dynamic_cast<LLMQore::RedactedThinkingContent *>(block)) {
|
} else if (auto *rth = dynamic_cast<LLMQore::RedactedThinkingContent *>(block)) {
|
||||||
|
if (rth->signature().isEmpty())
|
||||||
|
continue;
|
||||||
ContentBlockEntry e;
|
ContentBlockEntry e;
|
||||||
e.kind = ContentBlockEntry::Kind::RedactedThinking;
|
e.kind = ContentBlockEntry::Kind::RedactedThinking;
|
||||||
e.signature = rth->signature();
|
e.signature = rth->signature();
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ public:
|
|||||||
|
|
||||||
LLMQore::RequestID send(
|
LLMQore::RequestID send(
|
||||||
std::vector<std::unique_ptr<LLMQore::ContentBlock>> userBlocks,
|
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);
|
LLMQore::RequestID sendText(const QString &text);
|
||||||
|
|
||||||
@@ -83,7 +84,9 @@ private slots:
|
|||||||
void onRouterEvent(const QodeAssist::ResponseEvent &ev);
|
void onRouterEvent(const QodeAssist::ResponseEvent &ev);
|
||||||
|
|
||||||
private:
|
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;
|
Templates::ContextData toLegacyContext() const;
|
||||||
|
|
||||||
Agent *m_agent = nullptr; // child if non-null
|
Agent *m_agent = nullptr; // child if non-null
|
||||||
|
|||||||
@@ -5,5 +5,7 @@
|
|||||||
<file>ollama_gemma4_e4b_chat.toml</file>
|
<file>ollama_gemma4_e4b_chat.toml</file>
|
||||||
<file>ollama_codellama_7b_code_fim.toml</file>
|
<file>ollama_codellama_7b_code_fim.toml</file>
|
||||||
<file>ollama_codellama_13b_qml_fim.toml</file>
|
<file>ollama_codellama_13b_qml_fim.toml</file>
|
||||||
|
<file>claude_sonnet_chat.toml</file>
|
||||||
|
<file>google_gemini_chat.toml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</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 <utility>
|
||||||
|
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include <LLMQore/BaseClient.hpp>
|
#include <LLMQore/BaseClient.hpp>
|
||||||
#include <LLMQore/ClaudeClient.hpp>
|
#include <LLMQore/ClaudeClient.hpp>
|
||||||
#include <LLMQore/GoogleAIClient.hpp>
|
#include <LLMQore/GoogleAIClient.hpp>
|
||||||
@@ -58,6 +60,20 @@ QFuture<QList<QString>> GenericProvider::getInstalledModels(const QString &url)
|
|||||||
return m_client->listModels();
|
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 {
|
namespace {
|
||||||
|
|
||||||
using Cap = ProviderCapability;
|
using Cap = ProviderCapability;
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ public:
|
|||||||
ProviderCapabilities capabilities() const override;
|
ProviderCapabilities capabilities() const override;
|
||||||
::LLMQore::BaseClient *client() const override;
|
::LLMQore::BaseClient *client() const override;
|
||||||
|
|
||||||
|
RequestID sendRequest(
|
||||||
|
const QUrl &url, const QJsonObject &payload, const QString &endpoint) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_name;
|
QString m_name;
|
||||||
ProviderID m_id;
|
ProviderID m_id;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "JsonPromptTemplate.hpp"
|
#include "JsonPromptTemplate.hpp"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QHash>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
|
||||||
@@ -42,6 +43,16 @@ nlohmann::json buildContextJson(const ContextData &context)
|
|||||||
ctx["files_metadata"] = std::move(files);
|
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();
|
nlohmann::json history = nlohmann::json::array();
|
||||||
if (context.history) {
|
if (context.history) {
|
||||||
for (const auto &msg : context.history.value()) {
|
for (const auto &msg : context.history.value()) {
|
||||||
@@ -93,6 +104,7 @@ nlohmann::json buildContextJson(const ContextData &context)
|
|||||||
bj["type"] = "tool_result";
|
bj["type"] = "tool_result";
|
||||||
bj["tool_use_id"] = b.toolUseId.toStdString();
|
bj["tool_use_id"] = b.toolUseId.toStdString();
|
||||||
bj["content"] = b.result.toStdString();
|
bj["content"] = b.result.toStdString();
|
||||||
|
bj["name"] = toolNameById.value(b.toolUseId).toStdString();
|
||||||
break;
|
break;
|
||||||
case ContentBlockEntry::Kind::Image:
|
case ContentBlockEntry::Kind::Image:
|
||||||
bj["type"] = "image";
|
bj["type"] = "image";
|
||||||
|
|||||||
Reference in New Issue
Block a user