middleware: add snapshot helpers (save/load) backed by PgDal; link kom_dal with PIC; CLI: add --snapshot-save/--snapshot-load for easy syntax
This commit is contained in:
parent
70bd4ea064
commit
c36ef7e46b
|
|
@ -1,5 +1,8 @@
|
||||||
# Subdir CMake for src
|
# Subdir CMake for src
|
||||||
|
|
||||||
|
# Ensure internal libs are available to dependents
|
||||||
|
add_subdirectory(dal)
|
||||||
|
|
||||||
add_library(kompanion_mw SHARED
|
add_library(kompanion_mw SHARED
|
||||||
middleware/kompanioncontroller.cpp
|
middleware/kompanioncontroller.cpp
|
||||||
middleware/libkiexecutor.cpp
|
middleware/libkiexecutor.cpp
|
||||||
|
|
@ -8,7 +11,7 @@ add_library(kompanion_mw SHARED
|
||||||
middleware/orchestrator.cpp
|
middleware/orchestrator.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Qt6 REQUIRED COMPONENTS Core DBus)
|
find_package(Qt6 REQUIRED COMPONENTS Core DBus Sql)
|
||||||
|
|
||||||
set(KOMPANION_CONTROLLER_DBUS_XML ${CMAKE_CURRENT_SOURCE_DIR}/../docs/dbus/org.kde.kompanion.controller.xml)
|
set(KOMPANION_CONTROLLER_DBUS_XML ${CMAKE_CURRENT_SOURCE_DIR}/../docs/dbus/org.kde.kompanion.controller.xml)
|
||||||
set(KOMPANION_EXECUTOR_DBUS_XML ${CMAKE_CURRENT_SOURCE_DIR}/../docs/dbus/org.kde.kompanion.executor.xml)
|
set(KOMPANION_EXECUTOR_DBUS_XML ${CMAKE_CURRENT_SOURCE_DIR}/../docs/dbus/org.kde.kompanion.executor.xml)
|
||||||
|
|
@ -31,7 +34,7 @@ target_include_directories(kompanion_mw PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/middl
|
||||||
|
|
||||||
target_sources(kompanion_mw PRIVATE ${KOMPANION_DBUS_ADAPTOR_SRCS} ${KOMPANION_DBUS_INTERFACE_SRCS})
|
target_sources(kompanion_mw PRIVATE ${KOMPANION_DBUS_ADAPTOR_SRCS} ${KOMPANION_DBUS_INTERFACE_SRCS})
|
||||||
|
|
||||||
target_link_libraries(kompanion_mw PRIVATE Qt6::Core Qt6::DBus Qt6::Network)
|
target_link_libraries(kompanion_mw PRIVATE Qt6::Core Qt6::DBus Qt6::Sql Qt6::Network kom_dal)
|
||||||
target_compile_definitions(kompanion_mw PRIVATE KOMPANION_MW_LIBRARY)
|
target_compile_definitions(kompanion_mw PRIVATE KOMPANION_MW_LIBRARY)
|
||||||
|
|
||||||
# Example executable wiring GUI/controller/executor together could be added later.
|
# Example executable wiring GUI/controller/executor together could be added later.
|
||||||
|
|
|
||||||
|
|
@ -867,6 +867,18 @@ int main(int argc, char** argv) {
|
||||||
"path");
|
"path");
|
||||||
parser.addOption(embFileOption);
|
parser.addOption(embFileOption);
|
||||||
|
|
||||||
|
// Snapshot helpers
|
||||||
|
QCommandLineOption snapshotSaveOption(QStringList() << "--snapshot-save",
|
||||||
|
"Save a JSON snapshot (content) under a key in --ns. Provide content via -r/--stdin/[payload].");
|
||||||
|
parser.addOption(snapshotSaveOption);
|
||||||
|
QCommandLineOption snapshotLoadOption(QStringList() << "--snapshot-load",
|
||||||
|
"Load a JSON snapshot for --ns and --key and print it.");
|
||||||
|
parser.addOption(snapshotLoadOption);
|
||||||
|
QCommandLineOption keyOption(QStringList() << "--key",
|
||||||
|
"Key for snapshot operations (default 'session:last').",
|
||||||
|
"key", "session:last");
|
||||||
|
parser.addOption(keyOption);
|
||||||
|
|
||||||
parser.addPositionalArgument("tool", "Tool name to invoke.");
|
parser.addPositionalArgument("tool", "Tool name to invoke.");
|
||||||
parser.addPositionalArgument("payload", "Optional JSON payload or file path (use '-' for stdin).", "[payload]");
|
parser.addPositionalArgument("payload", "Optional JSON payload or file path (use '-' for stdin).", "[payload]");
|
||||||
|
|
||||||
|
|
@ -880,6 +892,8 @@ int main(int argc, char** argv) {
|
||||||
const bool interactive = parser.isSet(interactiveOption);
|
const bool interactive = parser.isSet(interactiveOption);
|
||||||
const bool initRequested = parser.isSet(initOption);
|
const bool initRequested = parser.isSet(initOption);
|
||||||
const bool runMcp = parser.isSet(mcpServeOption);
|
const bool runMcp = parser.isSet(mcpServeOption);
|
||||||
|
const bool snapSave = parser.isSet(snapshotSaveOption);
|
||||||
|
const bool snapLoad = parser.isSet(snapshotLoadOption);
|
||||||
|
|
||||||
std::optional<std::string> configDsn = readDsnFromConfig();
|
std::optional<std::string> configDsn = readDsnFromConfig();
|
||||||
const char* envDsn = std::getenv("PG_DSN");
|
const char* envDsn = std::getenv("PG_DSN");
|
||||||
|
|
@ -932,6 +946,34 @@ int main(int argc, char** argv) {
|
||||||
return runMcpServer(backend, addr, qerr);
|
return runMcpServer(backend, addr, qerr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snapshot helpers (exclusive)
|
||||||
|
if (snapSave || snapLoad) {
|
||||||
|
const QString ns = parser.value(nsNameOption);
|
||||||
|
if (ns.isEmpty()) { qerr << "--snapshot-save/load requires --ns <name>\n"; return 1; }
|
||||||
|
const QString key = parser.value(keyOption);
|
||||||
|
if (snapSave) {
|
||||||
|
// Resolve content from CLI
|
||||||
|
std::string raw;
|
||||||
|
QString err;
|
||||||
|
if (!resolveRequestPayload(parser, parser.positionalArguments(), requestOption, stdinOption, raw, &err)) {
|
||||||
|
qerr << (err.isEmpty() ? QStringLiteral("Failed to read snapshot content") : err) << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// Wrap into save_context call
|
||||||
|
std::ostringstream req;
|
||||||
|
req << "{\"namespace\":\"" << ns.toStdString() << "\",\"key\":\"" << key.toStdString() << "\",\"content\":" << raw << ",\"tags\":[\"snapshot\"]}";
|
||||||
|
const std::string out = server.dispatch("kom.memory.v1.save_context", req.str());
|
||||||
|
std::cout << out << std::endl;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
std::ostringstream req;
|
||||||
|
req << "{\"namespace\":\"" << ns.toStdString() << "\",\"key\":\"" << key.toStdString() << "\"}";
|
||||||
|
const std::string out = server.dispatch("kom.memory.v1.recall_context", req.str());
|
||||||
|
std::cout << out << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DB inspection helpers (exclusive)
|
// DB inspection helpers (exclusive)
|
||||||
if (parser.isSet(dbNsOption)) {
|
if (parser.isSet(dbNsOption)) {
|
||||||
return dbListNamespaces(qout) ? 0 : 1;
|
return dbListNamespaces(qout) ? 0 : 1;
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ target_compile_features(kom_dal PUBLIC cxx_std_20)
|
||||||
target_include_directories(kom_dal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(kom_dal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_link_libraries(kom_dal PUBLIC Qt6::Core Qt6::Sql)
|
target_link_libraries(kom_dal PUBLIC Qt6::Core Qt6::Sql)
|
||||||
target_compile_options(kom_dal PRIVATE -fexceptions)
|
target_compile_options(kom_dal PRIVATE -fexceptions)
|
||||||
|
set_target_properties(kom_dal PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "dal/PgDal.hpp"
|
||||||
|
|
||||||
// ---------- OllamaModelProvider ----------
|
// ---------- OllamaModelProvider ----------
|
||||||
OllamaModelProvider::OllamaModelProvider(QObject *parent)
|
OllamaModelProvider::OllamaModelProvider(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
|
@ -170,3 +172,48 @@ void Orchestrator::processPendingTasks() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Orchestrator::saveSnapshot(const QString &nameSpace,
|
||||||
|
const QString &key,
|
||||||
|
const QJsonObject &content,
|
||||||
|
const QStringList &tags)
|
||||||
|
{
|
||||||
|
ki::PgDal dal;
|
||||||
|
const QByteArray dsn = qgetenv("PG_DSN");
|
||||||
|
if (!dsn.isEmpty()) dal.connect(dsn.toStdString()); else dal.connect("stub://memory");
|
||||||
|
|
||||||
|
auto nsRow = dal.ensureNamespace(nameSpace.toStdString());
|
||||||
|
if (!nsRow) return false;
|
||||||
|
|
||||||
|
ki::ItemRow row;
|
||||||
|
row.namespace_id = nsRow->id;
|
||||||
|
row.key = key.toStdString();
|
||||||
|
row.tags.reserve(tags.size());
|
||||||
|
for (const auto &t : tags) row.tags.push_back(t.toStdString());
|
||||||
|
row.content_json = QString::fromUtf8(QJsonDocument(content).toJson(QJsonDocument::Compact)).toStdString();
|
||||||
|
row.metadata_json = "{}";
|
||||||
|
row.created_at = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
const std::string id = dal.upsertItem(row);
|
||||||
|
return !id.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QJsonObject> Orchestrator::loadSnapshot(const QString &nameSpace,
|
||||||
|
const QString &key)
|
||||||
|
{
|
||||||
|
ki::PgDal dal;
|
||||||
|
const QByteArray dsn = qgetenv("PG_DSN");
|
||||||
|
if (!dsn.isEmpty()) dal.connect(dsn.toStdString()); else dal.connect("stub://memory");
|
||||||
|
|
||||||
|
auto nsRow = dal.findNamespace(nameSpace.toStdString());
|
||||||
|
if (!nsRow) return std::nullopt;
|
||||||
|
|
||||||
|
std::vector<std::string> tags; tags.emplace_back("snapshot");
|
||||||
|
auto rows = dal.fetchContext(nsRow->id, std::optional<std::string>(key.toStdString()), tags, std::nullopt, 1);
|
||||||
|
if (rows.empty()) return std::nullopt;
|
||||||
|
const auto &row = rows.front();
|
||||||
|
if (row.content_json.empty()) return std::nullopt;
|
||||||
|
const auto doc = QJsonDocument::fromJson(QByteArray::fromStdString(row.content_json));
|
||||||
|
if (!doc.isObject()) return std::nullopt;
|
||||||
|
return doc.object();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,23 @@ public:
|
||||||
// One-shot tick (public for tests).
|
// One-shot tick (public for tests).
|
||||||
void processPendingTasks();
|
void processPendingTasks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* saveSnapshot: persist a JSON snapshot under (namespace,key) as a context item.
|
||||||
|
* - Tags default to {"snapshot"}.
|
||||||
|
* - If PG_DSN is unset, uses an in-memory stub (won't persist across restarts).
|
||||||
|
*/
|
||||||
|
bool saveSnapshot(const QString &nameSpace,
|
||||||
|
const QString &key,
|
||||||
|
const QJsonObject &content,
|
||||||
|
const QStringList &tags = {QStringLiteral("snapshot")});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loadSnapshot: fetch the latest item for (namespace,key) tagged as snapshot.
|
||||||
|
* Returns empty optional if not found or on failure.
|
||||||
|
*/
|
||||||
|
std::optional<QJsonObject> loadSnapshot(const QString &nameSpace,
|
||||||
|
const QString &key);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void taskProcessed(const QString &kind);
|
void taskProcessed(const QString &kind);
|
||||||
|
|
||||||
|
|
@ -88,4 +105,3 @@ private:
|
||||||
bool continuityDone_ = false;
|
bool continuityDone_ = false;
|
||||||
IModelProvider *model_ = nullptr; // not owned
|
IModelProvider *model_ = nullptr; // not owned
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue