#pragma once #include #include #include #include #include #include #include "kompanion_mw_export.h" namespace ki { class PgDal; } // Minimal model provider interface so tests can stub generation. class IModelProvider { public: virtual ~IModelProvider() = default; virtual QString generate(const QString &prompt, const QString &aspect) = 0; }; // Default Ollama-backed provider. Uses OLLAMA_BASE and simple /api/generate call. class OllamaModelProvider : public QObject, public IModelProvider { Q_OBJECT public: explicit OllamaModelProvider(QObject *parent=nullptr); QString generate(const QString &prompt, const QString &aspect) override; void setBaseUrl(const QString &base) { baseUrl_ = base; } void setDefaultModel(const QString &m) { defaultModel_ = m; } private: QString chooseModelForAspect(const QString &aspect) const; // simple heuristic QString baseUrl_; QString defaultModel_ = QStringLiteral("qwen2.5:7b"); QNetworkAccessManager nam_; }; // Simple stub provider used by tests; returns deterministic text. class StubModelProvider : public IModelProvider { public: explicit StubModelProvider(QString canned) : canned_(std::move(canned)) {} QString generate(const QString &prompt, const QString &aspect) override { Q_UNUSED(prompt); Q_UNUSED(aspect); return canned_; } private: QString canned_; }; // Orchestrator: replicates runtime/kom_runner.py behaviors in C++. // - Watches a JSONL tasks file under XDG_STATE_HOME/kompanion // - Processes tasks like {"type":"journal.from_prompt", "prompt":"...", "aspect":"companion"} // - Appends to journal (/journal/YYYY-MM-DD.md) and to a simple ledger JSONL class KOMPANION_MW_EXPORT Orchestrator : public QObject { Q_OBJECT public: explicit Orchestrator(QObject *parent=nullptr); ~Orchestrator(); // Injectable model provider (Ollama by default). Ownership left to caller. void setModelProvider(IModelProvider *prov) { model_ = prov; } // Directories resolved from XDG_* on start(); overridable for tests. void setStateDir(const QDir &dir) { stateDir_ = dir; } void setConfigDir(const QDir &dir) { configDir_ = dir; } // Poll loop control. void start(int intervalMs = 3000); void stop(); // 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 loadSnapshot(const QString &nameSpace, const QString &key); signals: void taskProcessed(const QString &kind); private: void ensureResolvedDirs(); void continuityHandshakeOnce(); void ledgerAppend(const QJsonObject &evt); void journalAppend(const QString &line); // Also emits ledger entry QString nowUtc() const; // Task handlers void handleJournalFromPrompt(const QJsonObject &obj); // Helpers QString tasksPath() const { return stateDir_.filePath("tasks.jsonl"); } QString journalDirPath() const { return stateDir_.filePath("journal"); } QString ledgerPath() const { return stateDir_.filePath("trust_ledger.jsonl"); } QDir stateDir_; QDir configDir_; QTimer timer_; bool continuityDone_ = false; IModelProvider *model_ = nullptr; // not owned // Reused DB handle so in-memory stub persists across calls in tests. ki::PgDal* dal_ = nullptr; ki::PgDal& dal(); };