mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-02-12 10:10:44 -05:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec26a31ec5 | |||
| 55d359e44e | |||
| 46258a11f6 | |||
| 4bccd8db91 | |||
| e3495e10f0 | |||
| d97a3514cc | |||
| fa79803836 | |||
| b4f969908f | |||
| 9beb48ee97 | |||
| d6320b04f7 | |||
| 7797007160 | |||
| a613ea19f4 | |||
| 1201da6af3 | |||
| 6bd6edf54d | |||
| 61aae8e3ba | |||
| 089bd0594e | |||
| e799d0bd00 | |||
| fc58c38f63 |
@ -34,9 +34,11 @@ add_qtc_plugin(QodeAssist
|
|||||||
templates/PromptTemplate.hpp
|
templates/PromptTemplate.hpp
|
||||||
templates/CodeLLamaTemplate.hpp
|
templates/CodeLLamaTemplate.hpp
|
||||||
templates/StarCoder2Template.hpp
|
templates/StarCoder2Template.hpp
|
||||||
|
templates/CodeQwenChat.hpp
|
||||||
providers/LLMProvider.hpp
|
providers/LLMProvider.hpp
|
||||||
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
|
providers/OllamaProvider.hpp providers/OllamaProvider.cpp
|
||||||
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
|
providers/LMStudioProvider.hpp providers/LMStudioProvider.cpp
|
||||||
|
providers/OpenAICompatProvider.h providers/OpenAICompatProvider.cpp
|
||||||
LLMProvidersManager.hpp LLMProvidersManager.cpp
|
LLMProvidersManager.hpp LLMProvidersManager.cpp
|
||||||
QodeAssistSettings.hpp QodeAssistSettings.cpp
|
QodeAssistSettings.hpp QodeAssistSettings.cpp
|
||||||
QodeAssist.qrc
|
QodeAssist.qrc
|
||||||
|
|||||||
@ -19,10 +19,41 @@
|
|||||||
|
|
||||||
#include "DocumentContextReader.hpp"
|
#include "DocumentContextReader.hpp"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
|
#include <languageserverprotocol/lsptypes.h>
|
||||||
|
|
||||||
|
#include "QodeAssistSettings.hpp"
|
||||||
|
|
||||||
|
const QRegularExpression &getYearRegex()
|
||||||
|
{
|
||||||
|
static const QRegularExpression yearRegex("\\b(19|20)\\d{2}\\b");
|
||||||
|
return yearRegex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRegularExpression &getNameRegex()
|
||||||
|
{
|
||||||
|
static const QRegularExpression nameRegex("\\b[A-Z][a-z.]+ [A-Z][a-z.]+\\b");
|
||||||
|
return nameRegex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QRegularExpression &getCommentRegex()
|
||||||
|
{
|
||||||
|
static const QRegularExpression
|
||||||
|
commentRegex(R"((/\*[\s\S]*?\*/|//.*$|#.*$|//{2,}[\s\S]*?//{2,}))",
|
||||||
|
QRegularExpression::MultilineOption);
|
||||||
|
return commentRegex;
|
||||||
|
}
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
DocumentContextReader::DocumentContextReader(TextEditor::TextDocument *textDocument)
|
||||||
|
: m_textDocument(textDocument)
|
||||||
|
, m_document(textDocument->document())
|
||||||
|
{
|
||||||
|
m_copyrightInfo = findCopyright();
|
||||||
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::getLineText(int lineNumber, int cursorPosition) const
|
QString DocumentContextReader::getLineText(int lineNumber, int cursorPosition) const
|
||||||
{
|
{
|
||||||
if (!m_document || lineNumber < 0)
|
if (!m_document || lineNumber < 0)
|
||||||
@ -51,76 +82,131 @@ QString DocumentContextReader::getContextBefore(int lineNumber,
|
|||||||
int cursorPosition,
|
int cursorPosition,
|
||||||
int linesCount) const
|
int linesCount) const
|
||||||
{
|
{
|
||||||
QString context;
|
int effectiveStartLine;
|
||||||
for (int i = qMax(0, lineNumber - linesCount); i <= lineNumber; ++i) {
|
if (m_copyrightInfo.found) {
|
||||||
QString line = getLineText(i, i == lineNumber ? cursorPosition : -1);
|
effectiveStartLine = qMax(m_copyrightInfo.endLine + 1, lineNumber - linesCount);
|
||||||
context += line;
|
} else {
|
||||||
if (i < lineNumber)
|
effectiveStartLine = qMax(0, lineNumber - linesCount);
|
||||||
context += "\n";
|
|
||||||
}
|
}
|
||||||
return context;
|
|
||||||
|
return getContextBetween(effectiveStartLine, lineNumber, cursorPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::getContextAfter(int lineNumber,
|
QString DocumentContextReader::getContextAfter(int lineNumber,
|
||||||
int cursorPosition,
|
int cursorPosition,
|
||||||
int linesCount) const
|
int linesCount) const
|
||||||
{
|
{
|
||||||
QString context;
|
int endLine = qMin(m_document->blockCount() - 1, lineNumber + linesCount);
|
||||||
int maxLine = lineNumber + linesCount;
|
return getContextBetween(lineNumber + 1, endLine, cursorPosition);
|
||||||
for (int i = lineNumber; i <= maxLine; ++i) {
|
|
||||||
QString line = getLineText(i);
|
|
||||||
if (i == lineNumber && cursorPosition >= 0) {
|
|
||||||
line = line.mid(cursorPosition);
|
|
||||||
}
|
|
||||||
context += line;
|
|
||||||
if (i < maxLine && !line.isEmpty())
|
|
||||||
context += "\n";
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::readWholeFileBefore(int lineNumber, int cursorPosition) const
|
QString DocumentContextReader::readWholeFileBefore(int lineNumber, int cursorPosition) const
|
||||||
{
|
{
|
||||||
QString content;
|
int startLine = 0;
|
||||||
QTextBlock block = m_document->begin();
|
if (m_copyrightInfo.found) {
|
||||||
int currentLine = 0;
|
startLine = m_copyrightInfo.endLine + 1;
|
||||||
|
|
||||||
while (block.isValid() && currentLine <= lineNumber) {
|
|
||||||
if (currentLine == lineNumber) {
|
|
||||||
content += block.text().left(cursorPosition);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
content += block.text() + "\n";
|
|
||||||
}
|
|
||||||
block = block.next();
|
|
||||||
currentLine++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
startLine = qMin(startLine, lineNumber);
|
||||||
|
|
||||||
|
QString result = getContextBetween(startLine, lineNumber, cursorPosition);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DocumentContextReader::readWholeFileAfter(int lineNumber, int cursorPosition) const
|
QString DocumentContextReader::readWholeFileAfter(int lineNumber, int cursorPosition) const
|
||||||
{
|
{
|
||||||
QString content;
|
return getContextBetween(lineNumber, m_document->blockCount() - 1, cursorPosition);
|
||||||
QTextBlock block = m_document->begin();
|
}
|
||||||
int currentLine = 0;
|
|
||||||
|
|
||||||
while (block.isValid() && currentLine < lineNumber) {
|
QString DocumentContextReader::getLanguageAndFileInfo() const
|
||||||
block = block.next();
|
{
|
||||||
currentLine++;
|
if (!m_textDocument)
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
QString language = LanguageServerProtocol::TextDocumentItem::mimeTypeToLanguageId(
|
||||||
|
m_textDocument->mimeType());
|
||||||
|
QString mimeType = m_textDocument->mimeType();
|
||||||
|
QString filePath = m_textDocument->filePath().toString();
|
||||||
|
QString fileExtension = QFileInfo(filePath).suffix();
|
||||||
|
|
||||||
|
return QString("//Language: %1 (MIME: %2) filepath: %3(%4)\n\n")
|
||||||
|
.arg(language, mimeType, filePath, fileExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getSpecificInstructions() const
|
||||||
|
{
|
||||||
|
QString specificInstruction = settings().specificInstractions().arg(
|
||||||
|
LanguageServerProtocol::TextDocumentItem::mimeTypeToLanguageId(m_textDocument->mimeType()));
|
||||||
|
return QString("//Instructions: %1").arg(specificInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyrightInfo DocumentContextReader::findCopyright()
|
||||||
|
{
|
||||||
|
CopyrightInfo result = {-1, -1, false};
|
||||||
|
|
||||||
|
QString text = m_document->toPlainText();
|
||||||
|
QRegularExpressionMatchIterator matchIterator = getCommentRegex().globalMatch(text);
|
||||||
|
|
||||||
|
QList<CopyrightInfo> copyrightBlocks;
|
||||||
|
|
||||||
|
while (matchIterator.hasNext()) {
|
||||||
|
QRegularExpressionMatch match = matchIterator.next();
|
||||||
|
QString matchedText = match.captured().toLower();
|
||||||
|
|
||||||
|
if (matchedText.contains("copyright") || matchedText.contains("(C)")
|
||||||
|
|| matchedText.contains("(c)") || matchedText.contains("©")
|
||||||
|
|| getYearRegex().match(text).hasMatch() || getNameRegex().match(text).hasMatch()) {
|
||||||
|
int startPos = match.capturedStart();
|
||||||
|
int endPos = match.capturedEnd();
|
||||||
|
|
||||||
|
CopyrightInfo info;
|
||||||
|
info.startLine = m_document->findBlock(startPos).blockNumber();
|
||||||
|
info.endLine = m_document->findBlock(endPos).blockNumber();
|
||||||
|
info.found = true;
|
||||||
|
|
||||||
|
copyrightBlocks.append(info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (block.isValid()) {
|
for (int i = 0; i < copyrightBlocks.size() - 1; ++i) {
|
||||||
if (currentLine == lineNumber) {
|
if (copyrightBlocks[i].endLine + 1 >= copyrightBlocks[i + 1].startLine) {
|
||||||
content += block.text().mid(cursorPosition) + "\n";
|
copyrightBlocks[i].endLine = copyrightBlocks[i + 1].endLine;
|
||||||
|
copyrightBlocks.removeAt(i + 1);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copyrightBlocks.isEmpty()) { // temproary solution, need cache
|
||||||
|
return copyrightBlocks.first();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DocumentContextReader::getContextBetween(int startLine,
|
||||||
|
int endLine,
|
||||||
|
int cursorPosition) const
|
||||||
|
{
|
||||||
|
QString context;
|
||||||
|
for (int i = startLine; i <= endLine; ++i) {
|
||||||
|
QTextBlock block = m_document->findBlockByNumber(i);
|
||||||
|
if (!block.isValid()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == endLine) {
|
||||||
|
context += block.text().left(cursorPosition);
|
||||||
} else {
|
} else {
|
||||||
content += block.text() + "\n";
|
context += block.text() + "\n";
|
||||||
}
|
}
|
||||||
block = block.next();
|
|
||||||
currentLine++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return content.trimmed();
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyrightInfo DocumentContextReader::copyrightInfo() const
|
||||||
|
{
|
||||||
|
return m_copyrightInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -20,24 +20,38 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
|
||||||
namespace QodeAssist {
|
namespace QodeAssist {
|
||||||
|
|
||||||
|
struct CopyrightInfo
|
||||||
|
{
|
||||||
|
int startLine;
|
||||||
|
int endLine;
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
class DocumentContextReader
|
class DocumentContextReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DocumentContextReader(QTextDocument *doc)
|
DocumentContextReader(TextEditor::TextDocument *textDocument);
|
||||||
: m_document(doc)
|
|
||||||
{}
|
|
||||||
|
|
||||||
QString getLineText(int lineNumber, int cursorPosition = -1) const;
|
QString getLineText(int lineNumber, int cursorPosition = -1) const;
|
||||||
QString getContextBefore(int lineNumber, int cursorPosition, int linesCount) const;
|
QString getContextBefore(int lineNumber, int cursorPosition, int linesCount) const;
|
||||||
QString getContextAfter(int lineNumber, int cursorPosition, int linesCount) const;
|
QString getContextAfter(int lineNumber, int cursorPosition, int linesCount) const;
|
||||||
QString readWholeFileBefore(int lineNumber, int cursorPosition) const;
|
QString readWholeFileBefore(int lineNumber, int cursorPosition) const;
|
||||||
QString readWholeFileAfter(int lineNumber, int cursorPosition) const;
|
QString readWholeFileAfter(int lineNumber, int cursorPosition) const;
|
||||||
|
QString getLanguageAndFileInfo() const;
|
||||||
|
QString getSpecificInstructions() const;
|
||||||
|
CopyrightInfo findCopyright();
|
||||||
|
QString getContextBetween(int startLine, int endLine, int cursorPosition) const;
|
||||||
|
|
||||||
|
CopyrightInfo copyrightInfo() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
TextEditor::TextDocument *m_textDocument;
|
||||||
QTextDocument *m_document;
|
QTextDocument *m_document;
|
||||||
|
CopyrightInfo m_copyrightInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -91,6 +91,27 @@ void LLMClientInterface::handleCancelRequest(const QJsonObject &request)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LLMClientInterface::processSingleLineCompletion(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const QString &accumulatedCompletion)
|
||||||
|
{
|
||||||
|
int newlinePos = accumulatedCompletion.indexOf('\n');
|
||||||
|
|
||||||
|
if (newlinePos != -1) {
|
||||||
|
QString singleLineCompletion = accumulatedCompletion.left(newlinePos).trimmed();
|
||||||
|
singleLineCompletion = removeStopWords(singleLineCompletion);
|
||||||
|
|
||||||
|
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
||||||
|
|
||||||
|
sendCompletionToClient(singleLineCompletion, request, position, true);
|
||||||
|
m_accumulatedResponses.remove(reply);
|
||||||
|
reply->abort();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QString LLMClientInterface::сontextBefore(TextEditor::TextEditorWidget *widget,
|
QString LLMClientInterface::сontextBefore(TextEditor::TextEditorWidget *widget,
|
||||||
int lineNumber,
|
int lineNumber,
|
||||||
int cursorPosition)
|
int cursorPosition)
|
||||||
@ -98,8 +119,13 @@ QString LLMClientInterface::сontextBefore(TextEditor::TextEditorWidget *widget,
|
|||||||
if (!widget)
|
if (!widget)
|
||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
QTextDocument *doc = widget->document();
|
DocumentContextReader reader(widget->textDocument());
|
||||||
DocumentContextReader reader(doc);
|
const auto ©right = reader.copyrightInfo();
|
||||||
|
|
||||||
|
logMessage(QString{"Line Number: %1"}.arg(lineNumber));
|
||||||
|
logMessage(QString("Copyright found %1 %2").arg(copyright.found).arg(copyright.endLine));
|
||||||
|
if (lineNumber < reader.findCopyright().endLine)
|
||||||
|
return QString();
|
||||||
|
|
||||||
QString contextBefore;
|
QString contextBefore;
|
||||||
if (settings().readFullFile()) {
|
if (settings().readFullFile()) {
|
||||||
@ -110,7 +136,8 @@ QString LLMClientInterface::сontextBefore(TextEditor::TextEditorWidget *widget,
|
|||||||
settings().readStringsBeforeCursor());
|
settings().readStringsBeforeCursor());
|
||||||
}
|
}
|
||||||
|
|
||||||
return contextBefore;
|
return QString("%1\n%2\n%3")
|
||||||
|
.arg(reader.getSpecificInstructions(), reader.getLanguageAndFileInfo(), contextBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LLMClientInterface::сontextAfter(TextEditor::TextEditorWidget *widget,
|
QString LLMClientInterface::сontextAfter(TextEditor::TextEditorWidget *widget,
|
||||||
@ -120,8 +147,9 @@ QString LLMClientInterface::сontextAfter(TextEditor::TextEditorWidget *widget,
|
|||||||
if (!widget)
|
if (!widget)
|
||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
QTextDocument *doc = widget->document();
|
DocumentContextReader reader(widget->textDocument());
|
||||||
DocumentContextReader reader(doc);
|
if (lineNumber < reader.findCopyright().endLine)
|
||||||
|
return QString();
|
||||||
|
|
||||||
QString contextAfter;
|
QString contextAfter;
|
||||||
if (settings().readFullFile()) {
|
if (settings().readFullFile()) {
|
||||||
@ -149,7 +177,7 @@ void LLMClientInterface::handleInitialize(const QJsonObject &request)
|
|||||||
result["capabilities"] = capabilities;
|
result["capabilities"] = capabilities;
|
||||||
|
|
||||||
QJsonObject serverInfo;
|
QJsonObject serverInfo;
|
||||||
serverInfo["name"] = "Ollama LSP Server";
|
serverInfo["name"] = "QodeAssist LSP Server";
|
||||||
serverInfo["version"] = "0.1";
|
serverInfo["version"] = "0.1";
|
||||||
result["serverInfo"] = serverInfo;
|
result["serverInfo"] = serverInfo;
|
||||||
|
|
||||||
@ -168,9 +196,7 @@ void LLMClientInterface::handleShutdown(const QJsonObject &request)
|
|||||||
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
emit messageReceived(LanguageServerProtocol::JsonRpcMessage(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::handleTextDocumentDidOpen(const QJsonObject &request)
|
void LLMClientInterface::handleTextDocumentDidOpen(const QJsonObject &request) {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMClientInterface::handleInitialized(const QJsonObject &request)
|
void LLMClientInterface::handleInitialized(const QJsonObject &request)
|
||||||
{
|
{
|
||||||
@ -200,6 +226,11 @@ void LLMClientInterface::handleLLMResponse(QNetworkReply *reply, const QJsonObje
|
|||||||
|
|
||||||
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
QJsonObject position = request["params"].toObject()["doc"].toObject()["position"].toObject();
|
||||||
|
|
||||||
|
if (!settings().multiLineCompletion()
|
||||||
|
&& processSingleLineCompletion(reply, request, accumulatedResponse)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isComplete || reply->isFinished()) {
|
if (isComplete || reply->isFinished()) {
|
||||||
if (isComplete) {
|
if (isComplete) {
|
||||||
auto cleanedCompletion = removeStopWords(accumulatedResponse);
|
auto cleanedCompletion = removeStopWords(accumulatedResponse);
|
||||||
@ -251,10 +282,7 @@ LLMClientInterface::ContextPair LLMClientInterface::prepareContext(
|
|||||||
|
|
||||||
void LLMClientInterface::updateProvider()
|
void LLMClientInterface::updateProvider()
|
||||||
{
|
{
|
||||||
m_serverUrl = QUrl(QString("%1:%2%3")
|
m_serverUrl = QUrl(QString("%1%2").arg(settings().url(), settings().endPoint()));
|
||||||
.arg(settings().url.value())
|
|
||||||
.arg(settings().port.value())
|
|
||||||
.arg(settings().endPoint.value()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
||||||
@ -293,22 +321,29 @@ void LLMClientInterface::sendCompletionToClient(const QString &completion,
|
|||||||
|
|
||||||
void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const ContextPair &prompt)
|
void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const ContextPair &prompt)
|
||||||
{
|
{
|
||||||
QJsonObject ollamaRequest = {{"model", settings().modelName.value()}, {"stream", true}};
|
QJsonObject providerRequest = {{"model", settings().modelName.value()}, {"stream", true}};
|
||||||
|
|
||||||
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
auto currentTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
||||||
currentTemplate->prepareRequest(ollamaRequest, prompt.prefix, prompt.suffix);
|
currentTemplate->prepareRequest(providerRequest, prompt.prefix, prompt.suffix);
|
||||||
|
|
||||||
auto &providerManager = LLMProvidersManager::instance();
|
auto &providerManager = LLMProvidersManager::instance();
|
||||||
providerManager.getCurrentProvider()->prepareRequest(ollamaRequest);
|
providerManager.getCurrentProvider()->prepareRequest(providerRequest);
|
||||||
|
|
||||||
logMessage(
|
logMessage(QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
||||||
QString("Sending request to llm: \nurl: %1\nRequest body:\n%2")
|
.arg(m_serverUrl.toString(),
|
||||||
.arg(m_serverUrl.toString())
|
QString::fromUtf8(
|
||||||
.arg(QString::fromUtf8(QJsonDocument(ollamaRequest).toJson(QJsonDocument::Indented))));
|
QJsonDocument(providerRequest).toJson(QJsonDocument::Indented))));
|
||||||
|
|
||||||
QNetworkRequest networkRequest(m_serverUrl);
|
QNetworkRequest networkRequest(m_serverUrl);
|
||||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
QNetworkReply *reply = m_manager->post(networkRequest, QJsonDocument(ollamaRequest).toJson());
|
|
||||||
|
if (providerRequest.contains("api_key")) {
|
||||||
|
QString apiKey = providerRequest["api_key"].toString();
|
||||||
|
networkRequest.setRawHeader("Authorization", QString("Bearer %1").arg(apiKey).toUtf8());
|
||||||
|
providerRequest.remove("api_key");
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply *reply = m_manager->post(networkRequest, QJsonDocument(providerRequest).toJson());
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
logMessage("Error: Failed to create network reply");
|
logMessage("Error: Failed to create network reply");
|
||||||
return;
|
return;
|
||||||
@ -325,7 +360,7 @@ void LLMClientInterface::sendLLMRequest(const QJsonObject &request, const Contex
|
|||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
m_activeRequests.remove(requestId);
|
m_activeRequests.remove(requestId);
|
||||||
if (reply->error() != QNetworkReply::NoError) {
|
if (reply->error() != QNetworkReply::NoError) {
|
||||||
logMessage(QString("Error in Ollama request: %1").arg(reply->errorString()));
|
logMessage(QString("Error in QodeAssist request: %1").arg(reply->errorString()));
|
||||||
} else {
|
} else {
|
||||||
logMessage("Request finished successfully");
|
logMessage("Request finished successfully");
|
||||||
}
|
}
|
||||||
@ -346,8 +381,6 @@ QString LLMClientInterface::removeStopWords(const QString &completion)
|
|||||||
return filteredCompletion;
|
return filteredCompletion;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLMClientInterface::parseCurrentMessage()
|
void LLMClientInterface::parseCurrentMessage() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QodeAssist
|
} // namespace QodeAssist
|
||||||
|
|||||||
@ -69,6 +69,9 @@ private:
|
|||||||
void handleInitialized(const QJsonObject &request);
|
void handleInitialized(const QJsonObject &request);
|
||||||
void handleExit(const QJsonObject &request);
|
void handleExit(const QJsonObject &request);
|
||||||
void handleCancelRequest(const QJsonObject &request);
|
void handleCancelRequest(const QJsonObject &request);
|
||||||
|
bool processSingleLineCompletion(QNetworkReply *reply,
|
||||||
|
const QJsonObject &request,
|
||||||
|
const QString &accumulatedCompletion);
|
||||||
|
|
||||||
QString сontextBefore(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
QString сontextBefore(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
||||||
QString сontextAfter(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
QString сontextAfter(TextEditor::TextEditorWidget *widget, int lineNumber, int cursorPosition);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Name" : "QodeAssist",
|
"Name" : "QodeAssist",
|
||||||
"Version" : "0.0.2",
|
"Version" : "0.0.6",
|
||||||
"CompatVersion" : "${IDE_VERSION_COMPAT}",
|
"CompatVersion" : "${IDE_VERSION_COMPAT}",
|
||||||
"Vendor" : "Petr Mironychev",
|
"Vendor" : "Petr Mironychev",
|
||||||
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
"Copyright" : "(C) ${IDE_COPYRIGHT_YEAR} Petr Mironychev, (C) ${IDE_COPYRIGHT_YEAR} The Qt Company Ltd",
|
||||||
@ -11,6 +11,6 @@ Alternatively, this file may be used under the terms of the GNU General Public L
|
|||||||
"Prerequisites:",
|
"Prerequisites:",
|
||||||
"- One of the supported LLM providers installed (e.g., Ollama or LM Studio)",
|
"- One of the supported LLM providers installed (e.g., Ollama or LM Studio)",
|
||||||
"- A compatible large language model downloaded for your chosen provider (e.g., CodeLlama, StarCoder2)"],
|
"- A compatible large language model downloaded for your chosen provider (e.g., CodeLlama, StarCoder2)"],
|
||||||
"Url" : "https://github.com/Palm1r",
|
"Url" : "https://github.com/Palm1r/QodeAssist",
|
||||||
${IDE_PLUGIN_DEPENDENCIES}
|
${IDE_PLUGIN_DEPENDENCIES}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,6 @@ const char ENABLE_AUTO_COMPLETE[] = "QodeAssist.enableAutoComplete";
|
|||||||
const char ENABLE_LOGGING[] = "QodeAssist.enableLogging";
|
const char ENABLE_LOGGING[] = "QodeAssist.enableLogging";
|
||||||
const char LLM_PROVIDERS[] = "QodeAssist.llmProviders";
|
const char LLM_PROVIDERS[] = "QodeAssist.llmProviders";
|
||||||
const char URL[] = "QodeAssist.url";
|
const char URL[] = "QodeAssist.url";
|
||||||
const char PORT[] = "QodeAssist.port";
|
|
||||||
const char END_POINT[] = "QodeAssist.endPoint";
|
const char END_POINT[] = "QodeAssist.endPoint";
|
||||||
const char MODEL_NAME[] = "QodeAssist.modelName";
|
const char MODEL_NAME[] = "QodeAssist.modelName";
|
||||||
const char SELECT_MODELS[] = "QodeAssist.selectModels";
|
const char SELECT_MODELS[] = "QodeAssist.selectModels";
|
||||||
@ -52,6 +51,9 @@ const char PROVIDER_PATHS[] = "QodeAssist.providerPaths";
|
|||||||
const char START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer";
|
const char START_SUGGESTION_TIMER[] = "QodeAssist.startSuggestionTimer";
|
||||||
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
|
const char MAX_FILE_THRESHOLD[] = "QodeAssist.maxFileThreshold";
|
||||||
const char OLLAMA_LIVETIME[] = "QodeAssist.ollamaLivetime";
|
const char OLLAMA_LIVETIME[] = "QodeAssist.ollamaLivetime";
|
||||||
|
const char SPECIFIC_INSTRUCTIONS[] = "QodeAssist.specificInstractions";
|
||||||
|
const char MULTILINE_COMPLETION[] = "QodeAssist.multilineCompletion";
|
||||||
|
const char API_KEY[] = "QodeAssist.apiKey";
|
||||||
|
|
||||||
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
const char QODE_ASSIST_GENERAL_OPTIONS_ID[] = "QodeAssist.GeneralOptions";
|
||||||
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
|
const char QODE_ASSIST_GENERAL_OPTIONS_CATEGORY[] = "QodeAssist.Category";
|
||||||
|
|||||||
@ -68,10 +68,6 @@ QodeAssistSettings::QodeAssistSettings()
|
|||||||
endPoint.setLabelText(Tr::tr("Endpoint:"));
|
endPoint.setLabelText(Tr::tr("Endpoint:"));
|
||||||
endPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
endPoint.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
|
||||||
port.setSettingsKey(Constants::PORT);
|
|
||||||
port.setLabelText(Tr::tr("Port:"));
|
|
||||||
port.setRange(1, 65535);
|
|
||||||
|
|
||||||
modelName.setSettingsKey(Constants::MODEL_NAME);
|
modelName.setSettingsKey(Constants::MODEL_NAME);
|
||||||
modelName.setLabelText(Tr::tr("LLM Name:"));
|
modelName.setLabelText(Tr::tr("LLM Name:"));
|
||||||
modelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
modelName.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
@ -97,7 +93,7 @@ QodeAssistSettings::QodeAssistSettings()
|
|||||||
|
|
||||||
readFullFile.setSettingsKey(Constants::READ_FULL_FILE);
|
readFullFile.setSettingsKey(Constants::READ_FULL_FILE);
|
||||||
readFullFile.setLabelText(Tr::tr("Read Full File"));
|
readFullFile.setLabelText(Tr::tr("Read Full File"));
|
||||||
readFullFile.setDefaultValue(true);
|
readFullFile.setDefaultValue(false);
|
||||||
|
|
||||||
maxFileThreshold.setSettingsKey(Constants::MAX_FILE_THRESHOLD);
|
maxFileThreshold.setSettingsKey(Constants::MAX_FILE_THRESHOLD);
|
||||||
maxFileThreshold.setLabelText(Tr::tr("Max File Threshold:"));
|
maxFileThreshold.setLabelText(Tr::tr("Max File Threshold:"));
|
||||||
@ -106,11 +102,11 @@ QodeAssistSettings::QodeAssistSettings()
|
|||||||
|
|
||||||
readStringsBeforeCursor.setSettingsKey(Constants::READ_STRINGS_BEFORE_CURSOR);
|
readStringsBeforeCursor.setSettingsKey(Constants::READ_STRINGS_BEFORE_CURSOR);
|
||||||
readStringsBeforeCursor.setLabelText(Tr::tr("Read Strings Before Cursor"));
|
readStringsBeforeCursor.setLabelText(Tr::tr("Read Strings Before Cursor"));
|
||||||
readStringsBeforeCursor.setDefaultValue(60);
|
readStringsBeforeCursor.setDefaultValue(50);
|
||||||
|
|
||||||
readStringsAfterCursor.setSettingsKey(Constants::READ_STRINGS_AFTER_CURSOR);
|
readStringsAfterCursor.setSettingsKey(Constants::READ_STRINGS_AFTER_CURSOR);
|
||||||
readStringsAfterCursor.setLabelText(Tr::tr("Read Strings After Cursor"));
|
readStringsAfterCursor.setLabelText(Tr::tr("Read Strings After Cursor"));
|
||||||
readStringsAfterCursor.setDefaultValue(40);
|
readStringsAfterCursor.setDefaultValue(30);
|
||||||
|
|
||||||
maxTokens.setSettingsKey(Constants::MAX_TOKENS);
|
maxTokens.setSettingsKey(Constants::MAX_TOKENS);
|
||||||
maxTokens.setLabelText(Tr::tr("Max Tokens"));
|
maxTokens.setLabelText(Tr::tr("Max Tokens"));
|
||||||
@ -123,15 +119,15 @@ QodeAssistSettings::QodeAssistSettings()
|
|||||||
topP.setSettingsKey(Constants::TOP_P);
|
topP.setSettingsKey(Constants::TOP_P);
|
||||||
topP.setLabelText(Tr::tr("top_p"));
|
topP.setLabelText(Tr::tr("top_p"));
|
||||||
topP.setDefaultValue(0.9);
|
topP.setDefaultValue(0.9);
|
||||||
topP.setRange(0.0, 10.0);
|
topP.setRange(0.0, 1.0);
|
||||||
|
|
||||||
useTopK.setSettingsKey(Constants::USE_TOP_K);
|
useTopK.setSettingsKey(Constants::USE_TOP_K);
|
||||||
useTopK.setDefaultValue(false);
|
useTopK.setDefaultValue(false);
|
||||||
|
|
||||||
topK.setSettingsKey(Constants::TOP_K);
|
topK.setSettingsKey(Constants::TOP_K);
|
||||||
topK.setLabelText(Tr::tr("top_k"));
|
topK.setLabelText(Tr::tr("top_k"));
|
||||||
topK.setDefaultValue(0.1);
|
topK.setDefaultValue(50);
|
||||||
topK.setRange(0, 10.0);
|
topK.setRange(1, 1000);
|
||||||
|
|
||||||
usePresencePenalty.setSettingsKey(Constants::USE_PRESENCE_PENALTY);
|
usePresencePenalty.setSettingsKey(Constants::USE_PRESENCE_PENALTY);
|
||||||
usePresencePenalty.setDefaultValue(false);
|
usePresencePenalty.setDefaultValue(false);
|
||||||
@ -157,7 +153,23 @@ QodeAssistSettings::QodeAssistSettings()
|
|||||||
startSuggestionTimer.setRange(10, 10000);
|
startSuggestionTimer.setRange(10, 10000);
|
||||||
startSuggestionTimer.setDefaultValue(500);
|
startSuggestionTimer.setDefaultValue(500);
|
||||||
|
|
||||||
|
specificInstractions.setSettingsKey(Constants::SPECIFIC_INSTRUCTIONS);
|
||||||
|
specificInstractions.setDisplayStyle(Utils::StringAspect::TextEditDisplay);
|
||||||
|
specificInstractions.setLabelText(
|
||||||
|
Tr::tr("Instructions: Please keep %1 for languge name, warning, it shouldn't too big"));
|
||||||
|
specificInstractions.setDefaultValue(
|
||||||
|
"You are an expert %1 code completion AI."
|
||||||
|
"CRITICAL: Please provide minimal the best possible code completion suggestions.\n");
|
||||||
|
|
||||||
resetToDefaults.m_buttonText = Tr::tr("Reset to Defaults");
|
resetToDefaults.m_buttonText = Tr::tr("Reset to Defaults");
|
||||||
|
multiLineCompletion.setSettingsKey(Constants::MULTILINE_COMPLETION);
|
||||||
|
multiLineCompletion.setDefaultValue(true);
|
||||||
|
multiLineCompletion.setLabelText(Tr::tr("Enable Multiline Completion"));
|
||||||
|
|
||||||
|
apiKey.setSettingsKey(Constants::API_KEY);
|
||||||
|
apiKey.setLabelText(Tr::tr("API Key:"));
|
||||||
|
apiKey.setDisplayStyle(Utils::StringAspect::LineEditDisplay);
|
||||||
|
apiKey.setPlaceHolderText(Tr::tr("Enter your API key here"));
|
||||||
|
|
||||||
const auto &manager = LLMProvidersManager::instance();
|
const auto &manager = LLMProvidersManager::instance();
|
||||||
if (!manager.getProviderNames().isEmpty()) {
|
if (!manager.getProviderNames().isEmpty()) {
|
||||||
@ -185,7 +197,6 @@ QodeAssistSettings::QodeAssistSettings()
|
|||||||
readStringsBeforeCursor.setEnabled(!readFullFile());
|
readStringsBeforeCursor.setEnabled(!readFullFile());
|
||||||
PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.stringValue());
|
PromptTemplateManager::instance().setCurrentTemplate(fimPrompts.stringValue());
|
||||||
LLMProvidersManager::instance().setCurrentProvider(llmProviders.stringValue());
|
LLMProvidersManager::instance().setCurrentProvider(llmProviders.stringValue());
|
||||||
updateProviderSettings();
|
|
||||||
|
|
||||||
setLoggingEnabled(enableLogging());
|
setLoggingEnabled(enableLogging());
|
||||||
|
|
||||||
@ -195,21 +206,24 @@ QodeAssistSettings::QodeAssistSettings()
|
|||||||
return Column{Group{title(Tr::tr("General Settings")),
|
return Column{Group{title(Tr::tr("General Settings")),
|
||||||
Form{Column{enableQodeAssist,
|
Form{Column{enableQodeAssist,
|
||||||
enableAutoComplete,
|
enableAutoComplete,
|
||||||
|
multiLineCompletion,
|
||||||
enableLogging,
|
enableLogging,
|
||||||
Row{Stretch{1}, resetToDefaults}}}},
|
Row{Stretch{1}, resetToDefaults}}}},
|
||||||
Group{title(Tr::tr("LLM Providers")),
|
Group{title(Tr::tr("LLM Providers")),
|
||||||
Form{Column{llmProviders, Row{url, port, endPoint}, providerPaths}}},
|
Form{Column{llmProviders, Row{url, endPoint}, providerPaths}}},
|
||||||
Group{title(Tr::tr("LLM Model Settings")),
|
Group{title(Tr::tr("LLM Model Settings")),
|
||||||
Form{Column{Row{selectModels, modelName}}}},
|
Form{Column{Row{selectModels, modelName}}}},
|
||||||
Group{title(Tr::tr("FIM Prompt Settings")),
|
Group{title(Tr::tr("FIM Prompt Settings")),
|
||||||
Form{Column{fimPrompts,
|
Form{Column{fimPrompts,
|
||||||
readFullFile,
|
readFullFile,
|
||||||
maxFileThreshold,
|
maxFileThreshold,
|
||||||
ollamaLivetime,
|
|
||||||
temperature,
|
|
||||||
maxTokens,
|
|
||||||
readStringsBeforeCursor,
|
readStringsBeforeCursor,
|
||||||
readStringsAfterCursor,
|
readStringsAfterCursor,
|
||||||
|
ollamaLivetime,
|
||||||
|
apiKey,
|
||||||
|
specificInstractions,
|
||||||
|
temperature,
|
||||||
|
maxTokens,
|
||||||
startSuggestionTimer,
|
startSuggestionTimer,
|
||||||
Row{useTopP, topP, Stretch{1}},
|
Row{useTopP, topP, Stretch{1}},
|
||||||
Row{useTopK, topK, Stretch{1}},
|
Row{useTopK, topK, Stretch{1}},
|
||||||
@ -269,7 +283,6 @@ void QodeAssistSettings::updateProviderSettings()
|
|||||||
if (provider) {
|
if (provider) {
|
||||||
logMessage(QString("currentProvider %1").arg(provider->name()));
|
logMessage(QString("currentProvider %1").arg(provider->name()));
|
||||||
url.setValue(provider->url());
|
url.setValue(provider->url());
|
||||||
port.setValue(provider->defaultPort());
|
|
||||||
endPoint.setValue(provider->completionEndpoint());
|
endPoint.setValue(provider->completionEndpoint());
|
||||||
ollamaLivetime.setEnabled(provider->name() == "Ollama");
|
ollamaLivetime.setEnabled(provider->name() == "Ollama");
|
||||||
}
|
}
|
||||||
@ -328,7 +341,6 @@ void QodeAssistSettings::resetSettingsToDefaults()
|
|||||||
resetAspect(enableAutoComplete);
|
resetAspect(enableAutoComplete);
|
||||||
resetAspect(llmProviders);
|
resetAspect(llmProviders);
|
||||||
resetAspect(url);
|
resetAspect(url);
|
||||||
resetAspect(port);
|
|
||||||
resetAspect(endPoint);
|
resetAspect(endPoint);
|
||||||
resetAspect(modelName);
|
resetAspect(modelName);
|
||||||
resetAspect(fimPrompts);
|
resetAspect(fimPrompts);
|
||||||
@ -349,6 +361,7 @@ void QodeAssistSettings::resetSettingsToDefaults()
|
|||||||
resetAspect(startSuggestionTimer);
|
resetAspect(startSuggestionTimer);
|
||||||
resetAspect(enableLogging);
|
resetAspect(enableLogging);
|
||||||
resetAspect(ollamaLivetime);
|
resetAspect(ollamaLivetime);
|
||||||
|
resetAspect(specificInstractions);
|
||||||
|
|
||||||
updateProviderSettings();
|
updateProviderSettings();
|
||||||
apply();
|
apply();
|
||||||
|
|||||||
@ -63,7 +63,6 @@ public:
|
|||||||
|
|
||||||
Utils::SelectionAspect llmProviders{this};
|
Utils::SelectionAspect llmProviders{this};
|
||||||
Utils::StringAspect url{this};
|
Utils::StringAspect url{this};
|
||||||
Utils::IntegerAspect port{this};
|
|
||||||
Utils::StringAspect endPoint{this};
|
Utils::StringAspect endPoint{this};
|
||||||
|
|
||||||
Utils::StringAspect modelName{this};
|
Utils::StringAspect modelName{this};
|
||||||
@ -81,7 +80,7 @@ public:
|
|||||||
Utils::DoubleAspect topP{this};
|
Utils::DoubleAspect topP{this};
|
||||||
|
|
||||||
Utils::BoolAspect useTopK{this};
|
Utils::BoolAspect useTopK{this};
|
||||||
Utils::DoubleAspect topK{this};
|
Utils::IntegerAspect topK{this};
|
||||||
|
|
||||||
Utils::BoolAspect usePresencePenalty{this};
|
Utils::BoolAspect usePresencePenalty{this};
|
||||||
Utils::DoubleAspect presencePenalty{this};
|
Utils::DoubleAspect presencePenalty{this};
|
||||||
@ -95,6 +94,10 @@ public:
|
|||||||
Utils::IntegerAspect maxFileThreshold{this};
|
Utils::IntegerAspect maxFileThreshold{this};
|
||||||
|
|
||||||
Utils::StringAspect ollamaLivetime{this};
|
Utils::StringAspect ollamaLivetime{this};
|
||||||
|
Utils::StringAspect specificInstractions{this};
|
||||||
|
Utils::BoolAspect multiLineCompletion{this};
|
||||||
|
|
||||||
|
Utils::StringAspect apiKey{this};
|
||||||
|
|
||||||
ButtonAspect resetToDefaults{this};
|
ButtonAspect resetToDefaults{this};
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ inline void logMessage(const QString &message, bool silent = true)
|
|||||||
if (!loggingEnabled())
|
if (!loggingEnabled())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const QString prefixedMessage = QLatin1String("[QLLamaAssist] ") + message;
|
const QString prefixedMessage = QLatin1String("[Qode Assist] ") + message;
|
||||||
if (silent) {
|
if (silent) {
|
||||||
Core::MessageManager::writeSilently(prefixedMessage);
|
Core::MessageManager::writeSilently(prefixedMessage);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
# QodeAssist
|
# QodeAssist
|
||||||
|
[](https://github.com/Palm1r/QodeAssist/actions/workflows/build_cmake.yml)
|
||||||
|
|
||||||
QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
|
QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides intelligent code completion and suggestions for C++ and QML, leveraging large language models through local providers like Ollama. Enhance your coding productivity with context-aware AI assistance directly in your Qt development environment.
|
||||||
|
|
||||||
@ -6,9 +7,10 @@ QodeAssist is an AI-powered coding assistant plugin for Qt Creator. It provides
|
|||||||
QodeAssist currently supports the following LLM (Large Language Model) providers:
|
QodeAssist currently supports the following LLM (Large Language Model) providers:
|
||||||
- [Ollama](https://ollama.com)
|
- [Ollama](https://ollama.com)
|
||||||
- [LM Studio](https://lmstudio.ai)
|
- [LM Studio](https://lmstudio.ai)
|
||||||
|
- OpenAI compatible providers
|
||||||
|
|
||||||
## Supported Models
|
## Supported Models
|
||||||
QodeAssist has been tested with the following language models:
|
QodeAssist has been tested with the following language models, all trained for Fill-in-theMiddle:
|
||||||
|
|
||||||
Ollama:
|
Ollama:
|
||||||
- [starcoder2](https://ollama.com/library/starcoder2)
|
- [starcoder2](https://ollama.com/library/starcoder2)
|
||||||
@ -38,7 +40,8 @@ ollama run starcoder2:7b
|
|||||||
```
|
```
|
||||||
4. Download the QodeAssist plugin.
|
4. Download the QodeAssist plugin.
|
||||||
5. Launch Qt Creator and install the plugin:
|
5. Launch Qt Creator and install the plugin:
|
||||||
- Go to About -> About Plugins
|
- Go to MacOS: Qt Creator -> About Plugins...
|
||||||
|
Windows\Linux: Help -> About Plugins...
|
||||||
- Click on "Install Plugin..."
|
- Click on "Install Plugin..."
|
||||||
- Select the downloaded QodeAssist plugin archive file
|
- Select the downloaded QodeAssist plugin archive file
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,6 @@ public:
|
|||||||
|
|
||||||
virtual QString name() const = 0;
|
virtual QString name() const = 0;
|
||||||
virtual QString url() const = 0;
|
virtual QString url() const = 0;
|
||||||
virtual int defaultPort() const = 0;
|
|
||||||
virtual QString completionEndpoint() const = 0;
|
virtual QString completionEndpoint() const = 0;
|
||||||
|
|
||||||
virtual void prepareRequest(QJsonObject &request) = 0;
|
virtual void prepareRequest(QJsonObject &request) = 0;
|
||||||
|
|||||||
@ -39,12 +39,7 @@ QString LMStudioProvider::name() const
|
|||||||
|
|
||||||
QString LMStudioProvider::url() const
|
QString LMStudioProvider::url() const
|
||||||
{
|
{
|
||||||
return "http://localhost";
|
return "http://localhost:1234";
|
||||||
}
|
|
||||||
|
|
||||||
int LMStudioProvider::defaultPort() const
|
|
||||||
{
|
|
||||||
return 1234;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString LMStudioProvider::completionEndpoint() const
|
QString LMStudioProvider::completionEndpoint() const
|
||||||
|
|||||||
@ -30,7 +30,6 @@ public:
|
|||||||
|
|
||||||
QString name() const override;
|
QString name() const override;
|
||||||
QString url() const override;
|
QString url() const override;
|
||||||
int defaultPort() const override;
|
|
||||||
QString completionEndpoint() const override;
|
QString completionEndpoint() const override;
|
||||||
void prepareRequest(QJsonObject &request) override;
|
void prepareRequest(QJsonObject &request) override;
|
||||||
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
||||||
|
|||||||
@ -39,12 +39,7 @@ QString OllamaProvider::name() const
|
|||||||
|
|
||||||
QString OllamaProvider::url() const
|
QString OllamaProvider::url() const
|
||||||
{
|
{
|
||||||
return "http://localhost";
|
return "http://localhost:11434";
|
||||||
}
|
|
||||||
|
|
||||||
int OllamaProvider::defaultPort() const
|
|
||||||
{
|
|
||||||
return 11434;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString OllamaProvider::completionEndpoint() const
|
QString OllamaProvider::completionEndpoint() const
|
||||||
|
|||||||
@ -30,7 +30,6 @@ public:
|
|||||||
|
|
||||||
QString name() const override;
|
QString name() const override;
|
||||||
QString url() const override;
|
QString url() const override;
|
||||||
int defaultPort() const override;
|
|
||||||
QString completionEndpoint() const override;
|
QString completionEndpoint() const override;
|
||||||
void prepareRequest(QJsonObject &request) override;
|
void prepareRequest(QJsonObject &request) override;
|
||||||
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
||||||
|
|||||||
124
providers/OpenAICompatProvider.cpp
Normal file
124
providers/OpenAICompatProvider.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "OpenAICompatProvider.h"
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QProcess>
|
||||||
|
|
||||||
|
#include "PromptTemplateManager.hpp"
|
||||||
|
#include "QodeAssistSettings.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Providers {
|
||||||
|
|
||||||
|
OpenAICompatProvider::OpenAICompatProvider() {}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::name() const
|
||||||
|
{
|
||||||
|
return "OpenAI Compatible (experimental)";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::url() const
|
||||||
|
{
|
||||||
|
return "http://localhost:1234";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString OpenAICompatProvider::completionEndpoint() const
|
||||||
|
{
|
||||||
|
return "/v1/chat/completions";
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenAICompatProvider::prepareRequest(QJsonObject &request)
|
||||||
|
{
|
||||||
|
const auto ¤tTemplate = PromptTemplateManager::instance().getCurrentTemplate();
|
||||||
|
|
||||||
|
if (request.contains("prompt")) {
|
||||||
|
QJsonArray messages{
|
||||||
|
{QJsonObject{{"role", "user"}, {"content", request.take("prompt").toString()}}}};
|
||||||
|
request["messages"] = std::move(messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
request["max_tokens"] = settings().maxTokens();
|
||||||
|
request["temperature"] = settings().temperature();
|
||||||
|
request["stop"] = QJsonArray::fromStringList(currentTemplate->stopWords());
|
||||||
|
if (settings().useTopP())
|
||||||
|
request["top_p"] = settings().topP();
|
||||||
|
if (settings().useTopK())
|
||||||
|
request["top_k"] = settings().topK();
|
||||||
|
if (settings().useFrequencyPenalty())
|
||||||
|
request["frequency_penalty"] = settings().frequencyPenalty();
|
||||||
|
if (settings().usePresencePenalty())
|
||||||
|
request["presence_penalty"] = settings().presencePenalty();
|
||||||
|
|
||||||
|
const QString &apiKey = settings().apiKey.value();
|
||||||
|
if (!apiKey.isEmpty()) {
|
||||||
|
request["api_key"] = apiKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OpenAICompatProvider::handleResponse(QNetworkReply *reply, QString &accumulatedResponse)
|
||||||
|
{
|
||||||
|
bool isComplete = false;
|
||||||
|
while (reply->canReadLine()) {
|
||||||
|
QByteArray line = reply->readLine().trimmed();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line == "data: [DONE]") {
|
||||||
|
isComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line.startsWith("data: ")) {
|
||||||
|
line = line.mid(6); // Remove "data: " prefix
|
||||||
|
}
|
||||||
|
QJsonDocument jsonResponse = QJsonDocument::fromJson(line);
|
||||||
|
if (jsonResponse.isNull()) {
|
||||||
|
qWarning() << "Invalid JSON response from LM Studio:" << line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QJsonObject responseObj = jsonResponse.object();
|
||||||
|
if (responseObj.contains("choices")) {
|
||||||
|
QJsonArray choices = responseObj["choices"].toArray();
|
||||||
|
if (!choices.isEmpty()) {
|
||||||
|
QJsonObject choice = choices.first().toObject();
|
||||||
|
QJsonObject delta = choice["delta"].toObject();
|
||||||
|
if (delta.contains("content")) {
|
||||||
|
QString completion = delta["content"].toString();
|
||||||
|
|
||||||
|
accumulatedResponse += completion;
|
||||||
|
}
|
||||||
|
if (choice["finish_reason"].toString() == "stop") {
|
||||||
|
isComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> OpenAICompatProvider::getInstalledModels(const Utils::Environment &env)
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Providers
|
||||||
39
providers/OpenAICompatProvider.h
Normal file
39
providers/OpenAICompatProvider.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LLMProvider.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Providers {
|
||||||
|
|
||||||
|
class OpenAICompatProvider : public LLMProvider
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenAICompatProvider();
|
||||||
|
|
||||||
|
QString name() const override;
|
||||||
|
QString url() const override;
|
||||||
|
QString completionEndpoint() const override;
|
||||||
|
void prepareRequest(QJsonObject &request) override;
|
||||||
|
bool handleResponse(QNetworkReply *reply, QString &accumulatedResponse) override;
|
||||||
|
QList<QString> getInstalledModels(const Utils::Environment &env) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Providers
|
||||||
@ -43,7 +43,9 @@
|
|||||||
#include "QodeAssistClient.hpp"
|
#include "QodeAssistClient.hpp"
|
||||||
#include "providers/LMStudioProvider.hpp"
|
#include "providers/LMStudioProvider.hpp"
|
||||||
#include "providers/OllamaProvider.hpp"
|
#include "providers/OllamaProvider.hpp"
|
||||||
|
#include "providers/OpenAICompatProvider.h"
|
||||||
#include "templates/CodeLLamaTemplate.hpp"
|
#include "templates/CodeLLamaTemplate.hpp"
|
||||||
|
#include "templates/CodeQwenChat.hpp"
|
||||||
#include "templates/StarCoder2Template.hpp"
|
#include "templates/StarCoder2Template.hpp"
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
@ -72,19 +74,23 @@ public:
|
|||||||
auto &providerManager = LLMProvidersManager::instance();
|
auto &providerManager = LLMProvidersManager::instance();
|
||||||
providerManager.registerProvider<Providers::OllamaProvider>();
|
providerManager.registerProvider<Providers::OllamaProvider>();
|
||||||
providerManager.registerProvider<Providers::LMStudioProvider>();
|
providerManager.registerProvider<Providers::LMStudioProvider>();
|
||||||
|
providerManager.registerProvider<Providers::OpenAICompatProvider>();
|
||||||
|
|
||||||
auto &templateManager = PromptTemplateManager::instance();
|
auto &templateManager = PromptTemplateManager::instance();
|
||||||
templateManager.registerTemplate<Templates::CodeLLamaTemplate>();
|
templateManager.registerTemplate<Templates::CodeLLamaTemplate>();
|
||||||
templateManager.registerTemplate<Templates::StarCoder2Template>();
|
templateManager.registerTemplate<Templates::StarCoder2Template>();
|
||||||
|
templateManager.registerTemplate<Templates::CodeQwenChatTemplate>();
|
||||||
|
|
||||||
Utils::Icon QCODEASSIST_ICON(
|
Utils::Icon QCODEASSIST_ICON(
|
||||||
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
|
{{":/resources/images/qoderassist-icon.png", Utils::Theme::IconsBaseColor}});
|
||||||
|
|
||||||
ActionBuilder requestAction(this, Constants::QODE_ASSIST_REQUEST_SUGGESTION);
|
ActionBuilder requestAction(this, Constants::QODE_ASSIST_REQUEST_SUGGESTION);
|
||||||
requestAction.setToolTip(
|
requestAction.setToolTip(
|
||||||
Tr::tr("Request Ollama suggestion at the current editor's cursor position."));
|
Tr::tr("Generate Qode Assist suggestion at the current cursor position."));
|
||||||
requestAction.setText(Tr::tr("Request Ollama Suggestion"));
|
requestAction.setText(Tr::tr("Request QodeAssist Suggestion"));
|
||||||
requestAction.setIcon(QCODEASSIST_ICON.icon());
|
requestAction.setIcon(QCODEASSIST_ICON.icon());
|
||||||
|
const QKeySequence defaultShortcut = QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Q);
|
||||||
|
requestAction.setDefaultKeySequence(defaultShortcut);
|
||||||
requestAction.addOnTriggered(this, [this] {
|
requestAction.addOnTriggered(this, [this] {
|
||||||
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
|
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget()) {
|
||||||
if (m_qodeAssistClient && m_qodeAssistClient->reachable()) {
|
if (m_qodeAssistClient && m_qodeAssistClient->reachable()) {
|
||||||
|
|||||||
44
templates/CodeQwenChat.hpp
Normal file
44
templates/CodeQwenChat.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "PromptTemplate.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::Templates {
|
||||||
|
|
||||||
|
class CodeQwenChatTemplate : public PromptTemplate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name() const override { return "CodeQwenChat (experimental)"; }
|
||||||
|
QString promptTemplate() const override { return "\n### Instruction:%1%2 ### Response:\n"; }
|
||||||
|
QStringList stopWords() const override
|
||||||
|
{
|
||||||
|
return QStringList() << "### Instruction:" << "### Response:" << "\n\n### ";
|
||||||
|
}
|
||||||
|
void prepareRequest(QJsonObject &request,
|
||||||
|
const QString &prefix,
|
||||||
|
const QString &suffix) const override
|
||||||
|
{
|
||||||
|
QString formattedPrompt = promptTemplate().arg(prefix, suffix);
|
||||||
|
request["prompt"] = formattedPrompt;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::Templates
|
||||||
Reference in New Issue
Block a user