WIP: provider types are recongized properly in qml

This commit is contained in:
Jared Van Bortel 2025-03-19 19:32:59 -04:00
parent b359c9245c
commit f71f8577ae
11 changed files with 90 additions and 55 deletions

View File

@ -13,6 +13,7 @@ import download
import modellist
import network
import gpt4all
import gpt4all.store_provider
import mysettings
import localdocs
@ -55,28 +56,38 @@ ColumnLayout {
height: parent.childHeight
provider: modelData
providerName: provider.name
providerImage: provider.icon
providerDesc: ({
'{20f963dc-1f99-441e-ad80-f30a0a06bcac}': qsTr(
'Groq offers a high-performance AI inference engine designed for low-latency and ' +
'efficient processing. Optimized for real-time applications, Groqs technology is ideal ' +
'for users who need fast responses from open large language models and other AI ' +
'workloads.<br><br>Get your API key: ' +
'<a href="https://console.groq.com/keys">https://groq.com/</a>'
),
'{6f874c3a-f1ad-47f7-9129-755c5477146c}': qsTr(
'OpenAI provides access to advanced AI models, including GPT-4 supporting a wide range ' +
'of applications, from conversational AI to content generation and code completion.' +
'<br><br>Get your API key: ' +
'<a href="https://platform.openai.com/signup">https://openai.com/</a>'
),
'{7ae617b3-c0b2-4d2c-9ff2-bc3f049494cc}': qsTr(
'Mistral AI specializes in efficient, open-weight language models optimized for various ' +
'natural language processing tasks. Their models are designed for flexibility and ' +
'performance, making them a solid option for applications requiring scalable AI ' +
'solutions.<br><br>Get your API key: <a href="https://mistral.ai/">https://mistral.ai/</a>'
),
})[provider.id.toString()]
providerImage: "icon" in provider ? provider.icon : "qrc:/gpt4all/icons/antenna_3.svg"
providerDesc: {
if (!provider.isBuiltin) {
switch (provider.type) {
case ProviderStore.ProviderType.openai:
return qsTr("A custom OpenAI provider.");
case ProviderStore.ProviderType.ollama:
return qsTr("A custom Ollama provider.");
}
}
return ({
'{20f963dc-1f99-441e-ad80-f30a0a06bcac}': qsTr(
'Groq offers a high-performance AI inference engine designed for low-latency and ' +
'efficient processing. Optimized for real-time applications, Groqs technology is ideal ' +
'for users who need fast responses from open large language models and other AI ' +
'workloads.<br><br>Get your API key: ' +
'<a href="https://console.groq.com/keys">https://groq.com/</a>'
),
'{6f874c3a-f1ad-47f7-9129-755c5477146c}': qsTr(
'OpenAI provides access to advanced AI models, including GPT-4 supporting a wide range ' +
'of applications, from conversational AI to content generation and code completion.' +
'<br><br>Get your API key: ' +
'<a href="https://platform.openai.com/signup">https://openai.com/</a>'
),
'{7ae617b3-c0b2-4d2c-9ff2-bc3f049494cc}': qsTr(
'Mistral AI specializes in efficient, open-weight language models optimized for various ' +
'natural language processing tasks. Their models are designed for flexibility and ' +
'performance, making them a solid option for applications requiring scalable AI ' +
'solutions.<br><br>Get your API key: <a href="https://mistral.ai/">https://mistral.ai/</a>'
),
})[provider.id.toString()];
}
}
}
}

View File

