Abstract the built-in web search completely away from ChatLLM.

Signed-off-by: Adam Treat <treat.adam@gmail.com>
This commit is contained in:
Adam Treat 2024-08-14 10:24:06 -04:00
parent 75dbf9de7d
commit 991afc6ef2
4 changed files with 28 additions and 32 deletions

View File

@ -1,4 +1,5 @@
#include "bravesearch.h" #include "bravesearch.h"
#include "mysettings.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDebug> #include <QDebug>
@ -18,9 +19,9 @@ using namespace Qt::Literals::StringLiterals;
QString BraveSearch::run(const QJsonObject &parameters, qint64 timeout) QString BraveSearch::run(const QJsonObject &parameters, qint64 timeout)
{ {
const QString apiKey = parameters["apiKey"].toString(); const QString apiKey = MySettings::globalInstance()->braveSearchAPIKey();
const QString query = parameters["query"].toString(); const QString query = parameters["query"].toString();
const int count = parameters["count"].toInt(); const int count = 2; // FIXME: This should be a setting
QThread workerThread; QThread workerThread;
BraveAPIWorker worker; BraveAPIWorker worker;
worker.moveToThread(&workerThread); worker.moveToThread(&workerThread);

View File

@ -34,7 +34,7 @@ private Q_SLOTS:
private: private:
QNetworkAccessManager *m_networkManager; QNetworkAccessManager *m_networkManager;
QString m_response; QString m_response;
ToolEnums::Error m_error; ToolEnums::Error m_error = ToolEnums::Error::NoError;
QString m_errorString; QString m_errorString;
}; };

View File

@ -1,6 +1,5 @@
#include "chatllm.h" #include "chatllm.h"
#include "bravesearch.h"
#include "chat.h" #include "chat.h"
#include "chatapi.h" #include "chatapi.h"
#include "localdocssearch.h" #include "localdocssearch.h"
@ -893,36 +892,31 @@ bool ChatLLM::promptRecursive(const QList<QString> &toolContexts, const QString
const QString tool = toolCallDoc["name"].toString(); const QString tool = toolCallDoc["name"].toString();
const QJsonObject args = toolCallDoc["parameters"].toObject(); const QJsonObject args = toolCallDoc["parameters"].toObject();
// FIXME: In the future this will try to match the tool call to a list of tools that are supported Tool *toolInstance = ToolModel::globalInstance()->get(tool);
// according to MySettings, but for now only brave search is supported if (!toolInstance) {
if (tool != "web_search" || !args.contains("query")) { qWarning() << "ERROR: Could not find the tool for " << toolCall;
// FIXME: Need to surface errors to the UI
qWarning() << "ERROR: Could not find the tool and correct parameters for " << toolCall;
return handleFailedToolCall(trimmed, totalTime); return handleFailedToolCall(trimmed, totalTime);
} }
const QString query = args["query"].toString(); // Inform the chat that we're executing a tool call
emit toolCalled(toolInstance->name().toLower());
emit toolCalled(tr("searching web...")); const QString response = toolInstance->run(args, 2000 /*msecs to timeout*/);
const QString apiKey = MySettings::globalInstance()->braveSearchAPIKey(); if (toolInstance->error() != ToolEnums::Error::NoError) {
Q_ASSERT(apiKey != ""); qWarning() << "ERROR: Tool call produced error:" << toolInstance->errorString();
BraveSearch brave; return handleFailedToolCall(trimmed, totalTime);
}
QJsonObject parameters; // If the tool supports excerpts then try to parse them here
parameters.insert("apiKey", apiKey); if (toolInstance->excerpts()) {
parameters.insert("query", query); QString parseError;
parameters.insert("count", 2); QList<SourceExcerpt> sourceExcerpts = SourceExcerpt::fromJson(response, parseError);
if (!parseError.isEmpty()) {
// FIXME: Need to surface errors to the UI qWarning() << "ERROR: Could not parse source excerpts for response:" << parseError;
const QString braveResponse = brave.run(parameters, 2000 /*msecs to timeout*/); } else if (!sourceExcerpts.isEmpty()) {
producedSourceExcerpts = true;
QString parseError; emit sourceExcerptsChanged(sourceExcerpts);
QList<SourceExcerpt> sourceExcerpts = SourceExcerpt::fromJson(braveResponse, parseError); }
if (!parseError.isEmpty()) {
qWarning() << "ERROR: Could not parse source excerpts for brave response:" << parseError;
} else if (!sourceExcerpts.isEmpty()) {
producedSourceExcerpts = true;
emit sourceExcerptsChanged(sourceExcerpts);
} }
m_promptResponseTokens = 0; m_promptResponseTokens = 0;
@ -931,7 +925,7 @@ bool ChatLLM::promptRecursive(const QList<QString> &toolContexts, const QString
// This is a recursive call but isRecursiveCall is checked above to arrest infinite recursive // This is a recursive call but isRecursiveCall is checked above to arrest infinite recursive
// tool calls // tool calls
return promptRecursive(QList<QString>()/*collectionList*/, braveResponse, toolTemplate, return promptRecursive(QList<QString>()/*tool context*/, response, toolTemplate,
n_predict, top_k, top_p, min_p, temp, n_batch, repeat_penalty, repeat_penalty_tokens, totalTime, n_predict, top_k, top_p, min_p, temp, n_batch, repeat_penalty, repeat_penalty_tokens, totalTime,
producedSourceExcerpts, true /*isRecursiveCall*/); producedSourceExcerpts, true /*isRecursiveCall*/);
} else { } else {
@ -946,6 +940,7 @@ bool ChatLLM::promptRecursive(const QList<QString> &toolContexts, const QString
bool ChatLLM::handleFailedToolCall(const std::string &response, qint64 elapsed) bool ChatLLM::handleFailedToolCall(const std::string &response, qint64 elapsed)
{ {
// FIXME: Need to surface errors to the UI
// Restore the strings that we excluded previously when detecting the tool call // Restore the strings that we excluded previously when detecting the tool call
m_response = "<tool_call>" + response + "</tool_call>"; m_response = "<tool_call>" + response + "</tool_call>";
emit responseChanged(QString::fromStdString(m_response)); emit responseChanged(QString::fromStdString(m_response));

View File

@ -881,8 +881,8 @@ Rectangle {
case Chat.PromptProcessing: return qsTr("processing ...") case Chat.PromptProcessing: return qsTr("processing ...")
case Chat.ResponseGeneration: return qsTr("generating response ..."); case Chat.ResponseGeneration: return qsTr("generating response ...");
case Chat.GeneratingQuestions: return qsTr("generating questions ..."); case Chat.GeneratingQuestions: return qsTr("generating questions ...");
case Chat.ToolCalled: return currentChat.toolDescription; case Chat.ToolCalled: return qsTr("executing %1 ...").arg(currentChat.toolDescription);
case Chat.ToolProcessing: return qsTr("processing web results ..."); // FIXME should not be hardcoded! case Chat.ToolProcessing: return qsTr("processing %1 results ...").arg(currentChat.toolDescription);
default: return ""; // handle unexpected values default: return ""; // handle unexpected values
} }
} }