From ed1b5a53893d2a3ec25a408ea87d7de480f9a843 Mon Sep 17 00:00:00 2001 From: Martin Dvorak Date: Tue, 12 Mar 2024 08:41:31 +0100 Subject: [PATCH] WIP: configuration & choice of the LLM models offered by the provider #1539 --- app/src/qt/dialogs/configuration_dialog.cpp | 2 +- lib/src/config/configuration.cpp | 2 +- lib/src/mind/ai/llm/ollama_wingman.cpp | 24 ++++++++++++++++----- lib/src/mind/ai/llm/ollama_wingman.h | 6 ++++-- lib/src/mind/ai/llm/openai_wingman.cpp | 24 +++++++++++++++------ lib/src/mind/ai/llm/openai_wingman.h | 10 +++++++-- lib/src/mind/ai/llm/wingman.cpp | 1 + lib/src/mind/ai/llm/wingman.h | 19 ++++++++++++++++ 8 files changed, 71 insertions(+), 17 deletions(-) diff --git a/app/src/qt/dialogs/configuration_dialog.cpp b/app/src/qt/dialogs/configuration_dialog.cpp index 3267fab8..f0846e6a 100644 --- a/app/src/qt/dialogs/configuration_dialog.cpp +++ b/app/src/qt/dialogs/configuration_dialog.cpp @@ -779,7 +779,7 @@ ConfigurationDialog::WingmanOllamaTab::WingmanOllamaTab(QWidget* parent) ).arg(ENV_VAR_OPENAI_API_KEY)); helpLabel->setVisible(!config.canWingmanOllama()); urlEdit = new QLineEdit(this); - clearUrlButton = new QPushButton(tr("Clear ollama URL"), this); + clearUrlButton = new QPushButton(tr("Clear URL"), this); QVBoxLayout* llmProvidersLayout = new QVBoxLayout(); llmProvidersLayout->addWidget(helpLabel); diff --git a/lib/src/config/configuration.cpp b/lib/src/config/configuration.cpp index ea25bc3c..4539d1d3 100644 --- a/lib/src/config/configuration.cpp +++ b/lib/src/config/configuration.cpp @@ -38,7 +38,7 @@ const string Configuration::DEFAULT_UI_THEME_NAME = string{UI_DEFAULT_THEME}; const string Configuration::DEFAULT_UI_HTML_CSS_THEME = string{UI_DEFAULT_HTML_CSS_THEME}; const string Configuration::DEFAULT_EDITOR_FONT= string{UI_DEFAULT_EDITOR_FONT}; const string Configuration::DEFAULT_TIME_SCOPE = string{"0y0m0d0h0m"}; -const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OPENAI = string{"gpt-3.5-turbo"}; // "gpt-3.5-turbo" and "gpt-4" are symbolic names +const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OPENAI = LLM_MODEL_OPENAI_GPT35TURBO; const string Configuration::DEFAULT_WINGMAN_LLM_MODEL_OLLAMA = string{"llama2"}; Configuration::Configuration() diff --git a/lib/src/mind/ai/llm/ollama_wingman.cpp b/lib/src/mind/ai/llm/ollama_wingman.cpp index 672c7aa6..ccbc9b48 100644 --- a/lib/src/mind/ai/llm/ollama_wingman.cpp +++ b/lib/src/mind/ai/llm/ollama_wingman.cpp @@ -40,13 +40,10 @@ size_t ollamaCurlWriteCallback(void* contents, size_t size, size_t nmemb, std::s * Ollama Wingman class implementation. */ -OllamaWingman::OllamaWingman( - const string& url, - const std::string& llmModel -) +OllamaWingman::OllamaWingman(const string& url) : Wingman(WingmanLlmProviders::WINGMAN_PROVIDER_OLLAMA), url{url + "/api/generate"}, - llmModel{llmModel} + llmModels{} { } @@ -54,6 +51,23 @@ OllamaWingman::~OllamaWingman() { } +void OllamaWingman::curlListModels() { + if(!this->llmModels.empty()) { + this->llmModels.clear(); + } + + // call to ollama API to list available models + throw std::runtime_error("OllamaWingman::curlListModels() not implemented"); +} + +std::vector& OllamaWingman::listModels() +{ + if(this->llmModels.empty()) { + this->curlListModels(); + } + return this->llmModels; +} + /** * OpenAI cURL GET request. * diff --git a/lib/src/mind/ai/llm/ollama_wingman.h b/lib/src/mind/ai/llm/ollama_wingman.h index 2b160a60..af2d1558 100644 --- a/lib/src/mind/ai/llm/ollama_wingman.h +++ b/lib/src/mind/ai/llm/ollama_wingman.h @@ -43,18 +43,20 @@ class OllamaWingman: Wingman { private: std::string url; - std::string llmModel; + std::vector llmModels; + void curlListModels(); void curlGet(CommandWingmanChat& command); public: - explicit OllamaWingman(const std::string& url, const std::string& llmModel); + explicit OllamaWingman(const std::string& url); OllamaWingman(const OllamaWingman&) = delete; OllamaWingman(const OllamaWingman&&) = delete; OllamaWingman& operator =(const OllamaWingman&) = delete; OllamaWingman& operator =(const OllamaWingman&&) = delete; ~OllamaWingman() override; + virtual std::vector& listModels() override; virtual void chat(CommandWingmanChat& command) override; }; diff --git a/lib/src/mind/ai/llm/openai_wingman.cpp b/lib/src/mind/ai/llm/openai_wingman.cpp index d5215a39..15cdb13e 100644 --- a/lib/src/mind/ai/llm/openai_wingman.cpp +++ b/lib/src/mind/ai/llm/openai_wingman.cpp @@ -26,6 +26,11 @@ namespace m8r { using namespace std; + +// OpenAI models: "gpt-3.5-turbo" and "gpt-4" are aliases for the latest models +const std::string LLM_GPT_35_TURBO=string{"gpt-3.5-turbo"}; +const std::string LLM_GPT_4=string{"gpt-4"}; + /* * cURL callback for writing data to string. */ @@ -40,21 +45,28 @@ size_t openaiCurlWriteCallback(void* contents, size_t size, size_t nmemb, std::s * OpenAi Wingman class implementation. */ -OpenAiWingman::OpenAiWingman( - const string& apiKey, - const std::string& llmModel -) +OpenAiWingman::OpenAiWingman(const string& apiKey) : Wingman(WingmanLlmProviders::WINGMAN_PROVIDER_OPENAI), apiKey{apiKey}, - llmModel{llmModel} + llmModels{}, + defaultLlmModel{LLM_GPT_35_TURBO} { MF_DEBUG("OpenAiWingman::OpenAiWingman() apiKey: " << apiKey << endl); + + // IMPROVE list models using OpenAI API - will many models be confusing for user? + llmModels.push_back(LLM_GPT_35_TURBO); + llmModels.push_back(LLM_GPT_4); } OpenAiWingman::~OpenAiWingman() { } +std::vector& OpenAiWingman::listModels() +{ + return this->llmModels; +} + // TODO refactor to parent class so that all wingmans can use it /** * OpenAI cURL GET request. @@ -225,7 +237,7 @@ void OpenAiWingman::curlGet(CommandWingmanChat& command) { command.httpResponse.clear(); command.answerMarkdown.clear(); command.answerTokens = 0; - command.answerLlmModel = llmModel; + command.answerLlmModel = llmModel.size()>0? llmModel: defaultLlmModel; return; } diff --git a/lib/src/mind/ai/llm/openai_wingman.h b/lib/src/mind/ai/llm/openai_wingman.h index 64fd80c9..b3b4701d 100644 --- a/lib/src/mind/ai/llm/openai_wingman.h +++ b/lib/src/mind/ai/llm/openai_wingman.h @@ -37,20 +37,26 @@ namespace m8r { */ class OpenAiWingman: Wingman { +public: + static const std::string LLM_MODEL_OPENAI_GPT35; + static const std::string LLM_MODEL_OPENAI_GPT4; + private: std::string apiKey; - std::string llmModel; + std::vector llmModels; + std::string defaultLlmModel; void curlGet(CommandWingmanChat& command); public: - explicit OpenAiWingman(const std::string& apiKey, const std::string& llmModel); + explicit OpenAiWingman(const std::string& apiKey); OpenAiWingman(const OpenAiWingman&) = delete; OpenAiWingman(const OpenAiWingman&&) = delete; OpenAiWingman& operator =(const OpenAiWingman&) = delete; OpenAiWingman& operator =(const OpenAiWingman&&) = delete; ~OpenAiWingman() override; + virtual std::vector& listModels() override; virtual void chat(CommandWingmanChat& command) override; }; diff --git a/lib/src/mind/ai/llm/wingman.cpp b/lib/src/mind/ai/llm/wingman.cpp index 5bdc8154..c8eb22d1 100644 --- a/lib/src/mind/ai/llm/wingman.cpp +++ b/lib/src/mind/ai/llm/wingman.cpp @@ -27,6 +27,7 @@ using namespace std; */ Wingman::Wingman(WingmanLlmProviders llmProvider) + : llmModel{} { this->llmProvider = llmProvider; } diff --git a/lib/src/mind/ai/llm/wingman.h b/lib/src/mind/ai/llm/wingman.h index 45ace621..f916fcc7 100644 --- a/lib/src/mind/ai/llm/wingman.h +++ b/lib/src/mind/ai/llm/wingman.h @@ -134,6 +134,9 @@ class Wingman PROMPT_ANTONYM, }; +protected: + std::string llmModel; + public: explicit Wingman(WingmanLlmProviders llmProvider); Wingman(const Wingman&) = delete; @@ -152,6 +155,22 @@ class Wingman return textPrompts; } + virtual void setLlmModel(const std::string& llmModel) { + this->llmModel = llmModel; + } + + virtual const std::string& getLlmModel() { + return llmModel; + } + + /** + * List available LLM models. + */ + virtual std::vector& listModels() = 0; + + /** + * Chat with given LLM model. + */ virtual void chat(CommandWingmanChat& command) = 0; };