mirror of
https://github.com/nomic-ai/gpt4all.git
synced 2025-06-24 22:42:27 +00:00
implement /api/show (not tested)
This commit is contained in:
parent
85eaa41e6d
commit
7ce2ea57e0
@ -14,5 +14,5 @@ target_sources(gpt4all-backend PUBLIC
|
||||
FILE_SET public_headers TYPE HEADERS BASE_DIRS include FILES
|
||||
include/gpt4all-backend/formatters.h
|
||||
include/gpt4all-backend/ollama-client.h
|
||||
include/gpt4all-backend/ollama-responses.h
|
||||
include/gpt4all-backend/ollama-types.h
|
||||
)
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "ollama-responses.h"
|
||||
#include "ollama-types.h"
|
||||
|
||||
#include <QCoro/QCoroTask> // IWYU pragma: keep
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
class QNetworkRequest;
|
||||
namespace boost::json { class value; }
|
||||
|
||||
|
||||
@ -50,8 +51,9 @@ using DataOrRespErr = std::expected<T, ResponseError>;
|
||||
|
||||
class OllamaClient {
|
||||
public:
|
||||
OllamaClient(QUrl baseUrl)
|
||||
OllamaClient(QUrl baseUrl, QString m_userAgent = QStringLiteral("GPT4All"))
|
||||
: m_baseUrl(baseUrl)
|
||||
, m_userAgent(std::move(m_userAgent))
|
||||
{}
|
||||
|
||||
const QUrl &baseUrl() const { return m_baseUrl; }
|
||||
@ -59,24 +61,35 @@ public:
|
||||
|
||||
/// Returns the version of the Ollama server.
|
||||
auto getVersion() -> QCoro::Task<DataOrRespErr<ollama::VersionResponse>>
|
||||
{ return getSimple<ollama::VersionResponse>(QStringLiteral("version")); }
|
||||
{ return get<ollama::VersionResponse>(QStringLiteral("version")); }
|
||||
|
||||
/// List models that are available locally.
|
||||
auto listModels() -> QCoro::Task<DataOrRespErr<ollama::ModelsResponse>>
|
||||
{ return getSimple<ollama::ModelsResponse>(QStringLiteral("tags")); }
|
||||
{ return get<ollama::ModelsResponse>(QStringLiteral("tags")); }
|
||||
|
||||
/// Show details about a model including modelfile, template, parameters, license, and system prompt.
|
||||
auto showModelInfo(const ollama::ModelInfoRequest &req) -> QCoro::Task<DataOrRespErr<ollama::ModelInfo>>
|
||||
{ return post<ollama::ModelInfo>(QStringLiteral("show"), req); }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
auto getSimple(const QString &endpoint) -> QCoro::Task<DataOrRespErr<T>>;
|
||||
QNetworkRequest makeRequest(const QString &path) const;
|
||||
|
||||
auto getSimpleGeneric(const QString &endpoint) -> QCoro::Task<DataOrRespErr<boost::json::value>>;
|
||||
template <typename Resp>
|
||||
auto get(const QString &path) -> QCoro::Task<DataOrRespErr<Resp>>;
|
||||
template <typename Resp, typename Req>
|
||||
auto post(const QString &path, Req const &req) -> QCoro::Task<DataOrRespErr<Resp>>;
|
||||
|
||||
auto getJson(const QString &path) -> QCoro::Task<DataOrRespErr<boost::json::value>>;
|
||||
auto postJson(const QString &path, const boost::json::value &req)
|
||||
-> QCoro::Task<DataOrRespErr<boost::json::value>>;
|
||||
|
||||
private:
|
||||
QUrl m_baseUrl;
|
||||
QString m_userAgent;
|
||||
QNetworkAccessManager m_nam;
|
||||
};
|
||||
|
||||
extern template auto OllamaClient::getSimple(const QString &) -> QCoro::Task<DataOrRespErr<ollama::VersionResponse>>;
|
||||
extern template auto OllamaClient::getSimple(const QString &) -> QCoro::Task<DataOrRespErr<ollama::ModelsResponse>>;
|
||||
extern template auto OllamaClient::get(const QString &) -> QCoro::Task<DataOrRespErr<ollama::VersionResponse>>;
|
||||
extern template auto OllamaClient::get(const QString &) -> QCoro::Task<DataOrRespErr<ollama::ModelsResponse>>;
|
||||
|
||||
} // namespace gpt4all::backend
|
||||
|
@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/describe/class.hpp>
|
||||
|
||||
#include <QString>
|
||||
#include <QtTypes>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace gpt4all::backend::ollama {
|
||||
|
||||
/// Details about a model.
|
||||
struct ModelDetails {
|
||||
QString parent_model; /// The parent of the model.
|
||||
QString format; /// The format of the model.
|
||||
QString family; /// The family of the model.
|
||||
std::vector<QString> families; /// The families of the model.
|
||||
QString parameter_size; /// The size of the model's parameters.
|
||||
QString quantization_level; /// The quantization level of the model.
|
||||
};
|
||||
BOOST_DESCRIBE_STRUCT(ModelDetails, (), (parent_model, format, family, families, parameter_size, quantization_level))
|
||||
|
||||
/// A model available locally.
|
||||
struct Model {
|
||||
QString model; /// The model name.
|
||||
QString modified_at; /// Model modification date.
|
||||
quint64 size; /// Size of the model on disk.
|
||||
QString digest; /// The model's digest.
|
||||
ModelDetails details; /// The model's details.
|
||||
};
|
||||
BOOST_DESCRIBE_STRUCT(Model, (), (model, modified_at, size, digest, details))
|
||||
|
||||
|
||||
/// The response class for the version endpoint.
|
||||
struct VersionResponse {
|
||||
QString version; /// The version of the Ollama server.
|
||||
};
|
||||
BOOST_DESCRIBE_STRUCT(VersionResponse, (), (version))
|
||||
|
||||
/// Response class for the list models endpoint.
|
||||
struct ModelsResponse {
|
||||
std::vector<Model> models; /// List of models available locally.
|
||||
};
|
||||
BOOST_DESCRIBE_STRUCT(ModelsResponse, (), (models))
|
||||
|
||||
} // namespace gpt4all::backend::ollama
|
120
gpt4all-backend/include/gpt4all-backend/ollama-types.h
Normal file
120
gpt4all-backend/include/gpt4all-backend/ollama-types.h
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
# include <boost/describe/class.hpp>
|
||||
# include <boost/describe/enum.hpp>
|
||||
#endif
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <QString>
|
||||
#include <QtTypes>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace gpt4all::backend::ollama {
|
||||
|
||||
/// Details about a model.
|
||||
struct ModelDetails {
|
||||
QString parent_model; /// The parent of the model.
|
||||
QString format; /// The format of the model.
|
||||
QString family; /// The family of the model.
|
||||
std::vector<QString> families; /// The families of the model.
|
||||
QString parameter_size; /// The size of the model's parameters.
|
||||
QString quantization_level; /// The quantization level of the model.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(ModelDetails, (), (parent_model, format, family, families, parameter_size, quantization_level))
|
||||
#endif
|
||||
|
||||
/// A model available locally.
|
||||
struct Model {
|
||||
QString model; /// The model name.
|
||||
QString modified_at; /// Model modification date.
|
||||
quint64 size; /// Size of the model on disk.
|
||||
QString digest; /// The model's digest.
|
||||
ModelDetails details; /// The model's details.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(Model, (), (model, modified_at, size, digest, details))
|
||||
#endif
|
||||
|
||||
|
||||
/// Request class for the show model info endpoint.
|
||||
struct ModelInfoRequest {
|
||||
QString model; /// The model name.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(ModelInfoRequest, (), (model))
|
||||
#endif
|
||||
|
||||
enum MessageRole {
|
||||
system,
|
||||
user,
|
||||
assistant,
|
||||
tool,
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_ENUM(MessageRole, system, user, assistant, tool)
|
||||
#endif
|
||||
|
||||
struct ToolCallFunction {
|
||||
QString name; /// The name of the function to be called.
|
||||
boost::json::object arguments; /// The arguments to pass to the function.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(ToolCallFunction, (), (name, arguments))
|
||||
#endif
|
||||
|
||||
struct ToolCall {
|
||||
ToolCallFunction function; /// The function the model wants to call.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(ToolCall, (), (function))
|
||||
#endif
|
||||
|
||||
/// A message in the chat endpoint
|
||||
struct Message {
|
||||
MessageRole role; /// The role of the message
|
||||
QString content; /// The content of the message
|
||||
std::vector<QString> images; /// (optional) a list of Base64-encoded images to include in the message
|
||||
std::vector<ToolCall> tool_calls; /// A list of tool calls the model wants to call.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(Message, (), (role, content, images, tool_calls))
|
||||
#endif
|
||||
|
||||
|
||||
/// The response class for the version endpoint.
|
||||
struct VersionResponse {
|
||||
QString version; /// The version of the Ollama server.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(VersionResponse, (), (version))
|
||||
#endif
|
||||
|
||||
/// Response class for the list models endpoint.
|
||||
struct ModelsResponse {
|
||||
std::vector<Model> models; /// List of models available locally.
|
||||
};
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
BOOST_DESCRIBE_STRUCT(ModelsResponse, (), (models))
|
||||
#endif
|
||||
|
||||
/// Details about a model including modelfile, template, parameters, license, and system prompt.
|
||||
struct ModelInfo {
|
||||
std::optional<QString> license; /// The model's license.
|
||||
std::optional<QString> modelfile; /// The modelfile associated with the model.
|
||||
std::optional<QString> parameters; /// The model parameters.
|
||||
std::optional<QString> template_; /// The prompt template for the model.
|
||||
std::optional<QString> system; /// The system prompt for the model.
|
||||
ModelDetails details;
|
||||
boost::json::object model_info;
|
||||
std::optional<std::vector<Message>> messages; /// The default messages for the model.
|
||||
};
|
||||
|
||||
#ifdef G4A_BACKEND_IMPL
|
||||
ModelInfo tag_invoke(const boost::json::value_to_tag<ModelInfo> &, const boost::json::value &value);
|
||||
#endif
|
||||
|
||||
} // namespace gpt4all::backend::ollama
|
@ -3,8 +3,11 @@ set(TARGET gpt4all-backend)
|
||||
add_library(${TARGET} STATIC
|
||||
json-helpers.cpp
|
||||
ollama-client.cpp
|
||||
ollama-types.cpp
|
||||
qt-json-stream.cpp
|
||||
)
|
||||
target_compile_features(${TARGET} PUBLIC cxx_std_23)
|
||||
target_compile_definitions(${TARGET} PRIVATE G4A_BACKEND_IMPL)
|
||||
gpt4all_add_warning_options(${TARGET})
|
||||
target_include_directories(${TARGET} PRIVATE
|
||||
.
|
||||
@ -23,5 +26,5 @@ target_link_libraries(${TARGET} PRIVATE
|
||||
|
||||
# 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)
|
||||
target_include_directories(${TARGET} SYSTEM PUBLIC ${LIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET} PUBLIC Boost::json)
|
||||
|
@ -4,6 +4,14 @@
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace json = boost::json;
|
||||
|
||||
|
||||
void tag_invoke(const boost::json::value_from_tag &, boost::json::value &value, const QString &qstr)
|
||||
{
|
||||
auto utf8 = qstr.toUtf8();
|
||||
value = json::value_from(json::string_view(utf8.data(), utf8.size()));
|
||||
}
|
||||
|
||||
QString tag_invoke(const boost::json::value_to_tag<QString> &, const boost::json::value &value)
|
||||
{
|
||||
|
@ -3,9 +3,13 @@
|
||||
class QString;
|
||||
namespace boost::json {
|
||||
class value;
|
||||
struct value_from_tag;
|
||||
template <typename T> struct value_to_tag;
|
||||
}
|
||||
|
||||
|
||||
/// Allows QString to be serialized to JSON.
|
||||
void tag_invoke(const boost::json::value_from_tag &, boost::json::value &value, const QString &qstr);
|
||||
|
||||
/// Allows JSON strings to be deserialized as QString.
|
||||
QString tag_invoke(const boost::json::value_to_tag<QString> &, const boost::json::value &value);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "ollama-client.h"
|
||||
|
||||
#include "json-helpers.h"
|
||||
#include "qt-json-stream.h"
|
||||
|
||||
#include <QCoro/QCoroIODevice> // IWYU pragma: keep
|
||||
#include <QCoro/QCoroNetworkReply> // IWYU pragma: keep
|
||||
@ -21,35 +22,21 @@ namespace json = boost::json;
|
||||
|
||||
namespace gpt4all::backend {
|
||||
|
||||
template <typename T>
|
||||
auto OllamaClient::getSimple(const QString &endpoint) -> QCoro::Task<DataOrRespErr<T>>
|
||||
{
|
||||
auto value = co_await getSimpleGeneric(endpoint);
|
||||
if (value)
|
||||
co_return boost::json::value_to<T>(*value);
|
||||
co_return std::unexpected(value.error());
|
||||
}
|
||||
|
||||
template auto OllamaClient::getSimple(const QString &) -> QCoro::Task<DataOrRespErr<VersionResponse>>;
|
||||
template auto OllamaClient::getSimple(const QString &) -> QCoro::Task<DataOrRespErr<ModelsResponse>>;
|
||||
|
||||
auto OllamaClient::getSimpleGeneric(const QString &endpoint) -> QCoro::Task<DataOrRespErr<json::value>>
|
||||
static auto processResponse(QNetworkReply &reply) -> QCoro::Task<DataOrRespErr<json::value>>
|
||||
{
|
||||
std::unique_ptr<QNetworkReply> reply(m_nam.get(
|
||||
QNetworkRequest(m_baseUrl.resolved(QUrl(endpoint)))
|
||||
));
|
||||
if (reply->error())
|
||||
co_return std::unexpected(reply.get());
|
||||
if (reply.error())
|
||||
co_return std::unexpected(&reply);
|
||||
|
||||
try {
|
||||
json::parser p;
|
||||
auto coroReply = qCoro(*reply);
|
||||
auto coroReply = qCoro(reply);
|
||||
do {
|
||||
auto chunk = co_await coroReply.readAll();
|
||||
if (reply->error())
|
||||
co_return std::unexpected(reply.get());
|
||||
if (reply.error())
|
||||
co_return std::unexpected(&reply);
|
||||
p.write(chunk.data(), chunk.size());
|
||||
} while (!reply->atEnd());
|
||||
} while (!reply.atEnd());
|
||||
|
||||
co_return p.release();
|
||||
} catch (const std::exception &e) {
|
||||
@ -57,4 +44,50 @@ auto OllamaClient::getSimpleGeneric(const QString &endpoint) -> QCoro::Task<Data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QNetworkRequest OllamaClient::makeRequest(const QString &path) const
|
||||
{
|
||||
QNetworkRequest req(m_baseUrl.resolved(QUrl(path)));
|
||||
req.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
|
||||
return req;
|
||||
}
|
||||
|
||||
template <typename Resp>
|
||||
auto OllamaClient::get(const QString &path) -> QCoro::Task<DataOrRespErr<Resp>>
|
||||
{
|
||||
auto value = co_await getJson(path);
|
||||
if (value)
|
||||
co_return json::value_to<Resp>(*value);
|
||||
co_return std::unexpected(value.error());
|
||||
}
|
||||
|
||||
template auto OllamaClient::get(const QString &) -> QCoro::Task<DataOrRespErr<VersionResponse>>;
|
||||
template auto OllamaClient::get(const QString &) -> QCoro::Task<DataOrRespErr<ModelsResponse>>;
|
||||
|
||||
template <typename Resp, typename Req>
|
||||
auto OllamaClient::post(const QString &path, const Req &req) -> QCoro::Task<DataOrRespErr<Resp>>
|
||||
{
|
||||
auto reqJson = json::value_from(req);
|
||||
auto value = co_await postJson(path, reqJson);
|
||||
if (value)
|
||||
co_return json::value_to<Resp>(*value);
|
||||
co_return std::unexpected(value.error());
|
||||
}
|
||||
|
||||
auto OllamaClient::getJson(const QString &path) -> QCoro::Task<DataOrRespErr<json::value>>
|
||||
{
|
||||
std::unique_ptr<QNetworkReply> reply(m_nam.get(makeRequest(path)));
|
||||
co_return co_await processResponse(*reply);
|
||||
}
|
||||
|
||||
auto OllamaClient::postJson(const QString &path, const json::value &req) -> QCoro::Task<DataOrRespErr<json::value>>
|
||||
{
|
||||
JsonStreamDevice reqStream(&req);
|
||||
std::unique_ptr<QNetworkReply> reply(
|
||||
m_nam.post(makeRequest(path), &reqStream)
|
||||
);
|
||||
co_return co_await processResponse(*reply);
|
||||
}
|
||||
|
||||
|
||||
} // namespace gpt4all::backend
|
||||
|
31
gpt4all-backend/src/ollama-types.cpp
Normal file
31
gpt4all-backend/src/ollama-types.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "ollama-types.h"
|
||||
|
||||
#include "json-helpers.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace json = boost::json;
|
||||
|
||||
|
||||
namespace gpt4all::backend::ollama {
|
||||
|
||||
ModelInfo tag_invoke(const boost::json::value_to_tag<ModelInfo> &, const boost::json::value &value)
|
||||
{
|
||||
using namespace json;
|
||||
auto &o = value.as_object();
|
||||
return {
|
||||
#define T(name) std::remove_reference_t<decltype(std::declval<ModelInfo>().name)>
|
||||
.license = value_to<T(license )>(o.at("license" )),
|
||||
.modelfile = value_to<T(modelfile )>(o.at("modelfile" )),
|
||||
.parameters = value_to<T(parameters)>(o.at("parameters")),
|
||||
.template_ = value_to<T(template_ )>(o.at("template" )), // :(
|
||||
.system = value_to<T(system )>(o.at("system" )),
|
||||
.details = value_to<T(details )>(o.at("details" )),
|
||||
.model_info = value_to<T(model_info)>(o.at("model_info")),
|
||||
.messages = value_to<T(messages )>(o.at("messages" )),
|
||||
#undef T
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace gpt4all::backend::ollama
|
30
gpt4all-backend/src/qt-json-stream.cpp
Normal file
30
gpt4all-backend/src/qt-json-stream.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "qt-json-stream.h"
|
||||
|
||||
|
||||
namespace json = boost::json;
|
||||
|
||||
|
||||
namespace gpt4all::backend {
|
||||
|
||||
JsonStreamDevice::JsonStreamDevice(const json::value *jv, QObject *parent)
|
||||
: QIODevice(parent)
|
||||
{
|
||||
m_sr.reset(jv);
|
||||
open(QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
qint64 JsonStreamDevice::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
if (m_sr.done()) return 0;
|
||||
auto chunk = m_sr.read(data, size_t(maxSize));
|
||||
return qint64(chunk.size());
|
||||
}
|
||||
|
||||
qint64 JsonStreamDevice::writeData(const char *data, qint64 maxSize)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(maxSize)
|
||||
return -1;
|
||||
}
|
||||
|
||||
} // namespace gpt4all::backend
|
27
gpt4all-backend/src/qt-json-stream.h
Normal file
27
gpt4all-backend/src/qt-json-stream.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <QIODevice>
|
||||
#include <QObject>
|
||||
#include <QtTypes>
|
||||
|
||||
|
||||
namespace gpt4all::backend {
|
||||
|
||||
class JsonStreamDevice : public QIODevice
|
||||
{
|
||||
public:
|
||||
explicit JsonStreamDevice(const boost::json::value *jv, QObject *parent = nullptr);
|
||||
|
||||
bool isSequential() const override { return true; }
|
||||
|
||||
protected:
|
||||
qint64 readData(char *data, qint64 maxSize) override;
|
||||
qint64 writeData(const char *data, qint64 maxSize) override;
|
||||
|
||||
private:
|
||||
boost::json::serializer m_sr;
|
||||
};
|
||||
|
||||
} // namespace gpt4all::backend
|
Loading…
Reference in New Issue
Block a user