Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [aiManager] add interface #1031

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/base/ai/abstractllm.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class AbstractLLM : public QObject
explicit AbstractLLM(QObject *parent = nullptr);
virtual ~AbstractLLM() {}

virtual QString modelName() const = 0;
virtual QString modelPath() const = 0;
virtual bool checkValid(QString *errStr) = 0;
virtual QJsonObject create(const Conversation &conversation) = 0;
Expand All @@ -37,6 +38,7 @@ class AbstractLLM : public QObject
virtual void cancel() = 0;
virtual void setMaxTokens(int maxToken) = 0;
virtual Conversation *getCurrentConversation() = 0;
virtual bool isIdle() = 0;

signals:
void dataReceived(const QString &data, ResponseState statu);
Expand Down
3 changes: 3 additions & 0 deletions src/common/util/eventdefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ OPI_OBJECT(session,
OPI_INTERFACE(sessionRenamed, "oldName", "newName")
OPI_INTERFACE(sessionRemoved, "session")
)
OPI_OBJECT(ai,
OPI_INTERFACE(LLMChanged)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个事件只表示模型数量发生了改变,事件名应该为modelCountChanged

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不是 是模型发生了变化就会发送,不管是模型被删除还是新增。 判断的那里设置Changed,两个判断 一个是模型被删除 一个是有新增

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

包括先删除再新增,数量不变,但是模型变化了,也会发送事件

)

