Convert to QtSql and extend
This commit is contained in:
parent
a04506a081
commit
70848fda6e
108
CMakeLists.txt
108
CMakeLists.txt
|
|
@ -1,60 +1,66 @@
|
|||
cmake_minimum_required(VERSION 3.22)
|
||||
project(metal_kompanion_mcp LANGUAGES CXX)
|
||||
project(Kompanion LANGUAGES CXX)
|
||||
set(PROJECT_VERSION "0.0.1")
|
||||
|
||||
set(QT_MIN_VERSION "6.8.0")
|
||||
set(KF6_MIN_VERSION "6.8.0")
|
||||
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
|
||||
|
||||
option(KOM_ENABLE_PG "Enable Postgres-backed DAL support" ON)
|
||||
include(KDEInstallDirs)
|
||||
include(KDECMakeSettings)
|
||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||
include(ECMMarkAsTest)
|
||||
include(ECMMarkNonGuiExecutable)
|
||||
include(FeatureSummary)
|
||||
include(CheckIncludeFile)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckSymbolExists)
|
||||
include(ECMOptionalAddSubdirectory)
|
||||
include(KDEClangFormat)
|
||||
include(ECMDeprecationSettings)
|
||||
|
||||
set(KOM_HAVE_PG OFF)
|
||||
if (KOM_ENABLE_PG)
|
||||
find_package(PostgreSQL QUIET)
|
||||
find_package(pqxx QUIET)
|
||||
if (PostgreSQL_FOUND AND pqxx_FOUND)
|
||||
set(KOM_HAVE_PG ON)
|
||||
endif()
|
||||
include(KDEGitCommitHooks)
|
||||
|
||||
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
|
||||
Core
|
||||
Sql
|
||||
)
|
||||
|
||||
option(KOMPANION_USE_GUI "Build optional GUI components using Qt6Gui" ON)
|
||||
if (KOMPANION_USE_GUI)
|
||||
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Gui)
|
||||
endif()
|
||||
|
||||
if (KOM_HAVE_PG)
|
||||
message(STATUS "kom_dal: Postgres support enabled (HAVE_PG)")
|
||||
else()
|
||||
message(STATUS "kom_dal: Postgres support disabled (pqxx/libpq not found)")
|
||||
endif()
|
||||
|
||||
set(KOM_HAVE_PG ${KOM_HAVE_PG} CACHE INTERNAL "kom_dal has Postgres backend")
|
||||
|
||||
set(KOMPANION_KCONFIG_TARGET "")
|
||||
find_package(KF6Config QUIET)
|
||||
find_package(KF6Config ${KF6_MIN_VERSION} QUIET)
|
||||
if (KF6Config_FOUND)
|
||||
set(KOMPANION_KCONFIG_TARGET KF6::ConfigCore)
|
||||
set(KOMPANION_KCONFIG_TARGET KF6::ConfigCore)
|
||||
else()
|
||||
find_package(KF5Config QUIET)
|
||||
if (KF5Config_FOUND)
|
||||
set(KOMPANION_KCONFIG_TARGET KF5::ConfigCore)
|
||||
endif()
|
||||
find_package(KF5Config QUIET)
|
||||
if (KF5Config_FOUND)
|
||||
set(KOMPANION_KCONFIG_TARGET KF5::ConfigCore)
|
||||
endif()
|
||||
endif()
|
||||
add_feature_info("KConfig" NOT (KOMPANION_KCONFIG_TARGET STREQUAL "") "Persist settings via KConfig if available.")
|
||||
|
||||
if (KOMPANION_KCONFIG_TARGET STREQUAL "")
|
||||
message(WARNING "KConfig (KF6/KF5) not found; defaulting to environment-based configuration.")
|
||||
endif()
|
||||
find_package(Qt6Test ${QT_MIN_VERSION} CONFIG QUIET)
|
||||
set_package_properties(Qt6Test PROPERTIES
|
||||
PURPOSE "Required for tests"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
add_feature_info("Qt6Test" Qt6Test_FOUND "Required for building tests")
|
||||
|
||||
set(KOMPANION_DB_INIT_INSTALL_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/kompanion/db/init")
|
||||
install(DIRECTORY db/init/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/kompanion/db/init FILES_MATCHING PATTERN "*.sql")
|
||||
|
||||
set(KOMPANION_DB_INIT_INSTALL_DIR "${KDE_INSTALL_FULL_DATADIR}/kompanion/db/init")
|
||||
install(DIRECTORY db/init/ DESTINATION ${KDE_INSTALL_DATADIR}/kompanion/db/init FILES_MATCHING PATTERN "*.sql")
|
||||
|
||||
option(BUILD_KOMPANION_CLI "Build Kompanion Qt command-line client" ON)
|
||||
if (BUILD_KOMPANION_CLI)
|
||||
find_package(Qt6 COMPONENTS Core QUIET)
|
||||
if (NOT Qt6_FOUND)
|
||||
message(WARNING "Qt6 Core not found; disabling Kompanion CLI.")
|
||||
set(BUILD_KOMPANION_CLI OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Placeholder: find Qt and qtmcp when available
|
||||
# find_package(Qt6 COMPONENTS Core Network REQUIRED)
|
||||
# find_package(qtmcp REQUIRED)
|
||||
|
||||
add_subdirectory(src/dal)
|
||||
|
||||
|
|
@ -63,14 +69,15 @@ add_executable(kom_mcp
|
|||
)
|
||||
target_include_directories(kom_mcp PRIVATE src)
|
||||
target_link_libraries(kom_mcp PRIVATE kom_dal)
|
||||
if (NOT KOMPANION_KCONFIG_TARGET STREQUAL "")
|
||||
target_link_libraries(kom_mcp PRIVATE ${KOMPANION_KCONFIG_TARGET})
|
||||
target_compile_definitions(kom_mcp PRIVATE HAVE_KCONFIG)
|
||||
endif()
|
||||
target_compile_options(kom_mcp PRIVATE -fexceptions)
|
||||
target_compile_definitions(kom_mcp PRIVATE
|
||||
PROJECT_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
|
||||
KOMPANION_DB_INIT_INSTALL_DIR="${KOMPANION_DB_INIT_INSTALL_DIR}"
|
||||
)
|
||||
if (NOT KOMPANION_KCONFIG_TARGET STREQUAL "")
|
||||
target_link_libraries(kom_mcp PRIVATE ${KOMPANION_KCONFIG_TARGET})
|
||||
target_compile_definitions(kom_mcp PRIVATE HAVE_KCONFIG)
|
||||
endif()
|
||||
|
||||
install(TARGETS kom_mcp RUNTIME DESTINATION bin)
|
||||
|
||||
|
|
@ -81,15 +88,20 @@ if (BUILD_KOMPANION_CLI)
|
|||
src/cli/KompanionApp.cpp
|
||||
)
|
||||
target_include_directories(kompanion PRIVATE src)
|
||||
target_link_libraries(kompanion PRIVATE Qt6::Core kom_dal)
|
||||
target_link_libraries(kompanion PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Sql
|
||||
kom_dal
|
||||
)
|
||||
target_compile_definitions(kompanion PRIVATE
|
||||
PROJECT_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
|
||||
KOMPANION_DB_INIT_INSTALL_DIR="${KOMPANION_DB_INIT_INSTALL_DIR}"
|
||||
)
|
||||
if (NOT KOMPANION_KCONFIG_TARGET STREQUAL "")
|
||||
target_link_libraries(kompanion PRIVATE ${KOMPANION_KCONFIG_TARGET})
|
||||
target_compile_definitions(kompanion PRIVATE HAVE_KCONFIG)
|
||||
target_link_libraries(kompanion PRIVATE ${KOMPANION_KCONFIG_TARGET})
|
||||
target_compile_definitions(kompanion PRIVATE HAVE_KCONFIG)
|
||||
endif()
|
||||
target_compile_options(kompanion PRIVATE -fexceptions -Wno-unused-function)
|
||||
install(TARGETS kompanion RUNTIME DESTINATION bin)
|
||||
endif()
|
||||
|
||||
|
|
@ -97,3 +109,5 @@ if (BUILD_TESTS)
|
|||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ The CLI (`kompanion`) and MCP runner (`kom_mcp`) fall back to this entry when th
|
|||
## Initialization Wizard
|
||||
- Run `kompanion --init` to launch an interactive wizard.
|
||||
- Autodetects reachable Postgres instances (tries `postgresql://kompanion:komup@localhost/kompanion_test`).
|
||||
- Inspects local socket (`/var/run/postgresql`) and existing databases owned by the current user via `psql -At`, offering them as defaults.
|
||||
- Prompts for host, port, database, user, password, or Unix socket path with sensible defaults.
|
||||
- Writes the resulting DSN to `kompanionrc` and exports `PG_DSN` for the current session.
|
||||
- If the target database is empty, it applies the SQL migrations shipped under `share/kompanion/db/init/*.sql`.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Interfaces
|
||||
- `IDatabase` — connect/tx + memory ops (ensureNamespace, upsertItem/Chunks/Embeddings, searchText/searchVector).
|
||||
- `PgDal` — stub implementation for now (no libpq linked).
|
||||
- `PgDal` — Qt6/QSql-based implementation with in-memory fallback.
|
||||
|
||||
## SQL Calls (target)
|
||||
- ensureNamespace: `INSERT ... ON CONFLICT (name) DO UPDATE RETURNING id`
|
||||
|
|
@ -14,12 +14,11 @@
|
|||
|
||||
## Next
|
||||
- Wire `Handlers::upsert_memory` / `search_memory` to `IDatabase`.
|
||||
- Add libpq (or pqxx) and parameterized statements.
|
||||
- Add RLS/session GUCs & retries.
|
||||
- Harden SQL with RLS/session GUCs & retries.
|
||||
- Expand hybrid search scoring (RRF weights, secret filters).
|
||||
|
||||
## Implementation Checklist (2025-10-15)
|
||||
- Detect `libpq` + `pqxx` in CMake, define `HAVE_PG`, and link `kom_dal` against `PostgreSQL::PostgreSQL` and `pqxx::pqxx`.
|
||||
- During `PgDal::connect`, prepare statements for namespace ensure/upsert, chunk/embedding insert, text/vector search, and hybrid search.
|
||||
- Guard runtime selection: `stub://` DSN keeps the current in-memory store; non-stub DSNs require a live Postgres connection.
|
||||
- Expose environment variables in docs: `PG_DSN` (full libpq connection string) and optional `PGSSLMODE`.
|
||||
- Surface informative `std::runtime_error` messages when pqxx operations fail so MCP handlers can emit actionable errors.
|
||||
- Require Qt6::Sql (`QPSQL` driver) at configure time; bail out early when unavailable.
|
||||
- During `PgDal::connect`, parse DSNs with `QUrl`, open `QSqlDatabase`, and retain in-memory fallback for `stub://`.
|
||||
- Use `QSqlQuery` with `INSERT ... RETURNING` for namespace/item/chunk/embedding operations.
|
||||
- Derive DSNs from `kompanionrc` (KConfig) or CLI wizard, and surface informative `std::runtime_error` messages when QSql operations fail.
|
||||
|
|
|
|||
|
|
@ -1,30 +1,26 @@
|
|||
# Memory Architecture Roadmap (2025-10-15)
|
||||
|
||||
## Current Snapshot
|
||||
- `PgDal` remains an in-memory stub; `libpq`/`pqxx` are not linked and there is no `HAVE_PG` compile guard.
|
||||
- `contract_memory` now builds via `tests/contract_memory.cpp` as a dedicated CTest executable (stub DAL still backing it).
|
||||
- MCP handlers directly invoke the stub DAL and perform ad-hoc JSON parsing.
|
||||
- No `resources/` descriptors exist for episodic memory APIs or the semantic sync workflow.
|
||||
- `PgDal` now prefers Qt6/QSql (`QPSQL`) with an in-memory fallback for `stub://` DSNs; schema migrations live in `db/init/`.
|
||||
- `kompanion --init` guides DSN detection (psql socket probing), applies migrations, and persists config via `~/.config/kompanionrc`.
|
||||
- MCP handlers still parse JSON manually but leverage the shared DAL; resource descriptors under `resources/memory/kom.memory.v1/` capture episodic/semantic contracts.
|
||||
- Contract tests (`contract_memory`, `contract_mcp_tools`, `mcp_memory_exchange`) validate the Qt-backed DAL and MCP handlers.
|
||||
|
||||
## 1. CTest Target: `contract_memory`
|
||||
1. Keep `contract_memory.cpp` focused on exercising `PgDal` write/read surfaces; expand as DAL features land.
|
||||
2. Ensure the executable runs without Postgres by defaulting to `stub://memory` when `PG_DSN` is absent.
|
||||
3. Layer follow-up assertions once pqxx-backed code exists (e.g., detect `HAVE_PG` and connect to real DB in CI).
|
||||
3. Layer follow-up assertions once the QSql path is exercised end-to-end (CI can target the packaged test database).
|
||||
|
||||
## 2. DAL Prepared Statements (`HAVE_PG`)
|
||||
## 2. DAL (Qt6/QSql) Evolution
|
||||
**Dependencies**
|
||||
- System packages: `libpq` headers and `libpqxx` (>= 7.8).
|
||||
- CMake: `find_package(PostgreSQL REQUIRED)` and `find_package(pqxx REQUIRED)`; link `PostgreSQL::PostgreSQL` and `pqxx::pqxx`.
|
||||
- Qt6 (Core, Sql) with the `QPSQL` driver available at runtime.
|
||||
- KDE Frameworks `ConfigCore` for persisting DSNs in `kompanionrc`.
|
||||
|
||||
**Implementation Steps**
|
||||
1. Extend `src/dal/CMakeLists.txt` to define `HAVE_PG` and link the libraries when detection succeeds.
|
||||
2. Enhance `PgDal` to manage both modes:
|
||||
- `stub://` DSN → in-memory containers (status quo).
|
||||
- Other DSNs → establish `pqxx::connection`, store it via `std::unique_ptr`, and call `conn->prepare(...)` during `connect`.
|
||||
3. Prepare statements for: `ensure_namespace`, `upsert_item`, `insert_chunk`, `insert_embedding`, `search_text`, `search_vector`, `hybrid_search`.
|
||||
4. Implement transactional helpers using `pqxx::work`, with RAII to guarantee `commit()`/`abort()` pairing.
|
||||
5. Translate `pqxx` exceptions into `std::runtime_error` with context so MCP handlers can emit useful error JSON.
|
||||
6. Document required environment variables (`PG_DSN`, optional `PGSSLMODE`) plus migration expectations in `docs/dal-skeleton.md`.
|
||||
1. Parse libpq-style DSNs with `QUrl`, open `QSqlDatabase` connections when the DSN is not `stub://`, and maintain the existing in-memory fallback for tests.
|
||||
2. Use `QSqlQuery` `INSERT ... RETURNING` statements for namespaces, items, chunks, and embeddings; emit vector literals (`[0.1,0.2]`) when targeting pgvector columns.
|
||||
3. Surface detailed `QSqlError` messages (throwing `std::runtime_error`) so MCP handlers and the CLI can report actionable failures.
|
||||
4. Share configuration between CLI and MCP runners via KConfig (`Database/PgDsn`), seeded through the new `kompanion --init` wizard.
|
||||
|
||||
## 3. MCP `resources/*` & Episodic→Semantic Sync
|
||||
**Directory Layout**
|
||||
|
|
@ -60,10 +56,10 @@
|
|||
3. Merge results in C++ with Reciprocal Rank Fusion or weighted sum, ensuring deterministic ordering on ties.
|
||||
|
||||
**Handler Integration**
|
||||
1. Extend `PgDal::hybridSearch` to dispatch to the prepared statements when `HAVE_PG` is defined; reuse in-memory fallback otherwise.
|
||||
1. Ensure `PgDal::hybridSearch` delegates to SQL-based lexical/vector search when a database connection is active, reusing the in-memory fallback only for `stub://`.
|
||||
2. Return richer matches (id, score, optional chunk text) to satisfy MCP response schema.
|
||||
3. Update `HandlersMemory::search_memory` to surface the new scores and annotate whether lexical/vector contributed (optional metadata).
|
||||
4. Add a contract test scenario once pqxx-backed execution is available (requires live Postgres fixture later).
|
||||
4. Exercise hybrid queries in contract tests against the packaged test database (`db/scripts/create-test-db.sh`).
|
||||
|
||||
## 5. Secret Handling, Snapshots, and CLI Hooks
|
||||
- **Secret propagation**: episodic `sensitivity` + `embeddable` flags gate embedding generation. DAL queries will add predicates (`metadata->>'sensitivity' != 'secret'`) before hybrid search.
|
||||
|
|
@ -76,9 +72,9 @@
|
|||
- **CLI UX**: `kompanion --init` guides first-run setup (auto-detects databases, applies schemas); `-I/--interactive` keeps a JSON REPL open, and `-V/--verbose` echoes request/response streams for future HTTP transport parity.
|
||||
|
||||
## Next-Step Checklist
|
||||
- [x] Detect pqxx via CMake and plumb `HAVE_PG`.
|
||||
- [x] Promote Qt6/QSql backend (QPSQL) as default DAL; retain `stub://` fallback for tests.
|
||||
- [x] Normalize contract_memory CTest target and remove stale library target.
|
||||
- [ ] Author `resources/memory/` descriptors and sync job outline.
|
||||
- [ ] Extend DAL header to carry prepared-statement aware APIs (may introduce new structs).
|
||||
- [ ] Extend DAL header to expose richer query structs (filters, pagination, secret handling).
|
||||
- [x] Update `docs/mcp-memory-api.md` to mention episodic sync + hybrid search fields.
|
||||
- [ ] Create follow-up acf subtasks when concrete implementation begins (pgvector migration, scheduler hook, runtime wiring).
|
||||
|
|
|
|||
|
|
@ -4,8 +4,5 @@ add_library(kom_dal STATIC
|
|||
|
||||
target_compile_features(kom_dal PUBLIC cxx_std_20)
|
||||
target_include_directories(kom_dal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if (KOM_HAVE_PG)
|
||||
target_compile_definitions(kom_dal PUBLIC HAVE_PG)
|
||||
target_link_libraries(kom_dal PUBLIC pqxx::pqxx PostgreSQL::PostgreSQL)
|
||||
endif()
|
||||
target_link_libraries(kom_dal PUBLIC Qt6::Core Qt6::Sql)
|
||||
target_compile_options(kom_dal PRIVATE -fexceptions)
|
||||
|
|
|
|||
1273
src/dal/PgDal.cpp
1273
src/dal/PgDal.cpp
File diff suppressed because it is too large
Load Diff
|
|
@ -2,19 +2,16 @@
|
|||
|
||||
#include "IDatabase.hpp"
|
||||
|
||||
#include <QSqlDatabase>
|
||||
#include <QString>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#ifdef HAVE_PG
|
||||
#include <pqxx/pqxx>
|
||||
#endif
|
||||
|
||||
namespace kom {
|
||||
|
||||
struct NamespaceRow {
|
||||
|
|
@ -27,11 +24,11 @@ struct ItemRow {
|
|||
std::string namespace_id;
|
||||
std::optional<std::string> key;
|
||||
std::string content_json;
|
||||
std::string metadata_json = "{}";
|
||||
std::optional<std::string> text;
|
||||
std::string metadata_json;
|
||||
std::vector<std::string> tags;
|
||||
int revision = 1;
|
||||
std::chrono::system_clock::time_point created_at{};
|
||||
std::chrono::system_clock::time_point created_at;
|
||||
std::optional<std::chrono::system_clock::time_point> expires_at;
|
||||
};
|
||||
|
||||
|
|
@ -79,9 +76,9 @@ public:
|
|||
const std::optional<std::string>& key,
|
||||
const std::vector<std::string>& tags,
|
||||
const std::optional<std::string>& sinceIso,
|
||||
int limit) const;
|
||||
int limit);
|
||||
|
||||
// IDatabase overrides (stubbed for now to operate on the in-memory backing store).
|
||||
// IDatabase overrides
|
||||
std::pair<std::string, int> upsertItem(
|
||||
const std::string& namespace_id,
|
||||
const std::optional<std::string>& key,
|
||||
|
|
@ -99,40 +96,61 @@ public:
|
|||
int k) override;
|
||||
|
||||
private:
|
||||
std::string allocateId(std::size_t& counter, const std::string& prefix);
|
||||
static std::string toLower(const std::string& value);
|
||||
static std::string toIso8601(std::chrono::system_clock::time_point tp);
|
||||
static bool includesAllTags(const std::vector<std::string>& haystack,
|
||||
const std::vector<std::string>& needles);
|
||||
bool isExpired(const ItemRow& row,
|
||||
std::chrono::system_clock::time_point now) const;
|
||||
void resetInMemoryStore();
|
||||
static std::string toPgArrayLiteral(const std::vector<std::string>& values);
|
||||
static std::string escapePgArrayElement(const std::string& value);
|
||||
static std::string toPgVectorLiteral(const std::vector<float>& values);
|
||||
#ifdef HAVE_PG
|
||||
void prepareStatements();
|
||||
NamespaceRow mapNamespaceRow(const pqxx::row& row) const;
|
||||
ItemRow mapItemRow(const pqxx::row& row) const;
|
||||
std::vector<std::string> parseTextArrayField(const pqxx::field& field) const;
|
||||
std::optional<NamespaceRow> pgEnsureNamespace(const std::string& name);
|
||||
std::optional<NamespaceRow> pgFindNamespace(const std::string& name) const;
|
||||
std::pair<std::string, int> pgUpsertItem(const ItemRow& row);
|
||||
std::vector<std::string> pgUpsertChunks(const std::vector<ChunkRow>& chunks);
|
||||
void pgUpsertEmbeddings(const std::vector<EmbeddingRow>& embeddings);
|
||||
std::vector<ItemRow> pgSearchText(const std::string& namespaceId,
|
||||
const std::string& query,
|
||||
int limit);
|
||||
std::vector<std::pair<std::string, float>> pgSearchVector(
|
||||
struct ConnectionConfig {
|
||||
QString host;
|
||||
int port = 5432;
|
||||
QString dbname;
|
||||
QString user;
|
||||
QString password;
|
||||
bool useSocket = false;
|
||||
QString socketPath;
|
||||
QString options;
|
||||
};
|
||||
|
||||
bool hasDatabase() const;
|
||||
bool openDatabase(const std::string& dsn);
|
||||
void closeDatabase();
|
||||
QSqlDatabase database() const;
|
||||
ConnectionConfig parseDsn(const std::string& dsn) const;
|
||||
|
||||
NamespaceRow sqlEnsureNamespace(const std::string& name);
|
||||
std::optional<NamespaceRow> sqlFindNamespace(const std::string& name) const;
|
||||
std::pair<std::string, int> sqlUpsertItem(const ItemRow& row);
|
||||
std::vector<std::string> sqlUpsertChunks(const std::vector<ChunkRow>& chunks);
|
||||
void sqlUpsertEmbeddings(const std::vector<EmbeddingRow>& embeddings);
|
||||
std::vector<ItemRow> sqlSearchText(const std::string& namespaceId,
|
||||
const std::string& query,
|
||||
int limit) const;
|
||||
std::vector<std::pair<std::string, float>> sqlSearchVector(
|
||||
const std::string& namespaceId,
|
||||
const std::vector<float>& embedding,
|
||||
int limit);
|
||||
std::optional<ItemRow> pgGetItemById(const std::string& id) const;
|
||||
#endif
|
||||
int limit) const;
|
||||
std::optional<ItemRow> sqlGetItemById(const std::string& id) const;
|
||||
std::vector<ItemRow> sqlFetchContext(const std::string& namespaceId,
|
||||
const std::optional<std::string>& key,
|
||||
const std::vector<std::string>& tags,
|
||||
const std::optional<std::string>& sinceIso,
|
||||
int limit) const;
|
||||
|
||||
std::vector<std::string> sqlHybridSearch(const std::vector<float>& query_vec,
|
||||
const std::string& model,
|
||||
const std::string& namespace_id,
|
||||
const std::string& query_text,
|
||||
int k);
|
||||
|
||||
std::string allocateId(std::size_t& counter, const std::string& prefix);
|
||||
static std::string toLower(const std::string& value);
|
||||
static bool isStubDsn(const std::string& dsn);
|
||||
static std::string escapePgArrayElement(const std::string& value);
|
||||
static std::string toPgArrayLiteral(const std::vector<std::string>& values);
|
||||
static std::string toPgVectorLiteral(const std::vector<float>& values);
|
||||
static std::vector<std::string> parsePgTextArray(const QString& value);
|
||||
|
||||
bool connected_ = false;
|
||||
bool useInMemory_ = true;
|
||||
std::string dsn_;
|
||||
QString connectionName_;
|
||||
bool transactionActive_ = false;
|
||||
|
||||
std::size_t nextNamespaceId_ = 1;
|
||||
std::size_t nextItemId_ = 1;
|
||||
|
|
@ -146,12 +164,6 @@ private:
|
|||
std::unordered_map<std::string, ChunkRow> chunks_;
|
||||
std::unordered_map<std::string, std::vector<std::string>> chunksByItem_;
|
||||
std::unordered_map<std::string, EmbeddingRow> embeddings_;
|
||||
|
||||
#ifdef HAVE_PG
|
||||
std::unique_ptr<pqxx::connection> connection_;
|
||||
mutable std::unique_ptr<pqxx::work> activeTx_;
|
||||
bool statementsPrepared_ = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace kom
|
||||
|
|
|
|||
19
src/main.cpp
19
src/main.cpp
|
|
@ -10,6 +10,10 @@
|
|||
#ifdef HAVE_KCONFIG
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
#else
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
#endif
|
||||
|
||||
#include "mcp/KomMcpServer.hpp"
|
||||
|
|
@ -62,8 +66,21 @@ std::optional<std::string> read_dsn_from_config() {
|
|||
return dsn.toStdString();
|
||||
}
|
||||
#else
|
||||
QString configFilePath() {
|
||||
QString base = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
||||
if (base.isEmpty()) {
|
||||
base = QDir::homePath();
|
||||
}
|
||||
return QDir(base).filePath(QStringLiteral("kompanionrc"));
|
||||
}
|
||||
|
||||
std::optional<std::string> read_dsn_from_config() {
|
||||
return std::nullopt;
|
||||
QSettings settings(configFilePath(), QSettings::IniFormat);
|
||||
const QString dsn = settings.value(QStringLiteral("Database/PgDsn")).toString();
|
||||
if (dsn.isEmpty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return dsn.toStdString();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
|
|||
| #18 | ⬜ todo | 492 | **Expose Agentic-Control-Framework as a tool** | Wrap ACF endpoints into a too... |
|
||||
| #19 | ⬜ todo | 509 | **DAL skeleton + SQL calls (pgvector)** | Create DAL interfaces and pgv... |
|
||||
| #20 | ⬜ todo | 491 | **Claude Code integration rescue plan** | Stabilize Qwen-2.5-Coder insi... |
|
||||
| #21 | ⬜ todo | 510 | **DAL Phase 1: libpq/pqxx wiring + SQL calls** | Link pqxx, implement PgDal ag... |
|
||||
| #21 | ⬜ todo | 510 | **DAL Phase 1: Qt6/QSql wiring + SQL calls** | Use QPSQL via Qt6::Sql, implement PgDal ag... |
|
||||
| #22 | ⬜ todo | 490 | **Handlers → DAL integration** | Wire kom.memory.v1.upsert_mem... |
|
||||
| #23 | ⬜ todo | 511 | **Contract tests: DAL-backed tools** | Expand CTest to cover DAL-bac... |
|
||||
|
||||
|
|
@ -48,8 +48,8 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
|
|||
|
||||
| ID | Status | Title |
|
||||
|:--:|:------:|:------|
|
||||
| #21.1 | ⬜ todo | CMake: find_package(pqxx) and link; CI env var DSN |
|
||||
| #21.2 | ⬜ todo | PgDal: implement connect/tx + prepared statements |
|
||||
| #21.1 | ⬜ todo | CMake: require Qt6::Sql (QPSQL); CI env var DSN |
|
||||
| #21.2 | ⬜ todo | PgDal: implement QSql connect/tx + prepared statements |
|
||||
| #21.3 | ⬜ todo | SQL: ensureNamespace, upsertItem/Chunks/Embeddings |
|
||||
| #21.4 | ⬜ todo | Search: FTS/trgm + vector <-> with filters (namespace/thread/tags) |
|
||||
|
||||
|
|
@ -60,4 +60,3 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
|
|||
| #22.1 | ⬜ todo | Replace ad-hoc JSON with parser (nlohmann/json or simdjson) |
|
||||
| #22.2 | ⬜ todo | Validate request bodies against schemas before DAL calls |
|
||||
| #22.3 | ⬜ todo | Scope & sensitivity enforcement (namespace/user + skip secret embeddings) |
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ add_executable(test_mcp_tools
|
|||
)
|
||||
target_include_directories(test_mcp_tools PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
||||
target_link_libraries(test_mcp_tools PRIVATE kom_dal)
|
||||
target_compile_options(test_mcp_tools PRIVATE -fexceptions)
|
||||
|
||||
add_test(NAME contract_mcp_tools COMMAND test_mcp_tools)
|
||||
|
||||
|
|
@ -11,6 +12,7 @@ add_executable(contract_memory
|
|||
)
|
||||
target_include_directories(contract_memory PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
||||
target_link_libraries(contract_memory PRIVATE kom_dal)
|
||||
target_compile_options(contract_memory PRIVATE -fexceptions)
|
||||
|
||||
add_test(NAME contract_memory COMMAND contract_memory)
|
||||
|
||||
|
|
@ -19,5 +21,6 @@ add_executable(test_memory_exchange
|
|||
)
|
||||
target_include_directories(test_memory_exchange PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
||||
target_link_libraries(test_memory_exchange PRIVATE kom_dal)
|
||||
target_compile_options(test_memory_exchange PRIVATE -fexceptions)
|
||||
|
||||
add_test(NAME mcp_memory_exchange COMMAND test_memory_exchange)
|
||||
|
|
|
|||
Loading…
Reference in New Issue