Skip to content

Commit

Permalink
Wingman: working version polish + async on #1514; ccache @ win
Browse files Browse the repository at this point in the history
  • Loading branch information
dvorka committed Feb 3, 2024
1 parent b1a4ea0 commit 3409c1c
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 184 deletions.
5 changes: 4 additions & 1 deletion app/app.pro
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ INCLUDEPATH += ./src/qt/spelling
#
win32{
QMAKE_CXXFLAGS += /MP
QMAKE_CXX = ccache $$QMAKE_CXX
!mfnoccache {
QMAKE_CXX = ccache $$QMAKE_CXX
}
} else {
# linux and macos
mfnoccache {
Expand Down Expand Up @@ -504,5 +506,6 @@ win32 {
message(DEFINES of app.pro build: $$DEFINES)
message(QMAKE_EXTRA_TARGETS of app.pro build: $$QMAKE_EXTRA_TARGETS)
message(QT of app.pro build: $$QT)
message(PATH is: $$(PATH))

# eof
2 changes: 1 addition & 1 deletion app/src/qt/main_window_presenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2187,7 +2187,7 @@ void MainWindowPresenter::handleActionWingman(bool showDialog)

void MainWindowPresenter::slotRunWingmanFromDialog(bool showDialog)
{
bool runAsynchronously = false;
bool runAsynchronously = true;

// pull prompt from the dialog & prepare prompt from the dialog
string prompt = this->wingmanDialog->getPrompt();
Expand Down
4 changes: 3 additions & 1 deletion lib/lib.pro
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ mfdebug|mfunits {
# compiler options (qmake CONFIG+=mfnoccache ...)
win32{
QMAKE_CXXFLAGS += /MP
QMAKE_CXX = ccache $$QMAKE_CXX
!mfnoccache {
QMAKE_CXX = ccache $$QMAKE_CXX
}
} else {
# linux and macos
mfnoccache {
Expand Down
223 changes: 42 additions & 181 deletions lib/src/mind/ai/llm/openai_wingman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,208 +117,64 @@ void OpenAiWingman::curlGet(CommandWingmanChat& command) {
#if defined(_WIN32) || defined(__APPLE__)
/* Qt Networking examples:
*
* - https://community.openai.com/t/qt-interface-w-chatgpt-api/354900
* - https://forum.qt.io/topic/116601/qnetworkaccessmanager-reply-is-always-empty/7
* - https://community.openai.com/t/qt-interface-w-chatgpt-api/354900
* - https://gist.github.com/FONQRI/d8fb13150c1e6760f1b1617730559418
*/
#ifdef THIS_WORKS_GET
QNetworkAccessManager nam;

QNetworkRequest request(QUrl("https://www.walkman-pictures.com/"));
auto reply = nam.get(request);

QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();

reply->deleteLater();
auto error = reply->error();
if(error != QNetworkReply::NoError) {
qDebug() << "network error:" << error << reply->errorString();
return;
}
QByteArray read = reply->readAll();
if(read.isEmpty()) {
qDebug() << "response is empty";
return;
}

QString fileName(QDir::currentPath() + "/" + "qnetwork-get-test.html");
QFile file(fileName);
//[CHANGED]Remove QIODevice::Text to prevent line terminator mis-translating
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qDebug() << "file open error:" << file.error() << file.errorString();
return;
}
//NOTE: Don't use QTextStream to write QByteArray to file, that's not the right way even if the result is correct!!!
file.write(read);
file.close();
#endif


QNetworkAccessManager networkManager;
string prompt{"Write a simple 'Hello World' program in Python."};
int maxTokens = 300;

QString qApiKey = QString::fromStdString(apiKey);

QUrl apiEndpoint("https://api.openai.com/v1/chat/completions");
//QUrl apiEndpoint("https://api.openai.com/v1/engines/davinci/completions");
QNetworkRequest request(apiEndpoint);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", "Bearer " + qApiKey.toUtf8());

/*
QJsonObject jsonPayload;
jsonPayload.insert("prompt", QJsonValue(QString::fromStdString(prompt)));
jsonPayload.insert("max_tokens", maxTokens);
*/
QNetworkRequest request(QUrl("https://api.openai.com/v1/chat/completions"));
request.setHeader(
QNetworkRequest::ContentTypeHeader,
"application/json");
request.setRawHeader(
"Authorization",
"Bearer " + QString::fromStdString(apiKey).toUtf8());

// Send API request
QNetworkReply* reply = networkManager.post(
request,
requestJSonStr.c_str()
//QJsonDocument(jsonPayload).toJson()
);

QEventLoop loop;
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();

reply->deleteLater();
auto error = reply->error();
if(error != QNetworkReply::NoError) {
qDebug() << "network error:" << error << reply->errorString();
return;
}
QByteArray read = reply->readAll();
if(read.isEmpty()) {
qDebug() << "response is empty";
return;
}

QString qCommandResponse = QString{read};
qDebug() << "Response is: '" << qCommandResponse << "'";
command.httpResponse = qCommandResponse.toStdString();
command.status = m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_OK;

/*
QString fileName(QDir::currentPath() + "/" + "qnetwork-get-test.html");
QFile file(fileName);
//[CHANGED]Remove QIODevice::Text to prevent line terminator mis-translating
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qDebug() << "file open error:" << file.error() << file.errorString();
return;
// response: error handling
auto error = reply->error();
if(error != QNetworkReply::NoError) {
command.errorMessage =
"Error: request to OpenAI Wingman provider failed due a network error - " +
reply->errorString().toStdString();
MF_DEBUG(command.errorMessage << endl);
command.status = m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_ERROR;
}
//NOTE: Don't use QTextStream to write QByteArray to file, that's not the right way even if the result is correct!!!
file.write(read);
file.close();
*/

/*
QObject::connect(
reply, &QNetworkReply::finished,
[reply]()
{
if (reply->error() != QNetworkReply::NoError) {
MF_DEBUG("Error: " << reply->errorString().toStdString() << endl);
} else {
QJsonObject jsonResponse = QJsonDocument::fromJson(reply->readAll()).object();
QString code = jsonResponse.value("choices").toArray().first().toObject().value("text").toString().trimmed();
MF_DEBUG("Received code:" << endl);
MF_DEBUG(code.toStdString());
QByteArray read;
if(command.status == m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_OK) {
read = reply->readAll();

if(read.isEmpty()) {
command.errorMessage =
"Error: Request to OpenAI Wingman provider failed - response is empty'";
MF_DEBUG(command.errorMessage << endl);
command.status = m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_ERROR;
}
reply->deleteLater();
});
for(int i=0; i<120; i++) {
MF_DEBUG("Step " << i << endl);
if(reply->isRunning()) {
MF_DEBUG(" IS RUNNING " << i << endl);
} else {
if(reply->error() == QNetworkReply::NoError) {
MF_DEBUG(" NO error! " << i << endl);
} else {
MF_DEBUG(" An ERROR! " << reply->error() << endl);
}
}
QThread::msleep(1000);
}
*/


#ifdef DISABLED_DEBUG_BLAH
// request
QNetworkRequest request{};

// request: headers
request.setUrl(
QUrl("https://api.openai.com/v1/chat/completions"));
request.setHeader(
QNetworkRequest::ContentTypeHeader,
"application/json");
// TODO removed string apiKeyUtf8{stringToUtf8(apiKey)};
request.setRawHeader(
"Authorization",
("Bearer " + apiKey).c_str());

// request body
MF_DEBUG("Building OpenAI request body..." << endl);
// TODO remove string requestJSonStrUtf8{stringToUtf8(requestJSonStr)};
QByteArray requestBody(requestJSonStr.c_str());
MF_DEBUG(" OpenAI request body bytearray DONE" << endl);

// create a network access manager
QNetworkAccessManager manager{};

MF_DEBUG("POSTing OpenAI request..." << endl);
QNetworkReply* reply = manager.post(request, requestBody);
MF_DEBUG(" OpenAI reply handle: " << reply << endl);

// connect to the finished signal to handle the response
/*
QObject::connect(
reply, &QNetworkReply::finished,
[=]()
{
if (reply->error() == QNetworkReply::NoError) {
auto commandhttpResponse = QString::fromUtf8(reply->readAll()).toStdString();
MF_DEBUG("Request to OpenAI successful: '" << commandhttpResponse << endl);
//command.status = m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_OK;
//command.httpResponse = QString::fromUtf8(reply->readAll()).toStdString();
MF_DEBUG(" Response from OpenAI: '" << commandhttpResponse << endl);
} else {
auto commanderrorMessage = reply->errorString().toStdString();
MF_DEBUG("Request to OpenAI FAILED: '" << commanderrorMessage << endl);
//command.status = m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_ERROR;
//command.errorMessage = reply->errorString().toStdString();
//command.httpResponse.clear();
//MF_DEBUG(" ERROR response from OpenAI: '" << command.httpResponse << endl);
}
reply->deleteLater();
});
*/

for(int i=0; i<30; i++) {
MF_DEBUG("Step " << i << endl);
if(reply->isRunning()) {
MF_DEBUG(" IS RUNNING " << i << endl);
} else {
if(reply->error() == QNetworkReply::NoError) {
MF_DEBUG(" NO error! " << i << endl);
} else {
MF_DEBUG(" An ERROR! " << reply->error() << endl);
}
}
QThread::msleep(1000);
// response: successful response processing
if(command.status == m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_OK) {
QString qCommandResponse = QString{read};
command.httpResponse = qCommandResponse.toStdString();
command.errorMessage.clear();
command.status = m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_OK;
MF_DEBUG(
"Successful OpenAI Wingman provider response:" << endl <<
" '" << command.httpResponse << "'" << endl);
}

// delete the network reply when it's finished
//QObject::connect(
// reply, &QNetworkReply::finished,
// reply, &QNetworkReply::deleteLater);
#endif // disabled blah

#else
#else
// set up cURL options
command.httpResponse.clear();
curl_easy_setopt(
Expand Down Expand Up @@ -356,7 +212,6 @@ void OpenAiWingman::curlGet(CommandWingmanChat& command) {

// finish error handling (shared by QNetwork/CURL)
if(command.status == WingmanStatusCode::WINGMAN_STATUS_CODE_ERROR) {
// if(true) {
std::cerr <<
"Error: Wingman OpenAI cURL/QtNetwork request failed (error message/HTTP response):" << endl <<
" '" << command.errorMessage << "'" << endl <<
Expand Down Expand Up @@ -466,11 +321,17 @@ void OpenAiWingman::curlGet(CommandWingmanChat& command) {
command.errorMessage.assign(
"OpenAI API HTTP required failed with finish_reason: "
+ statusStr);
command.answerHtml.clear();
command.answerTokens = 0;
command.answerLlmModel = llmModel;
}
MF_DEBUG(" status: " << command.status << endl);
}
} else {
command.status = m8r::WingmanStatusCode::WINGMAN_STATUS_CODE_ERROR;
command.answerHtml.clear();
command.answerTokens = 0;
command.answerLlmModel = llmModel;
if(
httpResponseJSon.contains("error")
&& httpResponseJSon["error"].contains("message")
Expand Down

0 comments on commit 3409c1c

Please sign in to comment.