mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-07-04 11:08:01 +00:00
WIP: use Boost::json for incremental parsing and reflection
This commit is contained in:
parent
927e963076
commit
06475dd113
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.29)
|
cmake_minimum_required(VERSION 3.28...3.31)
|
||||||
project(gpt4all-backend-test VERSION 0.1 LANGUAGES CXX)
|
project(gpt4all-backend-test VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
set(G4A_TEST_OLLAMA_URL "http://localhost:11434/" CACHE STRING "The base URL of the Ollama server to use.")
|
set(G4A_TEST_OLLAMA_URL "http://localhost:11434/" CACHE STRING "The base URL of the Ollama server to use.")
|
||||||
|
@ -23,9 +23,9 @@ static void run()
|
|||||||
LLMProvider provider(OLLAMA_URL);
|
LLMProvider provider(OLLAMA_URL);
|
||||||
auto version = QCoro::waitFor(provider.getVersion());
|
auto version = QCoro::waitFor(provider.getVersion());
|
||||||
if (version) {
|
if (version) {
|
||||||
fmt::print("Server version: {}\n", *version);
|
fmt::print("Server version: {}\n", version->version);
|
||||||
} else {
|
} else {
|
||||||
fmt::print("Network error: {}\n", version.error().errorString);
|
fmt::print("Error retrieving version: {}\n", version.error().errorString);
|
||||||
return QCoreApplication::exit(1);
|
return QCoreApplication::exit(1);
|
||||||
}
|
}
|
||||||
QCoreApplication::exit(0);
|
QCoreApplication::exit(0);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.28)
|
cmake_minimum_required(VERSION 3.28...3.31)
|
||||||
project(gpt4all-backend VERSION 0.1 LANGUAGES CXX)
|
project(gpt4all-backend VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23) # make sure fmt is compiled with the same C++ version as us
|
set(CMAKE_CXX_STANDARD 23) # make sure fmt is compiled with the same C++ version as us
|
||||||
@ -7,6 +7,7 @@ include(../common/common.cmake)
|
|||||||
find_package(Qt6 6.8 COMPONENTS Concurrent Core Network REQUIRED)
|
find_package(Qt6 6.8 COMPONENTS Concurrent Core Network REQUIRED)
|
||||||
|
|
||||||
add_subdirectory(../deps common_deps)
|
add_subdirectory(../deps common_deps)
|
||||||
|
add_subdirectory(deps)
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
target_sources(gpt4all-backend PUBLIC
|
target_sources(gpt4all-backend PUBLIC
|
||||||
|
14
gpt4all-backend/deps/CMakeLists.txt
Normal file
14
gpt4all-backend/deps/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
set(BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
|
# suppress warnings during boost build
|
||||||
|
add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:BOOST_ALLOW_DEPRECATED_HEADERS>)
|
||||||
|
|
||||||
|
set(GPT4ALL_BOOST_TAG 1.87.0)
|
||||||
|
FetchContent_Declare(
|
||||||
|
boost
|
||||||
|
URL "https://github.com/boostorg/boost/releases/download/boost-${GPT4ALL_BOOST_TAG}/boost-${GPT4ALL_BOOST_TAG}-cmake.tar.xz"
|
||||||
|
URL_HASH "SHA256=7da75f171837577a52bbf217e17f8ea576c7c246e4594d617bfde7fafd408be5"
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(boost)
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QCoro/QCoroTask> // IWYU pragma: keep
|
#include <QCoro/QCoroTask> // IWYU pragma: keep
|
||||||
|
#include <boost/describe/class.hpp>
|
||||||
|
|
||||||
#include <QJsonParseError>
|
#include <QJsonParseError>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
@ -19,7 +20,7 @@ struct ResponseError {
|
|||||||
private:
|
private:
|
||||||
using ErrorCode = std::variant<
|
using ErrorCode = std::variant<
|
||||||
QNetworkReply::NetworkError,
|
QNetworkReply::NetworkError,
|
||||||
QJsonParseError::ParseError
|
std::exception_ptr
|
||||||
>;
|
>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -33,17 +34,19 @@ public:
|
|||||||
assert(reply->error());
|
assert(reply->error());
|
||||||
}
|
}
|
||||||
|
|
||||||
ResponseError(const QJsonParseError &err)
|
ResponseError(const std::exception &e, std::exception_ptr err)
|
||||||
: error(err.error)
|
: error(std::move(err))
|
||||||
, errorString(err.errorString())
|
, errorString(e.what())
|
||||||
{
|
{
|
||||||
assert(err.error);
|
assert(std::get<std::exception_ptr>(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using DataOrRespErr = std::expected<T, ResponseError>;
|
using DataOrRespErr = std::expected<T, ResponseError>;
|
||||||
|
|
||||||
|
struct VersionResponse { QString version; };
|
||||||
|
BOOST_DESCRIBE_STRUCT(VersionResponse, (), (version))
|
||||||
|
|
||||||
class LLMProvider {
|
class LLMProvider {
|
||||||
public:
|
public:
|
||||||
@ -55,10 +58,11 @@ public:
|
|||||||
void getBaseUrl(QUrl value) { m_baseUrl = std::move(value); }
|
void getBaseUrl(QUrl value) { m_baseUrl = std::move(value); }
|
||||||
|
|
||||||
/// Retrieve the Ollama version, e.g. "0.5.1"
|
/// Retrieve the Ollama version, e.g. "0.5.1"
|
||||||
auto getVersion() const -> QCoro::Task<DataOrRespErr<QString>>;
|
auto getVersion() -> QCoro::Task<DataOrRespErr<VersionResponse>>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUrl m_baseUrl;
|
QUrl m_baseUrl;
|
||||||
|
QNetworkAccessManager m_nam;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gpt4all::backend
|
} // namespace gpt4all::backend
|
||||||
|
@ -1,13 +1,27 @@
|
|||||||
set(TARGET gpt4all-backend)
|
set(TARGET gpt4all-backend)
|
||||||
|
|
||||||
add_library(${TARGET} STATIC
|
add_library(${TARGET} STATIC
|
||||||
|
json_helpers.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
target_compile_features(${TARGET} PUBLIC cxx_std_23)
|
target_compile_features(${TARGET} PUBLIC cxx_std_23)
|
||||||
gpt4all_add_warning_options(${TARGET})
|
gpt4all_add_warning_options(${TARGET})
|
||||||
|
target_include_directories(${TARGET} PRIVATE
|
||||||
|
.
|
||||||
|
../include/gpt4all-backend
|
||||||
|
)
|
||||||
target_link_libraries(${TARGET} PUBLIC
|
target_link_libraries(${TARGET} PUBLIC
|
||||||
QCoro6::Coro Qt6::Core Qt6::Network
|
Boost::describe
|
||||||
|
QCoro6::Coro
|
||||||
|
Qt6::Core
|
||||||
|
Qt6::Network
|
||||||
)
|
)
|
||||||
target_link_libraries(${TARGET} PRIVATE
|
target_link_libraries(${TARGET} PRIVATE
|
||||||
QCoro6::Network fmt::fmt
|
QCoro6::Network
|
||||||
|
fmt::fmt
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# link Boost::json as -isystem to suppress -Wundef
|
||||||
|
get_target_property(LIB_INCLUDE_DIRS Boost::json INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
target_include_directories(${TARGET} SYSTEM PRIVATE ${LIB_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${TARGET} PRIVATE Boost::json)
|
||||||
|
12
gpt4all-backend/src/json_helpers.cpp
Normal file
12
gpt4all-backend/src/json_helpers.cpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "json_helpers.h"
|
||||||
|
|
||||||
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
|
||||||
|
QString tag_invoke(const boost::json::value_to_tag<QString> &, const boost::json::value &value)
|
||||||
|
{
|
||||||
|
auto &s = value.as_string();
|
||||||
|
return QString::fromUtf8(s.data(), s.size());
|
||||||
|
}
|
11
gpt4all-backend/src/json_helpers.h
Normal file
11
gpt4all-backend/src/json_helpers.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
namespace boost::json {
|
||||||
|
class value;
|
||||||
|
template <typename T> struct value_to_tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Allows JSON strings to be deserialized as QString.
|
||||||
|
QString tag_invoke(const boost::json::value_to_tag<QString> &, const boost::json::value &value);
|
@ -1,43 +1,45 @@
|
|||||||
#include <gpt4all-backend/main.h>
|
#include "main.h"
|
||||||
|
|
||||||
|
#include "json_helpers.h"
|
||||||
|
|
||||||
|
#include <QCoro/QCoroIODevice> // IWYU pragma: keep
|
||||||
#include <QCoro/QCoroNetworkReply> // IWYU pragma: keep
|
#include <QCoro/QCoroNetworkReply> // IWYU pragma: keep
|
||||||
|
#include <boost/json.hpp>
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QJsonValue>
|
|
||||||
|
|
||||||
#include <coroutine>
|
#include <coroutine>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace Qt::Literals::StringLiterals;
|
using namespace Qt::Literals::StringLiterals;
|
||||||
|
namespace json = boost::json;
|
||||||
|
|
||||||
|
|
||||||
namespace gpt4all::backend {
|
namespace gpt4all::backend {
|
||||||
|
|
||||||
auto LLMProvider::getVersion() const -> QCoro::Task<DataOrRespErr<QString>>
|
auto LLMProvider::getVersion() -> QCoro::Task<DataOrRespErr<VersionResponse>>
|
||||||
{
|
{
|
||||||
QNetworkAccessManager nam;
|
std::unique_ptr<QNetworkReply> reply(m_nam.get(QNetworkRequest(m_baseUrl.resolved(u"/api/version"_s))));
|
||||||
std::unique_ptr<QNetworkReply> reply(co_await nam.get(QNetworkRequest(m_baseUrl.resolved(u"/api/version"_s))));
|
|
||||||
if (reply->error())
|
if (reply->error())
|
||||||
co_return std::unexpected(reply.get());
|
co_return std::unexpected(reply.get());
|
||||||
|
|
||||||
QJsonParseError error;
|
try {
|
||||||
auto doc = QJsonDocument::fromJson(reply->readAll(), &error);
|
json::parser p;
|
||||||
if (doc.isNull())
|
auto coroReply = qCoro(*reply);
|
||||||
co_return std::unexpected(error);
|
do {
|
||||||
|
auto chunk = co_await coroReply.readAll();
|
||||||
|
if (reply->error())
|
||||||
|
co_return std::unexpected(reply.get());
|
||||||
|
p.write(chunk.data(), chunk.size());
|
||||||
|
} while (!reply->atEnd());
|
||||||
|
|
||||||
assert(doc.isObject());
|
co_return json::value_to<VersionResponse>(p.release());
|
||||||
auto obj = doc.object();
|
} catch (const std::exception &e) {
|
||||||
|
co_return std::unexpected(ResponseError(e, std::current_exception()));
|
||||||
auto version = std::as_const(obj).find("version"_L1);
|
}
|
||||||
assert(version != obj.constEnd());
|
|
||||||
|
|
||||||
assert(version->isString());
|
|
||||||
co_return version->toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace gpt4all::backend
|
} // namespace gpt4all::backend
|
||||||
|
Loading…
Reference in New Issue
Block a user