mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-05-28 03:10:28 -04:00
Version 0.2.2
- Improve chat history - Fix system prompt - Fix ollama model livetime - Fix providers and prompt default value
This commit is contained in:
commit
1649a246e1
@ -250,9 +250,6 @@ QString DocumentContextReader::getInstructions() const
|
||||
{
|
||||
QString instructions;
|
||||
|
||||
if (Settings::contextSettings().useSpecificInstructions())
|
||||
instructions += getSpecificInstructions();
|
||||
|
||||
if (Settings::contextSettings().useFilePathInContext())
|
||||
instructions += getLanguageAndFileInfo();
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "PromptTemplateManager.hpp"
|
||||
#include "QodeAssistUtils.hpp"
|
||||
#include "core/LLMRequestConfig.hpp"
|
||||
#include "settings/ContextSettings.hpp"
|
||||
#include "settings/GeneralSettings.hpp"
|
||||
|
||||
namespace QodeAssist {
|
||||
@ -159,6 +160,9 @@ void LLMClientInterface::handleCompletion(const QJsonObject &request)
|
||||
{"stop",
|
||||
QJsonArray::fromStringList(config.promptTemplate->stopWords())}};
|
||||
|
||||
if (Settings::contextSettings().useSpecificInstructions())
|
||||
config.providerRequest["system"] = Settings::contextSettings().specificInstractions();
|
||||
|
||||
config.promptTemplate->prepareRequest(config.providerRequest, updatedContext);
|
||||
config.provider->prepareRequest(config.providerRequest);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"Name" : "QodeAssist",
|
||||
"Version" : "0.2.1",
|
||||
"Version" : "0.2.2",
|
||||
"CompatVersion" : "${IDE_VERSION_COMPAT}",
|
||||
"Vendor" : "Petr Mironychev",
|
||||
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
||||
|
@ -53,10 +53,10 @@ const char AUTO_COMPLETION_CHAR_THRESHOLD[] = "QodeAssist.autoCompletionCharThre
|
||||
const char AUTO_COMPLETION_TYPING_INTERVAL[] = "QodeAssist.autoCompletionTypingInterval";
|
||||
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
|
||||
const char OLLAMA_LIVETIME[] = "QodeAssist.ollamaLivetime";
|
||||
const char SPECIFIC_INSTRUCTIONS[] = "QodeAssist.specificInstractions";
|
||||
const char SYSTEM_PROMPT[] = "QodeAssist.systemPrompt";
|
||||
const char MULTILINE_COMPLETION[] = "QodeAssist.multilineCompletion";
|
||||
const char API_KEY[] = "QodeAssist.apiKey";
|
||||
const char USE_SPECIFIC_INSTRUCTIONS[] = "QodeAssist.useSpecificInstructions";
|
||||
const char USE_SYSTEM_PROMPT[] = "QodeAssist.useSystemPrompt";
|
||||
const char USE_FILE_PATH_IN_CONTEXT[] = "QodeAssist.useFilePathInContext";
|
||||
const char CUSTOM_JSON_TEMPLATE[] = "QodeAssist.customJsonTemplate";
|
||||
const char USE_PROJECT_CHANGES_CACHE[] = "QodeAssist.useProjectChangesCache";
|
||||
|
10
README.md
10
README.md
@ -65,9 +65,15 @@ If you've successfully used a model that's not listed here, please let us know b
|
||||
|
||||
1. Install QtCreator 14.0
|
||||
2. Install [Ollama](https://ollama.com). Make sure to review the system requirements before installation.
|
||||
3. Install a language model in Ollama. For example, you can run:
|
||||
3. Install a language models in Ollama. For example, you can run:
|
||||
|
||||
For suggestions:
|
||||
```
|
||||
ollama run starcoder2:7b
|
||||
ollama run codellama:7b-code
|
||||
```
|
||||
For chat:
|
||||
```
|
||||
ollama run codellama:7b-instruct
|
||||
```
|
||||
4. Download the QodeAssist plugin.
|
||||
5. Launch Qt Creator and install the plugin:
|
||||
|
@ -31,6 +31,51 @@
|
||||
|
||||
namespace QodeAssist::Chat {
|
||||
|
||||
int ChatHistory::estimateTokenCount(const QString &text) const
|
||||
{
|
||||
return text.length() / 4;
|
||||
}
|
||||
|
||||
void ChatHistory::addMessage(ChatMessage::Role role, const QString &content)
|
||||
{
|
||||
int tokenCount = estimateTokenCount(content);
|
||||
m_messages.append({role, content, tokenCount});
|
||||
m_totalTokens += tokenCount;
|
||||
trim();
|
||||
}
|
||||
void ChatHistory::clear()
|
||||
{
|
||||
m_messages.clear();
|
||||
m_totalTokens = 0;
|
||||
}
|
||||
|
||||
QVector<ChatMessage> ChatHistory::getMessages() const
|
||||
{
|
||||
return m_messages;
|
||||
}
|
||||
|
||||
QString ChatHistory::getSystemPrompt() const
|
||||
{
|
||||
return m_systemPrompt;
|
||||
}
|
||||
|
||||
void ChatHistory::setSystemPrompt(const QString &prompt)
|
||||
{
|
||||
m_systemPrompt = prompt;
|
||||
}
|
||||
|
||||
void ChatHistory::trim()
|
||||
{
|
||||
while (m_messages.size() > MAX_HISTORY_SIZE || m_totalTokens > MAX_TOKENS) {
|
||||
if (!m_messages.isEmpty()) {
|
||||
m_totalTokens -= m_messages.first().tokenCount;
|
||||
m_messages.removeFirst();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChatClientInterface::ChatClientInterface(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_requestHandler(new LLMRequestHandler(this))
|
||||
@ -51,15 +96,10 @@ ChatClientInterface::ChatClientInterface(QObject *parent)
|
||||
}
|
||||
});
|
||||
|
||||
// QJsonObject systemMessage;
|
||||
// systemMessage["role"] = "system";
|
||||
// systemMessage["content"] = "You are a helpful C++ and QML programming assistant.";
|
||||
// m_chatHistory.append(systemMessage);
|
||||
m_chatHistory.setSystemPrompt("You are a helpful C++ and QML programming assistant.");
|
||||
}
|
||||
|
||||
ChatClientInterface::~ChatClientInterface()
|
||||
{
|
||||
}
|
||||
ChatClientInterface::~ChatClientInterface() = default;
|
||||
|
||||
void ChatClientInterface::sendMessage(const QString &message)
|
||||
{
|
||||
@ -79,14 +119,11 @@ void ChatClientInterface::sendMessage(const QString &message)
|
||||
QJsonObject providerRequest;
|
||||
providerRequest["model"] = Settings::generalSettings().chatModelName();
|
||||
providerRequest["stream"] = true;
|
||||
|
||||
providerRequest["messages"] = m_chatHistory;
|
||||
providerRequest["messages"] = prepareMessagesForRequest();
|
||||
|
||||
chatTemplate->prepareRequest(providerRequest, context);
|
||||
chatProvider->prepareRequest(providerRequest);
|
||||
|
||||
m_chatHistory = providerRequest["messages"].toArray();
|
||||
|
||||
LLMConfig config;
|
||||
config.requestType = RequestType::Chat;
|
||||
config.provider = chatProvider;
|
||||
@ -99,18 +136,22 @@ void ChatClientInterface::sendMessage(const QString &message)
|
||||
request["id"] = QUuid::createUuid().toString();
|
||||
|
||||
m_accumulatedResponse.clear();
|
||||
m_pendingMessage = message;
|
||||
m_chatHistory.addMessage(ChatMessage::Role::User, message);
|
||||
m_requestHandler->sendLLMRequest(config, request);
|
||||
}
|
||||
|
||||
void ChatClientInterface::clearMessages()
|
||||
{
|
||||
m_chatHistory = {};
|
||||
m_chatHistory.clear();
|
||||
m_accumulatedResponse.clear();
|
||||
m_pendingMessage.clear();
|
||||
logMessage("Chat history cleared");
|
||||
}
|
||||
|
||||
QVector<ChatMessage> ChatClientInterface::getChatHistory() const
|
||||
{
|
||||
return m_chatHistory.getMessages();
|
||||
}
|
||||
|
||||
void ChatClientInterface::handleLLMResponse(const QString &response, bool isComplete)
|
||||
{
|
||||
m_accumulatedResponse += response;
|
||||
@ -119,42 +160,33 @@ void ChatClientInterface::handleLLMResponse(const QString &response, bool isComp
|
||||
logMessage("Message completed. Final response: " + m_accumulatedResponse);
|
||||
emit messageReceived(m_accumulatedResponse.trimmed());
|
||||
|
||||
QJsonObject assistantMessage;
|
||||
assistantMessage["role"] = "assistant";
|
||||
assistantMessage["content"] = m_accumulatedResponse.trimmed();
|
||||
m_chatHistory.append(assistantMessage);
|
||||
|
||||
m_pendingMessage.clear();
|
||||
m_chatHistory.addMessage(ChatMessage::Role::Assistant, m_accumulatedResponse.trimmed());
|
||||
m_accumulatedResponse.clear();
|
||||
|
||||
trimChatHistory();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatClientInterface::trimChatHistory()
|
||||
QJsonArray ChatClientInterface::prepareMessagesForRequest() const
|
||||
{
|
||||
int maxTokens = 4000;
|
||||
int totalTokens = 0;
|
||||
QJsonArray newHistory;
|
||||
QJsonArray messages;
|
||||
|
||||
if (!m_chatHistory.isEmpty()
|
||||
&& m_chatHistory.first().toObject()["role"].toString() == "system") {
|
||||
newHistory.append(m_chatHistory.first());
|
||||
}
|
||||
messages.append(QJsonObject{{"role", "system"}, {"content", m_chatHistory.getSystemPrompt()}});
|
||||
|
||||
for (int i = m_chatHistory.size() - 1; i >= 0; --i) {
|
||||
QJsonObject message = m_chatHistory[i].toObject();
|
||||
int messageTokens = message["content"].toString().length() / 4;
|
||||
|
||||
if (totalTokens + messageTokens > maxTokens) {
|
||||
for (const auto &message : m_chatHistory.getMessages()) {
|
||||
QString role;
|
||||
switch (message.role) {
|
||||
case ChatMessage::Role::User:
|
||||
role = "user";
|
||||
break;
|
||||
case ChatMessage::Role::Assistant:
|
||||
role = "assistant";
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
newHistory.prepend(message);
|
||||
totalTokens += messageTokens;
|
||||
messages.append(QJsonObject{{"role", role}, {"content", message.content}});
|
||||
}
|
||||
|
||||
m_chatHistory = newHistory;
|
||||
return messages;
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Chat
|
||||
|
@ -20,12 +20,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QtCore/qjsonarray.h>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include "QodeAssistData.hpp"
|
||||
#include "core/LLMRequestHandler.hpp"
|
||||
|
||||
namespace QodeAssist::Chat {
|
||||
|
||||
struct ChatMessage
|
||||
{
|
||||
enum class Role { System, User, Assistant };
|
||||
Role role;
|
||||
QString content;
|
||||
int tokenCount;
|
||||
};
|
||||
|
||||
class ChatHistory
|
||||
{
|
||||
public:
|
||||
void addMessage(ChatMessage::Role role, const QString &content);
|
||||
void clear();
|
||||
QVector<ChatMessage> getMessages() const;
|
||||
QString getSystemPrompt() const;
|
||||
void setSystemPrompt(const QString &prompt);
|
||||
void trim();
|
||||
|
||||
private:
|
||||
QVector<ChatMessage> m_messages;
|
||||
QString m_systemPrompt;
|
||||
int m_totalTokens = 0;
|
||||
static const int MAX_HISTORY_SIZE = 50;
|
||||
static const int MAX_TOKENS = 4000;
|
||||
|
||||
int estimateTokenCount(const QString &text) const;
|
||||
};
|
||||
|
||||
class ChatClientInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -36,6 +65,7 @@ public:
|
||||
|
||||
void sendMessage(const QString &message);
|
||||
void clearMessages();
|
||||
QVector<ChatMessage> getChatHistory() const;
|
||||
|
||||
signals:
|
||||
void messageReceived(const QString &message);
|
||||
@ -43,12 +73,11 @@ signals:
|
||||
|
||||
private:
|
||||
void handleLLMResponse(const QString &response, bool isComplete);
|
||||
void trimChatHistory();
|
||||
QJsonArray prepareMessagesForRequest() const;
|
||||
|
||||
LLMRequestHandler *m_requestHandler;
|
||||
QString m_accumulatedResponse;
|
||||
QString m_pendingMessage;
|
||||
QJsonArray m_chatHistory;
|
||||
ChatHistory m_chatHistory;
|
||||
};
|
||||
|
||||
} // namespace QodeAssist::Chat
|
||||
|
@ -55,9 +55,20 @@ QString LMStudioProvider::chatEndpoint() const
|
||||
void LMStudioProvider::prepareRequest(QJsonObject &request)
|
||||
{
|
||||
auto &settings = Settings::presetPromptsSettings();
|
||||
QJsonArray messages;
|
||||
|
||||
if (request.contains("system")) {
|
||||
QJsonObject systemMessage{{"role", "system"},
|
||||
{"content", request.take("system").toString()}};
|
||||
messages.append(systemMessage);
|
||||
}
|
||||
|
||||
if (request.contains("prompt")) {
|
||||
QJsonArray messages{
|
||||
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
||||
QJsonObject userMessage{{"role", "user"}, {"content", request.take("prompt").toString()}};
|
||||
messages.append(userMessage);
|
||||
}
|
||||
|
||||
if (!messages.isEmpty()) {
|
||||
request["messages"] = std::move(messages);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,6 @@ void OllamaProvider::prepareRequest(QJsonObject &request)
|
||||
|
||||
QJsonObject options;
|
||||
options["num_predict"] = settings.maxTokens();
|
||||
options["keep_alive"] = settings.ollamaLivetime();
|
||||
options["temperature"] = settings.temperature();
|
||||
if (settings.useTopP())
|
||||
options["top_p"] = settings.topP();
|
||||
@ -70,6 +69,7 @@ void OllamaProvider::prepareRequest(QJsonObject &request)
|
||||
if (settings.usePresencePenalty())
|
||||
options["presence_penalty"] = settings.presencePenalty();
|
||||
request["options"] = options;
|
||||
request["keep_alive"] = settings.ollamaLivetime();
|
||||
}
|
||||
|
||||
bool OllamaProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "PromptTemplateManager.hpp"
|
||||
#include "settings/PresetPromptsSettings.hpp"
|
||||
|
||||
namespace QodeAssist::Providers {
|
||||
@ -54,9 +53,20 @@ QString OpenAICompatProvider::chatEndpoint() const
|
||||
void OpenAICompatProvider::prepareRequest(QJsonObject &request)
|
||||
{
|
||||
auto &settings = Settings::presetPromptsSettings();
|
||||
QJsonArray messages;
|
||||
|
||||
if (request.contains("system")) {
|
||||
QJsonObject systemMessage{{"role", "system"},
|
||||
{"content", request.take("system").toString()}};
|
||||
messages.append(systemMessage);
|
||||
}
|
||||
|
||||
if (request.contains("prompt")) {
|
||||
QJsonArray messages{
|
||||
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
||||
QJsonObject userMessage{{"role", "user"}, {"content", request.take("prompt").toString()}};
|
||||
messages.append(userMessage);
|
||||
}
|
||||
|
||||
if (!messages.isEmpty()) {
|
||||
request["messages"] = std::move(messages);
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,6 @@
|
||||
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ContextSettings.hpp"
|
||||
|
||||
#include <QMessageBox>
|
||||
@ -60,11 +58,11 @@ ContextSettings::ContextSettings()
|
||||
useFilePathInContext.setDefaultValue(false);
|
||||
useFilePathInContext.setLabelText(Tr::tr("Use File Path in Context"));
|
||||
|
||||
useSpecificInstructions.setSettingsKey(Constants::USE_SPECIFIC_INSTRUCTIONS);
|
||||
useSpecificInstructions.setSettingsKey(Constants::USE_SYSTEM_PROMPT);
|
||||
useSpecificInstructions.setDefaultValue(true);
|
||||
useSpecificInstructions.setLabelText(Tr::tr("Use Specific Instructions"));
|
||||
useSpecificInstructions.setLabelText(Tr::tr("Use System Prompt"));
|
||||
|
||||
specificInstractions.setSettingsKey(Constants::SPECIFIC_INSTRUCTIONS);
|
||||
specificInstractions.setSettingsKey(Constants::SYSTEM_PROMPT);
|
||||
specificInstractions.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
|
||||
specificInstractions.setLabelText(
|
||||
Tr::tr("Instructions: Please keep %1 for languge name, warning, it shouldn't too big"));
|
||||
|
@ -131,6 +131,11 @@ GeneralSettings::GeneralSettings()
|
||||
loadProviders();
|
||||
loadPrompts();
|
||||
|
||||
llmProviders.setDefaultValue(llmProviders.indexForDisplay("Ollama"));
|
||||
chatLlmProviders.setDefaultValue(chatLlmProviders.indexForDisplay("Ollama"));
|
||||
fimPrompts.setDefaultValue(fimPrompts.indexForDisplay("CodeLLama FIM"));
|
||||
chatPrompts.setDefaultValue(chatPrompts.indexForDisplay("CodeLLama Chat"));
|
||||
|
||||
readSettings();
|
||||
|
||||
auto fimProviderName = llmProviders.displayForIndex(llmProviders.value());
|
||||
@ -273,12 +278,12 @@ void GeneralSettings::resetPageToDefaults()
|
||||
resetAspect(startSuggestionTimer);
|
||||
resetAspect(autoCompletionTypingInterval);
|
||||
resetAspect(autoCompletionCharThreshold);
|
||||
resetAspect(llmProviders);
|
||||
resetAspect(chatLlmProviders);
|
||||
resetAspect(fimPrompts);
|
||||
resetAspect(chatPrompts);
|
||||
}
|
||||
|
||||
int fimIndex = llmProviders.indexForDisplay("Ollama");
|
||||
llmProviders.setVolatileValue(fimIndex);
|
||||
int chatIndex = chatLlmProviders.indexForDisplay("Ollama");
|
||||
chatLlmProviders.setVolatileValue(chatIndex);
|
||||
modelName.setVolatileValue("");
|
||||
chatModelName.setVolatileValue("");
|
||||
|
||||
|
@ -27,7 +27,7 @@ class CodeLlamaFimTemplate : public PromptTemplate
|
||||
{
|
||||
public:
|
||||
TemplateType type() const override { return TemplateType::Fim; }
|
||||
QString name() const override { return "CodeLlama FIM"; }
|
||||
QString name() const override { return "CodeLLama FIM"; }
|
||||
QString promptTemplate() const override { return "%1<PRE> %2 <SUF>%3 <MID>"; }
|
||||
QStringList stopWords() const override
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ class CodeLlamaInstructTemplate : public PromptTemplate
|
||||
{
|
||||
public:
|
||||
TemplateType type() const override { return TemplateType::Chat; }
|
||||
QString name() const override { return "CodeLlama Chat"; }
|
||||
QString name() const override { return "CodeLLama Chat"; }
|
||||
QString promptTemplate() const override { return "[INST] %1 [/INST]"; }
|
||||
QStringList stopWords() const override { return QStringList() << "[INST]" << "[/INST]"; }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user