From 69d9af1a973672296b7d2e2d2c1113e106c9c0af Mon Sep 17 00:00:00 2001 From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com> Date: Sat, 11 Oct 2025 19:46:27 +0200 Subject: [PATCH] feat: Add tooling support to google provider (#237) --- CMakeLists.txt | 1 + llmcore/BaseTool.cpp | 16 ++ llmcore/BaseTool.hpp | 3 +- providers/GoogleAIProvider.cpp | 283 +++++++++++++++++++++------- providers/GoogleAIProvider.hpp | 21 ++- providers/GoogleMessage.cpp | 173 +++++++++++++++++ providers/GoogleMessage.hpp | 62 ++++++ tools/ListProjectFilesTool.cpp | 2 + tools/ReadProjectFileByNameTool.cpp | 2 + tools/ReadVisibleFilesTool.cpp | 2 + 10 files changed, 500 insertions(+), 65 deletions(-) create mode 100644 providers/GoogleMessage.cpp create mode 100644 providers/GoogleMessage.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e21e5f..83b40e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,7 @@ add_qtc_plugin(QodeAssist providers/ClaudeMessage.hpp providers/ClaudeMessage.cpp providers/OpenAIMessage.hpp providers/OpenAIMessage.cpp providers/OllamaMessage.hpp providers/OllamaMessage.cpp + providers/GoogleMessage.hpp providers/GoogleMessage.cpp ) get_target_property(QtCreatorCorePath QtCreator::Core LOCATION) diff --git a/llmcore/BaseTool.cpp b/llmcore/BaseTool.cpp index 1f7c559..e1594af 100644 --- a/llmcore/BaseTool.cpp +++ b/llmcore/BaseTool.cpp @@ -54,4 +54,20 @@ QJsonObject BaseTool::customizeForOllama(const QJsonObject &baseDefinition) cons return customizeForOpenAI(baseDefinition); } +QJsonObject BaseTool::customizeForGoogle(const QJsonObject &baseDefinition) const +{ + QJsonObject functionDeclaration; + functionDeclaration["name"] = name(); + functionDeclaration["description"] = description(); + functionDeclaration["parameters"] = baseDefinition; + + QJsonArray functionDeclarations; + functionDeclarations.append(functionDeclaration); + + QJsonObject tool; + tool["function_declarations"] = functionDeclarations; + + return tool; +} + } // namespace QodeAssist::LLMCore diff --git a/llmcore/BaseTool.hpp b/llmcore/BaseTool.hpp index 3320695..f0c3cd8 100644 --- a/llmcore/BaseTool.hpp +++ b/llmcore/BaseTool.hpp @@ -27,7 +27,7 @@ namespace QodeAssist::LLMCore { -enum class ToolSchemaFormat { OpenAI, Claude, Ollama }; +enum class ToolSchemaFormat { OpenAI, Claude, Ollama, Google }; class BaseTool : public QObject { @@ -47,6 +47,7 @@ protected: virtual QJsonObject customizeForOpenAI(const QJsonObject &baseDefinition) const; virtual QJsonObject customizeForClaude(const QJsonObject &baseDefinition) const; virtual QJsonObject customizeForOllama(const QJsonObject &baseDefinition) const; + virtual QJsonObject customizeForGoogle(const QJsonObject &baseDefinition) const; }; } // namespace QodeAssist::LLMCore diff --git a/providers/GoogleAIProvider.cpp b/providers/GoogleAIProvider.cpp index 00018cb..e696e45 100644 --- a/providers/GoogleAIProvider.cpp +++ b/providers/GoogleAIProvider.cpp @@ -34,6 +34,17 @@ namespace QodeAssist::Providers { +GoogleAIProvider::GoogleAIProvider(QObject *parent) + : LLMCore::Provider(parent) + , m_toolsManager(new Tools::ToolsManager(this)) +{ + connect( + m_toolsManager, + &Tools::ToolsManager::toolExecutionComplete, + this, + &GoogleAIProvider::onToolExecutionComplete); +} + QString GoogleAIProvider::name() const { return "Google AI"; @@ -89,6 +100,16 @@ void GoogleAIProvider::prepareRequest( } else { applyModelParams(Settings::chatAssistantSettings()); } + + if (supportsTools() && type == LLMCore::RequestType::Chat + && Settings::chatAssistantSettings().useTools()) { + auto toolsDefinitions = m_toolsManager->getToolsDefinitions( + LLMCore::ToolSchemaFormat::Google); + if (!toolsDefinitions.isEmpty()) { + request["tools"] = toolsDefinitions; + LOG_MESSAGE(QString("Added %1 tools to Google AI request").arg(toolsDefinitions.size())); + } + } } QList GoogleAIProvider::getInstalledModels(const QString &url) @@ -143,7 +164,8 @@ QList GoogleAIProvider::validateRequest( {"system_instruction", QJsonArray{}}, {"generationConfig", QJsonObject{{"temperature", {}}, {"maxOutputTokens", {}}, {"topP", {}}, {"topK", {}}}}, - {"safetySettings", QJsonArray{}}}; + {"safetySettings", QJsonArray{}}, + {"tools", QJsonArray{}}}; return LLMCore::ValidationUtils::validateRequestFields(request, templateReq); } @@ -172,8 +194,12 @@ LLMCore::ProviderID GoogleAIProvider::providerID() const void GoogleAIProvider::sendRequest( const LLMCore::RequestID &requestId, const QUrl &url, const QJsonObject &payload) { - m_dataBuffers[requestId].clear(); + if (!m_messages.contains(requestId)) { + m_dataBuffers[requestId].clear(); + } + m_requestUrls[requestId] = url; + m_originalRequests[requestId] = payload; QNetworkRequest networkRequest(url); prepareNetworkRequest(networkRequest); @@ -187,6 +213,18 @@ void GoogleAIProvider::sendRequest( emit httpClient()->sendRequest(request); } +bool GoogleAIProvider::supportsTools() const +{ + return true; +} + +void GoogleAIProvider::cancelRequest(const LLMCore::RequestID &requestId) +{ + LOG_MESSAGE(QString("GoogleAIProvider: Cancelling request %1").arg(requestId)); + LLMCore::Provider::cancelRequest(requestId); + cleanupRequest(requestId); +} + void GoogleAIProvider::onDataReceived( const QodeAssist::LLMCore::RequestID &requestId, const QByteArray &data) { @@ -207,17 +245,24 @@ void GoogleAIProvider::onDataReceived( LOG_MESSAGE(fullError); emit requestFailed(requestId, fullError); - m_dataBuffers.remove(requestId); + cleanupRequest(requestId); return; } } - bool isDone = handleStreamResponse(requestId, data); + LLMCore::DataBuffers &buffers = m_dataBuffers[requestId]; + QStringList lines = buffers.rawStreamBuffer.processData(data); - if (isDone) { - LLMCore::DataBuffers &buffers = m_dataBuffers[requestId]; - emit fullResponseReceived(requestId, buffers.responseContent); - m_dataBuffers.remove(requestId); + for (const QString &line : lines) { + if (line.trimmed().isEmpty()) { + continue; + } + + QJsonObject chunk = parseEventLine(line); + if (chunk.isEmpty()) + continue; + + processStreamChunk(requestId, chunk); } } @@ -227,67 +272,179 @@ void GoogleAIProvider::onRequestFinished( if (!success) { LOG_MESSAGE(QString("GoogleAIProvider request %1 failed: %2").arg(requestId, error)); emit requestFailed(requestId, error); - } else { - if (m_dataBuffers.contains(requestId)) { - const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId]; - if (!buffers.responseContent.isEmpty()) { - emit fullResponseReceived(requestId, buffers.responseContent); + cleanupRequest(requestId); + return; + } + + if (m_messages.contains(requestId)) { + GoogleMessage *message = m_messages[requestId]; + if (message->state() == LLMCore::MessageState::RequiresToolExecution) { + LOG_MESSAGE(QString("Waiting for tools to complete for %1").arg(requestId)); + m_dataBuffers.remove(requestId); + return; + } + } + + if (m_dataBuffers.contains(requestId)) { + const LLMCore::DataBuffers &buffers = m_dataBuffers[requestId]; + if (!buffers.responseContent.isEmpty()) { + LOG_MESSAGE(QString("Emitting full response for %1").arg(requestId)); + emit fullResponseReceived(requestId, buffers.responseContent); + } + } + + cleanupRequest(requestId); +} + +void GoogleAIProvider::onToolExecutionComplete( + const QString &requestId, const QHash &toolResults) +{ + if (!m_messages.contains(requestId) || !m_requestUrls.contains(requestId)) { + LOG_MESSAGE(QString("ERROR: Missing data for continuation request %1").arg(requestId)); + cleanupRequest(requestId); + return; + } + + LOG_MESSAGE(QString("Tool execution complete for Google AI request %1").arg(requestId)); + + for (auto it = toolResults.begin(); it != toolResults.end(); ++it) { + GoogleMessage *message = m_messages[requestId]; + auto toolContent = message->getCurrentToolUseContent(); + for (auto tool : toolContent) { + if (tool->id() == it.key()) { + auto toolStringName = m_toolsManager->toolsFactory()->getStringName(tool->name()); + emit toolExecutionCompleted( + requestId, tool->id(), toolStringName, toolResults[tool->id()]); + break; } } } + GoogleMessage *message = m_messages[requestId]; + QJsonObject continuationRequest = m_originalRequests[requestId]; + QJsonArray contents = continuationRequest["contents"].toArray(); + + contents.append(message->toProviderFormat()); + + QJsonObject userMessage; + userMessage["role"] = "user"; + userMessage["parts"] = message->createToolResultParts(toolResults); + contents.append(userMessage); + + continuationRequest["contents"] = contents; + + LOG_MESSAGE(QString("Sending continuation request for %1 with %2 tool results") + .arg(requestId) + .arg(toolResults.size())); + + sendRequest(requestId, m_requestUrls[requestId], continuationRequest); +} + +void GoogleAIProvider::processStreamChunk(const QString &requestId, const QJsonObject &chunk) +{ + if (!chunk.contains("candidates")) { + return; + } + + GoogleMessage *message = m_messages.value(requestId); + if (!message) { + message = new GoogleMessage(this); + m_messages[requestId] = message; + LOG_MESSAGE(QString("Created NEW GoogleMessage for request %1").arg(requestId)); + + if (m_dataBuffers.contains(requestId)) { + emit continuationStarted(requestId); + LOG_MESSAGE(QString("Starting continuation for request %1").arg(requestId)); + } + } else if ( + m_dataBuffers.contains(requestId) + && message->state() == LLMCore::MessageState::RequiresToolExecution) { + message->startNewContinuation(); + LOG_MESSAGE(QString("Cleared message state for continuation request %1").arg(requestId)); + } + + QJsonArray candidates = chunk["candidates"].toArray(); + for (const QJsonValue &candidate : candidates) { + QJsonObject candidateObj = candidate.toObject(); + + if (candidateObj.contains("content")) { + QJsonObject content = candidateObj["content"].toObject(); + if (content.contains("parts")) { + QJsonArray parts = content["parts"].toArray(); + for (const QJsonValue &part : parts) { + QJsonObject partObj = part.toObject(); + + if (partObj.contains("text")) { + QString text = partObj["text"].toString(); + message->handleContentDelta(text); + + LLMCore::DataBuffers &buffers = m_dataBuffers[requestId]; + buffers.responseContent += text; + emit partialResponseReceived(requestId, text); + } else if (partObj.contains("functionCall")) { + QJsonObject functionCall = partObj["functionCall"].toObject(); + QString name = functionCall["name"].toString(); + QJsonObject args = functionCall["args"].toObject(); + + message->handleFunctionCallStart(name); + message->handleFunctionCallArgsDelta( + QString::fromUtf8(QJsonDocument(args).toJson(QJsonDocument::Compact))); + message->handleFunctionCallComplete(); + } + } + } + } + + if (candidateObj.contains("finishReason")) { + QString finishReason = candidateObj["finishReason"].toString(); + message->handleFinishReason(finishReason); + handleMessageComplete(requestId); + } + } +} + +void GoogleAIProvider::handleMessageComplete(const QString &requestId) +{ + if (!m_messages.contains(requestId)) + return; + + GoogleMessage *message = m_messages[requestId]; + + if (message->state() == LLMCore::MessageState::RequiresToolExecution) { + LOG_MESSAGE(QString("Google AI message requires tool execution for %1").arg(requestId)); + + auto toolUseContent = message->getCurrentToolUseContent(); + + if (toolUseContent.isEmpty()) { + LOG_MESSAGE(QString("No tools to execute for %1").arg(requestId)); + return; + } + + for (auto toolContent : toolUseContent) { + auto toolStringName = m_toolsManager->toolsFactory()->getStringName(toolContent->name()); + emit toolExecutionStarted(requestId, toolContent->id(), toolStringName); + m_toolsManager->executeToolCall( + requestId, toolContent->id(), toolContent->name(), toolContent->input()); + } + + } else { + LOG_MESSAGE(QString("Google AI message marked as complete for %1").arg(requestId)); + } +} + +void GoogleAIProvider::cleanupRequest(const LLMCore::RequestID &requestId) +{ + LOG_MESSAGE(QString("Cleaning up Google AI request %1").arg(requestId)); + + if (m_messages.contains(requestId)) { + GoogleMessage *message = m_messages.take(requestId); + message->deleteLater(); + } + m_dataBuffers.remove(requestId); m_requestUrls.remove(requestId); -} - -bool GoogleAIProvider::handleStreamResponse( - const LLMCore::RequestID &requestId, const QByteArray &data) -{ - LLMCore::DataBuffers &buffers = m_dataBuffers[requestId]; - QStringList lines = buffers.rawStreamBuffer.processData(data); - - bool isDone = false; - QString tempResponse; - - for (const QString &line : lines) { - if (line.trimmed().isEmpty()) { - continue; - } - - QJsonObject responseObj = parseEventLine(line); - if (responseObj.isEmpty()) - continue; - - if (responseObj.contains("candidates")) { - QJsonArray candidates = responseObj["candidates"].toArray(); - for (const QJsonValue &candidate : candidates) { - QJsonObject candidateObj = candidate.toObject(); - if (candidateObj.contains("content")) { - QJsonObject content = candidateObj["content"].toObject(); - if (content.contains("parts")) { - QJsonArray parts = content["parts"].toArray(); - for (const QJsonValue &part : parts) { - QJsonObject partObj = part.toObject(); - if (partObj.contains("text")) { - tempResponse += partObj["text"].toString(); - } - } - } - } - - if (candidateObj.contains("finishReason")) { - isDone = true; - } - } - } - } - - if (!tempResponse.isEmpty()) { - buffers.responseContent += tempResponse; - emit partialResponseReceived(requestId, tempResponse); - } - - return isDone; + m_originalRequests.remove(requestId); + m_toolsManager->cleanupRequest(requestId); } } // namespace QodeAssist::Providers diff --git a/providers/GoogleAIProvider.hpp b/providers/GoogleAIProvider.hpp index 25ad5a0..5f0c157 100644 --- a/providers/GoogleAIProvider.hpp +++ b/providers/GoogleAIProvider.hpp @@ -19,13 +19,18 @@ #pragma once +#include "GoogleMessage.hpp" #include "llmcore/Provider.hpp" +#include "tools/ToolsManager.hpp" namespace QodeAssist::Providers { class GoogleAIProvider : public LLMCore::Provider { + Q_OBJECT public: + explicit GoogleAIProvider(QObject *parent = nullptr); + QString name() const override; QString url() const override; QString completionEndpoint() const override; @@ -45,6 +50,9 @@ public: void sendRequest( const LLMCore::RequestID &requestId, const QUrl &url, const QJsonObject &payload) override; + bool supportsTools() const override; + void cancelRequest(const LLMCore::RequestID &requestId) override; + public slots: void onDataReceived( const QodeAssist::LLMCore::RequestID &requestId, const QByteArray &data) override; @@ -53,8 +61,19 @@ public slots: bool success, const QString &error) override; +private slots: + void onToolExecutionComplete( + const QString &requestId, const QHash &toolResults); + private: - bool handleStreamResponse(const LLMCore::RequestID &requestId, const QByteArray &data); + void processStreamChunk(const QString &requestId, const QJsonObject &chunk); + void handleMessageComplete(const QString &requestId); + void cleanupRequest(const LLMCore::RequestID &requestId); + + QHash m_messages; + QHash m_requestUrls; + QHash m_originalRequests; + Tools::ToolsManager *m_toolsManager; }; } // namespace QodeAssist::Providers diff --git a/providers/GoogleMessage.cpp b/providers/GoogleMessage.cpp new file mode 100644 index 0000000..1d5117d --- /dev/null +++ b/providers/GoogleMessage.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2025 Petr Mironychev + * + * This file is part of QodeAssist. + * + * QodeAssist is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QodeAssist is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QodeAssist. If not, see . + */ + +#include "GoogleMessage.hpp" + +#include +#include + +#include "logger/Logger.hpp" + +namespace QodeAssist::Providers { + +GoogleMessage::GoogleMessage(QObject *parent) + : QObject(parent) +{} + +void GoogleMessage::handleContentDelta(const QString &text) +{ + if (m_currentBlocks.isEmpty() || !qobject_cast(m_currentBlocks.last())) { + auto textContent = new LLMCore::TextContent(); + textContent->setParent(this); + m_currentBlocks.append(textContent); + } + + if (auto textContent = qobject_cast(m_currentBlocks.last())) { + textContent->appendText(text); + } +} + +void GoogleMessage::handleFunctionCallStart(const QString &name) +{ + m_currentFunctionName = name; + m_pendingFunctionArgs.clear(); + + LOG_MESSAGE(QString("Google: Starting function call: %1").arg(name)); +} + +void GoogleMessage::handleFunctionCallArgsDelta(const QString &argsJson) +{ + m_pendingFunctionArgs += argsJson; +} + +void GoogleMessage::handleFunctionCallComplete() +{ + if (m_currentFunctionName.isEmpty()) { + return; + } + + QJsonObject args; + if (!m_pendingFunctionArgs.isEmpty()) { + QJsonDocument doc = QJsonDocument::fromJson(m_pendingFunctionArgs.toUtf8()); + if (doc.isObject()) { + args = doc.object(); + } + } + + QString id = QUuid::createUuid().toString(QUuid::WithoutBraces); + auto toolContent = new LLMCore::ToolUseContent(id, m_currentFunctionName, args); + toolContent->setParent(this); + m_currentBlocks.append(toolContent); + + LOG_MESSAGE(QString("Google: Completed function call: name=%1, args=%2") + .arg(m_currentFunctionName) + .arg(QString::fromUtf8(QJsonDocument(args).toJson(QJsonDocument::Compact)))); + + m_currentFunctionName.clear(); + m_pendingFunctionArgs.clear(); +} + +void GoogleMessage::handleFinishReason(const QString &reason) +{ + m_finishReason = reason; + updateStateFromFinishReason(); + + LOG_MESSAGE( + QString("Google: Finish reason: %1, state: %2").arg(reason).arg(static_cast(m_state))); +} + +QJsonObject GoogleMessage::toProviderFormat() const +{ + QJsonObject content; + content["role"] = "model"; + + QJsonArray parts; + + for (auto block : m_currentBlocks) { + if (!block) + continue; + + if (auto text = qobject_cast(block)) { + parts.append(QJsonObject{{"text", text->text()}}); + } else if (auto tool = qobject_cast(block)) { + QJsonObject functionCall; + functionCall["name"] = tool->name(); + functionCall["args"] = tool->input(); + parts.append(QJsonObject{{"functionCall", functionCall}}); + } + } + + content["parts"] = parts; + return content; +} + +QJsonArray GoogleMessage::createToolResultParts(const QHash &toolResults) const +{ + QJsonArray parts; + + for (auto toolContent : getCurrentToolUseContent()) { + if (toolResults.contains(toolContent->id())) { + QJsonObject functionResponse; + functionResponse["name"] = toolContent->name(); + + QJsonObject response; + response["result"] = toolResults[toolContent->id()]; + functionResponse["response"] = response; + + parts.append(QJsonObject{{"functionResponse", functionResponse}}); + } + } + + return parts; +} + +QList GoogleMessage::getCurrentToolUseContent() const +{ + QList toolBlocks; + for (auto block : m_currentBlocks) { + if (auto toolContent = qobject_cast(block)) { + toolBlocks.append(toolContent); + } + } + return toolBlocks; +} + +void GoogleMessage::startNewContinuation() +{ + LOG_MESSAGE(QString("GoogleMessage: Starting new continuation")); + + m_currentBlocks.clear(); + m_pendingFunctionArgs.clear(); + m_currentFunctionName.clear(); + m_finishReason.clear(); + m_state = LLMCore::MessageState::Building; +} + +void GoogleMessage::updateStateFromFinishReason() +{ + if (m_finishReason == "STOP" || m_finishReason == "MAX_TOKENS") { + m_state = getCurrentToolUseContent().isEmpty() + ? LLMCore::MessageState::Complete + : LLMCore::MessageState::RequiresToolExecution; + } else { + m_state = LLMCore::MessageState::Complete; + } +} + +} // namespace QodeAssist::Providers diff --git a/providers/GoogleMessage.hpp b/providers/GoogleMessage.hpp new file mode 100644 index 0000000..263447b --- /dev/null +++ b/providers/GoogleMessage.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2025 Petr Mironychev + * + * This file is part of QodeAssist. + * + * QodeAssist is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * QodeAssist is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QodeAssist. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace QodeAssist::Providers { + +class GoogleMessage : public QObject +{ + Q_OBJECT +public: + explicit GoogleMessage(QObject *parent = nullptr); + + void handleContentDelta(const QString &text); + void handleFunctionCallStart(const QString &name); + void handleFunctionCallArgsDelta(const QString &argsJson); + void handleFunctionCallComplete(); + void handleFinishReason(const QString &reason); + + QJsonObject toProviderFormat() const; + QJsonArray createToolResultParts(const QHash &toolResults) const; + + QList getCurrentToolUseContent() const; + QList currentBlocks() const { return m_currentBlocks; } + + LLMCore::MessageState state() const { return m_state; } + void startNewContinuation(); + +private: + void updateStateFromFinishReason(); + + QList m_currentBlocks; + QString m_pendingFunctionArgs; + QString m_currentFunctionName; + QString m_finishReason; + LLMCore::MessageState m_state = LLMCore::MessageState::Building; +}; + +} // namespace QodeAssist::Providers diff --git a/tools/ListProjectFilesTool.cpp b/tools/ListProjectFilesTool.cpp index d6288d0..671ab0b 100644 --- a/tools/ListProjectFilesTool.cpp +++ b/tools/ListProjectFilesTool.cpp @@ -68,6 +68,8 @@ QJsonObject ListProjectFilesTool::getDefinition(LLMCore::ToolSchemaFormat format return customizeForClaude(definition); case LLMCore::ToolSchemaFormat::Ollama: return customizeForOllama(definition); + case LLMCore::ToolSchemaFormat::Google: + return customizeForGoogle(definition); } return definition; diff --git a/tools/ReadProjectFileByNameTool.cpp b/tools/ReadProjectFileByNameTool.cpp index 9ad235b..d8041b9 100644 --- a/tools/ReadProjectFileByNameTool.cpp +++ b/tools/ReadProjectFileByNameTool.cpp @@ -86,6 +86,8 @@ QJsonObject ReadProjectFileByNameTool::getDefinition(LLMCore::ToolSchemaFormat f return customizeForClaude(definition); case LLMCore::ToolSchemaFormat::Ollama: return customizeForOllama(definition); + case LLMCore::ToolSchemaFormat::Google: + return customizeForGoogle(definition); } return definition; diff --git a/tools/ReadVisibleFilesTool.cpp b/tools/ReadVisibleFilesTool.cpp index 7a34f12..4db1d0b 100644 --- a/tools/ReadVisibleFilesTool.cpp +++ b/tools/ReadVisibleFilesTool.cpp @@ -66,6 +66,8 @@ QJsonObject ReadVisibleFilesTool::getDefinition(LLMCore::ToolSchemaFormat format return customizeForClaude(definition); case LLMCore::ToolSchemaFormat::Ollama: return customizeForOllama(definition); + case LLMCore::ToolSchemaFormat::Google: + return customizeForGoogle(definition); } return definition;