@ -131,6 +131,8 @@ Rectangle {
}
onTextChanged: {
if (!initialized) return;
console.log(`${provider} has an apiKey: ${('apiKey' in provider)},${typeof provider.apiKey},${provider.apiKey}`);
return;
ok = provider.setApiKeyQml(text.trim()) && text.trim() !== "";
}
placeholderText: qsTr("Provider API Key")
@ -208,7 +210,7 @@ Rectangle {
property string apiKeyText: apiKeyField.text.trim()
property string modelNameText: myModelList.currentText.trim()
enabled: baseUrlGood && apiKeyGood && modelNameText !== ""
enabled: nameField.ok && baseUrlField.ok && apiKeyField.ok && modelNameText !== ""
onClicked: {
Download.installCompatibleModel(

View File

@ -64,12 +64,6 @@ auto OllamaProvider::listModels() -> QCoro::Task<backend::DataOrRespErr<QStringL
co_return res;
}
QCoro::QmlTask OllamaProvider::statusQml()
{ return wrapQmlTask(this, &OllamaProvider::status, u"OllamaProvider::status"_s); }
QCoro::QmlTask OllamaProvider::listModelsQml()
{ return wrapQmlTask(this, &OllamaProvider::listModels, u"OllamaProvider::listModels"_s); }
auto OllamaProvider::newModel(const QByteArray &modelHash) const -> std::shared_ptr<OllamaModelDescription>
{ return std::static_pointer_cast<OllamaModelDescription>(newModelImpl(modelHash)); }

View File

@ -46,8 +46,9 @@ protected:
class OllamaProvider : public QObject, public virtual ModelProvider {
Q_OBJECT
Q_PROPERTY(QUuid id READ id CONSTANT)
Q_PROPERTY(bool isBuiltin READ isBuiltin CONSTANT)
Q_PROPERTY(QUuid id READ id CONSTANT)
Q_PROPERTY(bool isBuiltin READ isBuiltin CONSTANT)
Q_PROPERTY(ProviderType type READ type CONSTANT)
protected:
explicit OllamaProvider();
@ -58,6 +59,8 @@ public:
QObject *asQObject() override { return this; }
const QObject *asQObject() const override { return this; }
ProviderType type() const final { return ProviderType::ollama; }
auto supportedGenerationParams() const -> QSet<GenerationParam> override;
auto makeGenerationParams(const QMap<GenerationParam, QVariant> &values) const -> OllamaGenerationParams * override;
@ -65,9 +68,9 @@ public:
auto status () -> QCoro::Task<ProviderStatus > override;
auto listModels() -> QCoro::Task<backend::DataOrRespErr<QStringList>> override;
// QML wrapped endpoints
Q_INVOKABLE QCoro::QmlTask statusQml ();
Q_INVOKABLE QCoro::QmlTask listModelsQml();
// QML endpoints
Q_INVOKABLE QCoro::QmlTask statusQml () { return ModelProvider::statusQml (); }
Q_INVOKABLE QCoro::QmlTask listModelsQml() { return ModelProvider::listModelsQml(); }
[[nodiscard]] auto newModel(const QByteArray &modelHash) const -> std::shared_ptr<OllamaModelDescription>;
@ -103,6 +106,10 @@ public:
/// Create a new OllamaProvider on disk.
explicit OllamaProviderCustom(protected_t p, ProviderStore *store, QString name, QUrl baseUrl);
// QML setters
Q_INVOKABLE bool setNameQml (QString value) { return ModelProviderCustom::setNameQml (std::move(value)); }
Q_INVOKABLE bool setBaseUrlQml(QString value) { return ModelProviderCustom::setBaseUrlQml(std::move(value)); }
Q_SIGNALS:
void nameChanged (const QString &value);
void baseUrlChanged(const QUrl &value);

View File

@ -155,12 +155,6 @@ auto OpenaiProvider::listModels() -> QCoro::Task<backend::DataOrRespErr<QStringL
co_return models;
}
QCoro::QmlTask OpenaiProvider::statusQml()
{ return wrapQmlTask(this, &OpenaiProvider::status, u"OpenaiProvider::status"_s); }
QCoro::QmlTask OpenaiProvider::listModelsQml()
{ return wrapQmlTask(this, &OpenaiProvider::listModels, u"OpenaiProvider::listModels"_s); }
auto OpenaiProvider::newModel(const QString &modelName) const -> std::shared_ptr<OpenaiModelDescription>
{ return std::static_pointer_cast<OpenaiModelDescription>(newModelImpl(modelName)); }

View File

@ -50,9 +50,10 @@ protected:
class OpenaiProvider : public QObject, public virtual ModelProvider {
Q_OBJECT
Q_PROPERTY(QUuid id READ id CONSTANT )
Q_PROPERTY(QString apiKey READ apiKey NOTIFY apiKeyChanged)
Q_PROPERTY(bool isBuiltin READ isBuiltin CONSTANT )
Q_PROPERTY(QUuid id READ id CONSTANT )
Q_PROPERTY(QString apiKey READ apiKey NOTIFY apiKeyChanged)
Q_PROPERTY(bool isBuiltin READ isBuiltin CONSTANT )
Q_PROPERTY(ProviderType type READ type CONSTANT )
protected:
explicit OpenaiProvider();
@ -64,6 +65,8 @@ public:
QObject *asQObject() override { return this; }
const QObject *asQObject() const override { return this; }
ProviderType type() const final { return ProviderType::openai; }
[[nodiscard]] const QString &apiKey() const { return m_apiKey; }
[[nodiscard]] virtual DataStoreResult<> setApiKey(QString value) = 0;
@ -77,9 +80,9 @@ public:
auto status () -> QCoro::Task<ProviderStatus > override;
auto listModels() -> QCoro::Task<backend::DataOrRespErr<QStringList>> override;
// QML wrapped endpoints
Q_INVOKABLE QCoro::QmlTask statusQml ();
Q_INVOKABLE QCoro::QmlTask listModelsQml();
// QML endpoints
Q_INVOKABLE QCoro::QmlTask statusQml () { return ModelProvider::statusQml (); }
Q_INVOKABLE QCoro::QmlTask listModelsQml() { return ModelProvider::listModelsQml(); }
[[nodiscard]] auto newModel(const QString &modelName) const -> std::shared_ptr<OpenaiModelDescription>;
@ -140,6 +143,10 @@ public:
[[nodiscard]] DataStoreResult<> setApiKey(QString value) override
{ return setMemberProp<QString>(&OpenaiProviderCustom::m_apiKey, "apiKey", std::move(value)); }
// QML setters
Q_INVOKABLE bool setNameQml (QString value) { return ModelProviderCustom::setNameQml (std::move(value)); }
Q_INVOKABLE bool setBaseUrlQml(QString value) { return ModelProviderCustom::setBaseUrlQml(std::move(value)); }
Q_SIGNALS:
void nameChanged (const QString &value);
void baseUrlChanged(const QUrl &value);

View File

@ -5,6 +5,7 @@
#include "qmlsharedptr.h" // IWYU pragma: keep
#include "utils.h" // IWYU pragma: keep
#include <QCoro/QCoroQmlTask> // IWYU pragma: keep
#include <gpt4all-backend/ollama-client.h>
#include <QAbstractListModel>
@ -34,15 +35,12 @@ class QJSEngine;
template <typename Key, typename T> class QHash;
namespace QCoro {
template <typename T> class Task;
struct QmlTask;
}
namespace gpt4all::ui {
Q_NAMESPACE
class ModelDescription;
namespace detail {
@ -71,6 +69,10 @@ QCoro::QmlTask wrapQmlTask(C *obj, F f, QString prefix, Args &&...args);
template <typename C, typename F, typename... Args>
bool wrapQmlFunc(C *obj, F &&f, QStringView prefix, Args &&...args);
inline namespace llmodel_provider {
Q_NAMESPACE
enum class GenerationParam {
NPredict,
Temperature,
@ -82,6 +84,8 @@ enum class GenerationParam {
};
Q_ENUM_NS(GenerationParam)
} // inline namespace llmodel_provider
class GenerationParams {
public:
virtual ~GenerationParams() noexcept = 0;
@ -128,7 +132,8 @@ public:
virtual QObject *asQObject() = 0;
virtual const QObject *asQObject() const = 0;
virtual bool isBuiltin() const = 0;
virtual bool isBuiltin() const = 0;
virtual ProviderType type () const = 0;
// getters
[[nodiscard]] const QUuid &id () const { return m_id; }
@ -142,6 +147,12 @@ public:
virtual auto status () -> QCoro::Task<ProviderStatus > = 0;
virtual auto listModels() -> QCoro::Task<backend::DataOrRespErr<QStringList>> = 0;
// QML endpoints
QCoro::QmlTask statusQml()
{ return wrapQmlTask(this, &ModelProvider::status, QStringLiteral("ModelProvider::status") ); }
QCoro::QmlTask listModelsQml()
{ return wrapQmlTask(this, &ModelProvider::listModels, QStringLiteral("ModelProvider::listModels")); }
/// create a model using this provider
[[nodiscard]] auto newModel(const QVariant &key) const -> std::shared_ptr<ModelDescription>;
@ -207,9 +218,9 @@ public:
{ return setMemberProp<QUrl >(&ModelProviderCustom::m_baseUrl, "baseUrl", std::move(value)); }
// QML setters
Q_INVOKABLE bool setNameQml (QString value)
bool setNameQml (QString value)
{ return wrapQmlFunc(this, &ModelProviderCustom::setName, u"setName", std::move(value)); }
Q_INVOKABLE bool setBaseUrlQml(QString value)
bool setBaseUrlQml(QString value)
{ return wrapQmlFunc(this, &ModelProviderCustom::setBaseUrl, u"setBaseUrl", std::move(value)); }
[[nodiscard]] auto persist() -> DataStoreResult<>;

View File

@ -1,7 +1,6 @@
#include <fmt/format.h>
#include <QCoro/QCoroQmlTask>
#include <QCoro/QCoroTask>
#include <QCoro/QCoroTask> // IWYU pragma: keep
#include <QDebug>
#include <QVariant>

View File

@ -157,6 +157,8 @@ int main(int argc, char *argv[])
qmlRegisterSingletonInstance("gpt4all.ProviderRegistry", 1, 0, "ProviderRegistry", ProviderRegistry::globalInstance());
qmlRegisterUncreatableMetaObject(ToolEnums::staticMetaObject, "toolenums", 1, 0, "ToolEnums", "Error: only enums");
qmlRegisterUncreatableMetaObject(MySettingsEnums::staticMetaObject, "mysettingsenums", 1, 0, "MySettingsEnums", "Error: only enums");
qmlRegisterUncreatableMetaObject(gpt4all::ui::llmodel_provider::staticMetaObject, "gpt4all.llmodel_provider", 1, 0, "Provider", "Error: only enums");
qmlRegisterUncreatableMetaObject(gpt4all::ui::store_provider::staticMetaObject, "gpt4all.store_provider", 1, 0, "ProviderStore", "Error: only enums");
{
auto fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);

View File

@ -42,7 +42,7 @@ auto tag_invoke(const boost::json::value_to_tag<ModelProviderData> &, const boos
json::value_to<QString>(jv.at("name" )),
json::value_to<QString>(jv.at("base_url")),
});
ModelProviderData::ProviderDetails provider_details;
ModelProviderData::ProviderDetails provider_details = std::monostate();
switch (type) {
using enum ProviderType;
case openai:

View File

@ -6,6 +6,7 @@
#include <boost/describe/enum.hpp>
#include <boost/json.hpp> // IWYU pragma: keep
#include <QObject>
#include <QString>
#include <QUrl>
#include <QUuid>
@ -14,12 +15,19 @@
namespace gpt4all::ui {
inline namespace store_provider {
Q_NAMESPACE
// indices of this enum should be consistent with indices of ProviderDetails
enum class ProviderType {
openai = 0,
ollama = 1,
};
BOOST_DESCRIBE_ENUM(ProviderType, openai, ollama)
Q_ENUM_NS(ProviderType)
} // inline namespace provider
struct CustomProviderDetails {
QString name;