struct AnalysedData
{
Expand Down
15 changes: 14 additions & 1 deletion src/plugins/aimanager/aimanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
#include "aimanager.h"
#include "services/ai/aiservice.h"
#include "openai/openaicompatiblellm.h"
#include "services/option/optionmanager.h"

Check warning on line 8 in src/plugins/aimanager/aimanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "services/option/optionmanager.h" not found.
#include "option/detailwidget.h"
#include "common/util/eventdefinitions.h"

Check warning on line 10 in src/plugins/aimanager/aimanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "common/util/eventdefinitions.h" not found.

#include <QMap>

Check warning on line 12 in src/plugins/aimanager/aimanager.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QMap> not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace dpfservice;

Expand Down Expand Up @@ -83,10 +84,22 @@

void AiManager::readLLMFromOption()
{
auto currentModels = d->models;
bool changed = false;
d->models.clear();

QMap<QString, QVariant> map = OptionManager::getInstance()->getValue(kCATEGORY_CUSTOMMODELS, kCATEGORY_OPTIONKEY).toMap();
auto LLMs = map.value(kCATEGORY_CUSTOMMODELS);
if (LLMs.toList().size() != currentModels.size())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

不应该通过个数来判断,模型自身参数的变化也应该有通知

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

通过个数判断是为了快速判断模型有没有被删除,不然需要额外用一个循环来判断当前模型是否被删除。 现在还没有支持模型的参数修改,只有新增和删除。

changed = true;

for (auto llmInfo : LLMs.toList()) {
appendModel(LLMInfo::fromVariantMap(llmInfo.toMap()));
LLMInfo info = LLMInfo::fromVariantMap(llmInfo.toMap());
if (!currentModels.contains(info))
changed = true;
appendModel(info);
}

if (changed)
ai.LLMChanged();
}
124 changes: 101 additions & 23 deletions src/plugins/aimanager/openai/openaicompatibleconversation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,107 @@

#include "openaicompatibleconversation.h"

#include <QJsonDocument>

Check warning on line 7 in src/plugins/aimanager/openai/openaicompatibleconversation.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QJsonDocument> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QRegularExpression>

Check warning on line 8 in src/plugins/aimanager/openai/openaicompatibleconversation.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QRegularExpression> not found. Please note: Cppcheck does not need standard library headers to get proper results.

void mergeFunctionCall(QJsonObject &functionCall, const QJsonObject &delta)
{
if (delta.contains("name")) {
functionCall["name"] = functionCall["name"].toString() + delta.value("name").toString();
}
if (delta.contains("arguments")) {
functionCall["arguments"] = functionCall["arguments"].toString() + delta.value("arguments").toString();
}
}

void mergeToolCallMap(QMap<int, QJsonObject> &toolCallMaps, const QJsonArray &tool_calls)
{
for (const QJsonValue &tool_call : tool_calls) {
const QJsonObject &toolCallObj = tool_call.toObject();
int index = toolCallObj["index"].toInt();

if (!toolCallMaps[index].contains("function")) {
toolCallMaps[index]["function"] = QJsonObject();
}

toolCallMaps[index]["index"] = index;

if (toolCallObj.contains("id")) {
toolCallMaps[index]["id"] = toolCallObj.value("id");
}
if (toolCallObj.contains("type")) {
toolCallMaps[index]["type"] = toolCallObj.value("type");
}

QJsonObject toolFun = toolCallMaps[index]["function"].toObject();

if (const QJsonValue &tmpToolFunVal = toolCallObj.value("function"); !tmpToolFunVal.isUndefined()) {
const QJsonObject &tmpToolFun = tmpToolFunVal.toObject();
if (tmpToolFun.contains("name")) {
toolFun["name"] = toolFun["name"].toString() + tmpToolFun.value("name").toString();
}
if (tmpToolFun.contains("arguments")) {
toolFun["arguments"] = toolFun["arguments"].toString() + tmpToolFun.value("arguments").toString();
}
}

toolCallMaps[index]["function"] = toolFun;
}
}


OpenAiCompatibleConversation::OpenAiCompatibleConversation()
{
}

QJsonObject OpenAiCompatibleConversation::parseContentString(const QString &content)

Check warning on line 60 in src/plugins/aimanager/openai/openaicompatibleconversation.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Local variable 'content' shadows outer argument
{
QString deltacontent;

QRegularExpression regex(R"(data:\s*\{(.*)\})");
QRegularExpressionMatchIterator iter = regex.globalMatch(content);
QString finishReason;
QJsonObject functionCall;
QMap<int, QJsonObject> toolCallMaps;

QString finishReason = "";
while (iter.hasNext()) {
QRegularExpressionMatch match = iter.next();
QString matchString = match.captured(0);

int startIndex = matchString.indexOf('{');
int endIndex = matchString.lastIndexOf('}');
if (startIndex < 0 || endIndex <= startIndex) {
continue;
}
QString content = matchString.mid(startIndex, endIndex - startIndex + 1);
QJsonObject j = QJsonDocument::fromJson(content.toUtf8()).object();

if (!j.contains("choices")) {
continue;
}

if (startIndex >= 0 && endIndex > startIndex) {
QString content = matchString.mid(startIndex, endIndex - startIndex + 1);

QJsonObject j = QJsonDocument::fromJson(content.toUtf8()).object();
if (j.contains("choices")) {
const QJsonArray &choices = j["choices"].toArray();
for (auto choice = choices.begin(); choice != choices.end(); choice++) {
const QJsonObject &cj = choice->toObject();
if (cj.contains("finish_reason"))
finishReason = cj["finish_reason"].toString();
if (cj.contains("delta")) {
const QJsonObject &delta = cj["delta"].toObject();
if (delta.contains("content")) {
const QString &deltaData = delta["content"].toString();
deltacontent += deltaData;
}
} else if (cj.contains("text")) {
deltacontent += cj["text"].toString();
}
const QJsonArray &choices = j["choices"].toArray();
for (const QJsonValue &choice : choices) {
const QJsonObject &cj = choice.toObject();

if (cj.contains("finish_reason")) {
finishReason = cj["finish_reason"].toString();
}

if (cj.contains("delta")) {
const QJsonObject &delta = cj["delta"].toObject();

if (delta.contains("content")) {
deltacontent += delta["content"].toString();
}

if (delta.contains("function_call")) {
mergeFunctionCall(functionCall, delta.value("function_call").toObject());
}

if (delta.contains("tool_calls")) {
mergeToolCallMap(toolCallMaps, delta.value("tool_calls").toArray());
}
} else if (cj.contains("text")) {
deltacontent += cj["text"].toString();
}
}
}
Expand All @@ -55,8 +114,27 @@
response["content"] = deltacontent;
}

if (!finishReason.isEmpty())
if (!functionCall.isEmpty() || !toolCallMaps.isEmpty()) {
QJsonObject tools;

if (!functionCall.isEmpty()) {
tools["function_call"] = functionCall;
}

if (!toolCallMaps.isEmpty()) {
QJsonArray toolCalls;
for (const auto &toolCallObj : toolCallMaps) {
toolCalls << toolCallObj;
}
tools["tool_calls"] = toolCalls;
}

response["tools"] = tools;
}

if (!finishReason.isEmpty()) {
response["finish_reason"] = finishReason;
}

return response;
}
Expand Down
13 changes: 13 additions & 0 deletions src/plugins/aimanager/openai/openaicompatiblellm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@
delete d;
}

QString OpenAiCompatibleLLM::modelName() const

Check warning on line 122 in src/plugins/aimanager/openai/openaicompatiblellm.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'modelName' is never used.
{
return d->modelName;
}

QString OpenAiCompatibleLLM::modelPath() const
{
return d->modelPath;
Expand Down Expand Up @@ -196,6 +201,9 @@

void OpenAiCompatibleLLM::request(const QJsonObject &data)
{
if (d->waitingResponse)
return;

QByteArray body = QJsonDocument(data).toJson();
d->httpResult.clear();
d->waitingResponse = true;
Expand Down Expand Up @@ -339,3 +347,8 @@
{
d->maxTokens = maxTokens;
}

bool OpenAiCompatibleLLM::isIdle()

Check warning on line 351 in src/plugins/aimanager/openai/openaicompatiblellm.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

The function 'isIdle' is never used.
{
return !d->waitingResponse;
}
2 changes: 2 additions & 0 deletions src/plugins/aimanager/openai/openaicompatiblellm.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class OpenAiCompatibleLLM : public AbstractLLM
void setModelPath(const QString &path);
void setApiKey(const QString &apiKey);

QString modelName() const override;
QString modelPath() const override;
bool checkValid(QString *errStr) override;
QJsonObject create(const Conversation &conversation) override;
Expand All @@ -31,6 +32,7 @@ class OpenAiCompatibleLLM : public AbstractLLM
void processResponse(QNetworkReply *reply) override;
void cancel() override;
void setMaxTokens(int maxTokens) override;
bool isIdle() override;

signals:
void requstCancel();
Expand Down
Loading