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
|
||||
|
||||
# Ensure internal libs are available to dependents
|
||||
add_subdirectory(dal)
|
||||
|
||||
add_library(kompanion_mw SHARED
|
||||
middleware/kompanioncontroller.cpp
|
||||
middleware/libkiexecutor.cpp
|
||||
|
|
@ -8,7 +11,7 @@ add_library(kompanion_mw SHARED
|
|||
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_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_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)
|
||||
|
||||
# Example executable wiring GUI/controller/executor together could be added later.
|
||||
|
|
|
|||
|
|
@ -867,6 +867,18 @@ int main(int argc, char** argv) {
|
|||
"path");
|
||||
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("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 initRequested = parser.isSet(initOption);
|
||||
const bool runMcp = parser.isSet(mcpServeOption);
|
||||
const bool snapSave = parser.isSet(snapshotSaveOption);
|
||||
const bool snapLoad = parser.isSet(snapshotLoadOption);
|
||||
|
||||
std::optional<std::string> configDsn = readDsnFromConfig();
|
||||
const char* envDsn = std::getenv("PG_DSN");
|
||||
|
|
@ -932,6 +946,34 @@ int main(int argc, char** argv) {
|
|||
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)
|
||||
if (parser.isSet(dbNsOption)) {
|
||||
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_link_libraries(kom_dal PUBLIC Qt6::Core Qt6::Sql)
|
||||
target_compile_options(kom_dal PRIVATE -fexceptions)
|
||||
set_target_properties(kom_dal PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
#include <QTextStream>
|
||||
#include <QTimer>
|
||||
|
||||
#include "dal/PgDal.hpp"
|
||||
|
||||
// ---------- OllamaModelProvider ----------
|
||||
OllamaModelProvider::OllamaModelProvider(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).
|
||||
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:
|
||||
void taskProcessed(const QString &kind);
|
||||
|
||||
|
|
@ -88,4 +105,3 @@ private:
|
|||
bool continuityDone_ = false;
|
||||
IModelProvider *model_ = nullptr; // not owned
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue