Skip to content

Commit

Permalink
feat: [terminal] Add Smart Terminal Functionality
Browse files Browse the repository at this point in the history
1.add service of Ai to support other plugins use it
2.add intelligent command generator with terminal

Log: as title
  • Loading branch information
LiHua000 authored and deepin-mozart committed Nov 12, 2024
1 parent 97897e5 commit 50c84ab
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/plugins/codegeex/codegeex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "common/common.h"
#include "services/window/windowservice.h"
#include "services/option/optionservice.h"
#include "services/ai/aiservice.h"
#include "copilot.h"

#include "base/abstractwidget.h"
Expand All @@ -24,6 +25,12 @@ using namespace dpfservice;

void CodeGeex::initialize()
{
// load Ai service.
QString errStr;
auto &ctx = dpfInstance.serviceContext();
if (!ctx.load(AiService::name(), &errStr)) {
qCritical() << errStr;
}
}

bool CodeGeex::start()
Expand Down Expand Up @@ -61,6 +68,11 @@ bool CodeGeex::start()
});
});

using namespace std::placeholders;
auto aiService = dpfGetService(dpfservice::AiService);
aiService->available = std::bind(&CodeGeeXManager::isLoggedIn, CodeGeeXManager::instance());
aiService->askQuestion = std::bind(&CodeGeeXManager::independentAsking, CodeGeeXManager::instance(), _1, _2);

return true;
}

Expand Down
30 changes: 30 additions & 0 deletions src/plugins/codegeex/codegeexmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,36 @@ void CodeGeeXManager::setReferenceFiles(const QStringList &files)
askApi.setReferenceFiles(files);
}

void CodeGeeXManager::independentAsking(const QString &prompt, QIODevice *pipe)
{
if (!isLoggedIn()) {
emit notify(1, tr("CodeGeeX is not avaliable, please logging in"));
pipe->close();
return;
}
AskApi *api = new AskApi;
api->postSSEChat(kUrlSSEChat, sessionId, prompt, QSysInfo::machineUniqueId(), {}, currentTalkID);
QTimer::singleShot(10000, api, [=](){
if (pipe && pipe->isOpen()) {
qWarning() << "timed out, close pipe";
pipe->close();
api->deleteLater();
emit notify(1, tr("Request timed out, please check the network or if the model is available."));
}
});

connect(api, &AskApi::response, api, [=](const QString &msgID, const QString &data, const QString &event) {
QString msgData = modifiedData(data);
if (event == "finish") {
api->deleteLater();
pipe->close();
return;
} else if (event == "add") {
pipe->write(msgData.toUtf8());
}
});
}

void CodeGeeXManager::createNewSession()
{
QString currentMSecsStr = QString::number(QDateTime::currentMSecsSinceEpoch());
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/codegeex/codegeexmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <QMap>
#include <QTimer>
#include <QMutex>
#include <QIODevice>

struct RecordData
{
Expand Down Expand Up @@ -89,6 +90,7 @@ class CodeGeeXManager : public QObject
bool isReferenceCodebase() const;
void setReferenceFiles(const QStringList &files);

void independentAsking(const QString &prompt, QIODevice *pipe);
// Rag
QString condaRootPath() const;
void showIndexingWidget();
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/console/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ set(CMAKE_INCLUDE_CURRENT_DIR true)
set(CXX_CPP
consolewidget.cpp
consolemanager.cpp
generateinput.cpp
console.cpp
console.json
)

set(CXX_H
consolewidget.h
consolemanager.h
generateinput.h
console.h
)

Expand Down
13 changes: 13 additions & 0 deletions src/plugins/console/consolewidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later

#include "consolewidget.h"
#include "generateinput.h"
#include "services/project/projectservice.h"

#include <DMenu>
Expand All @@ -20,6 +21,8 @@ class ConsoleWidgetPrivate
QAction *consoleCopy = nullptr;
QAction *consolePaste = nullptr;
QAction *enterCurrentPath = nullptr;
QAction *showInputWidget = nullptr;
GenerateInput *inputWidget = nullptr;

ProjectService *prjService = nullptr;
};
Expand Down Expand Up @@ -52,14 +55,23 @@ ConsoleWidget::ConsoleWidget(QWidget *parent, bool startNow)
changeDir(d->prjService->getActiveProjectInfo().workspaceFolder());
sendText("clear\n");

d->inputWidget = new GenerateInput(this);
d->inputWidget->hide();
auto layout = this->layout();
if (layout)
layout->addWidget(d->inputWidget);

d->consoleCopy = new QAction(tr("copy"), this);
d->consolePaste = new QAction(tr("paste"), this);
d->enterCurrentPath = new QAction(tr("Enter current project root path"), this);
d->showInputWidget = new QAction(tr("Intelligent Command Generation"), this);
QObject::connect(d->consoleCopy, &QAction::triggered, this, &QTermWidget::copyClipboard);
QObject::connect(d->consolePaste, &QAction::triggered, this, &QTermWidget::pasteClipboard);
QObject::connect(d->enterCurrentPath, &QAction::triggered, this, &ConsoleWidget::enterCurrentProjectPath);
QObject::connect(d->showInputWidget, &QAction::triggered, d->inputWidget, &GenerateInput::show);
QObject::connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged,
this, &ConsoleWidget::updateColorScheme);
QObject::connect(d->inputWidget, &GenerateInput::commandGenerated, this, &ConsoleWidget::sendText);
}

ConsoleWidget::~ConsoleWidget()
Expand All @@ -74,6 +86,7 @@ void ConsoleWidget::contextMenuEvent(QContextMenuEvent *event)
d->menu->setParent(this);
d->menu->addAction(d->consoleCopy);
d->menu->addAction(d->consolePaste);
d->menu->addAction(d->showInputWidget);
d->menu->addAction(d->enterCurrentPath);
}
if (selectedText().isEmpty()) {
Expand Down
132 changes: 132 additions & 0 deletions src/plugins/console/generateinput.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "generateinput.h"
#include "services/ai/aiservice.h"

#include <DLineEdit>
#include <DSuggestButton>
#include <DSpinner>
#include <DIconButton>

#include <QHBoxLayout>

DWIDGET_USE_NAMESPACE

class GenerateInputPrivate
{
public:
QBuffer *pipe { nullptr };
DLineEdit *edit { nullptr };
DSuggestButton *confirmBtn { nullptr };
DIconButton *closeBtn { nullptr };
DSpinner *spinner { nullptr };
};

GenerateInput::GenerateInput(QWidget *parent)
: QWidget(parent), d(new GenerateInputPrivate)
{
initUi();
initPipe();
initConnect();
}

GenerateInput::~GenerateInput()
{
if (d)
delete d;
}

void GenerateInput::initUi()
{
setFixedHeight(50);
auto layout = new QHBoxLayout(this);
d->edit = new DLineEdit(this);
d->edit->setPlaceholderText(tr("Describe the task you want to execute"));
d->confirmBtn = new DSuggestButton(this);
d->confirmBtn->setText(tr("Generate"));
d->confirmBtn->setEnabled(false);
d->closeBtn = new DIconButton(this);
d->closeBtn->setIcon(QIcon::fromTheme("common_close"));
d->closeBtn->setIconSize({ 16, 16 });
d->closeBtn->setFlat(true);
d->spinner = new DSpinner(this);
d->spinner->setFixedSize(16, 16);
d->spinner->hide();

layout->addWidget(d->edit);
layout->addWidget(d->confirmBtn);
layout->addWidget(d->closeBtn);
}

void GenerateInput::initPipe()
{
d->pipe = new QBuffer(this);
connect(d->pipe, &QBuffer::aboutToClose, this , [=](){
d->pipe->seek(0);
auto response = QString(d->pipe->readAll());
QString results;
QRegularExpression regex(R"(```.*\n((.*\n)*?.*)\n\s*```)");

QRegularExpressionMatchIterator matchIterator = regex.globalMatch(response);
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
QString codePart = match.captured(1).trimmed();
if (!codePart.isEmpty()) {
if (!results.isEmpty())
results.append(';');
results.append(codePart);
}
}

switchState(false);
emit commandGenerated(results);
});
}

void GenerateInput::initConnect()
{
connect(d->edit, &DLineEdit::returnPressed, this, &GenerateInput::onGenerate);
connect(d->edit, &DLineEdit::textChanged, this, [=](){
d->confirmBtn->setEnabled(!d->edit->text().isEmpty());
});
connect(d->confirmBtn, &DSuggestButton::clicked, this, &GenerateInput::onGenerate);
connect(d->closeBtn, &DIconButton::clicked, this, [=](){
this->hide();
});
}

void GenerateInput::onGenerate()
{
auto text = d->edit->text();
if (text.isEmpty()) {
switchState(false);
return;
}

switchState(true);
d->pipe->open(QIODevice::ReadWrite);
QString prompt = "你是一个智能终端机器人,你的工作环境是deepin-os/UOS/Linux,你的任务是根据描述生成可以直接使用的终端命令,不要进行额外的回答。描述是:" + text;
using namespace dpfservice;
auto aiSrv = dpfGetService(AiService);
aiSrv->askQuestion(prompt, d->pipe);
}

void GenerateInput::switchState(bool generating)
{
if (generating) {
d->spinner->start();
d->spinner->show();
d->pipe->setData(""); // reset pipe
d->spinner->move(d->edit->rect().bottomRight() - QPoint(38, 16));
d->edit->setEnabled(false);
d->confirmBtn->setEnabled(false);
} else {
d->spinner->stop();
d->spinner->hide();
d->edit->clear();
d->edit->setEnabled(true);
d->confirmBtn->setEnabled(true);
}
}
32 changes: 32 additions & 0 deletions src/plugins/console/generateinput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef GENERATEINPUT_H
#define GENERATEINPUT_H

#include <QWidget>

class GenerateInputPrivate;
class GenerateInput : public QWidget
{
Q_OBJECT
public:
explicit GenerateInput(QWidget *parent = nullptr);
~GenerateInput();

signals:
void commandGenerated(const QString &command);

public slots:
void onGenerate();
void switchState(bool generating);

private:
void initUi();
void initPipe();
void initConnect();
GenerateInputPrivate *d;
};

#endif // GENERATEINPUT_H
1 change: 1 addition & 0 deletions src/services/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ FILE(GLOB SERVICES_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/editor/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/terminal/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/locator/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/ai/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/*.h"
)

Expand Down
37 changes: 37 additions & 0 deletions src/services/ai/aiservice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef AISERVICE_H
#define AISERVICE_H

#include "services/services_global.h"
#include <framework/framework.h>

#include <QIODevice>

namespace dpfservice {
// service interface
class SERVICE_EXPORT AiService final : public dpf::PluginService, dpf::AutoServiceRegister<AiService>
{
Q_OBJECT
Q_DISABLE_COPY(AiService)
public:
static QString name()
{
return "org.deepin.service.AiService";
}

explicit AiService(QObject *parent = nullptr)
: dpf::PluginService (parent)
{

}

DPF_INTERFACE(bool, available);
DPF_INTERFACE(void, askQuestion, const QString &prompt, QIODevice *pipe); // pipe recevice data from ai
};

} // namespace dpfservice

#endif // AISERVICE_H

0 comments on commit 50c84ab

Please sign in to comment.