Add testsystem
This commit is contained in:
parent
2210e3a260
commit
5ae22ff9f8
165
.acf/tasks.json
165
.acf/tasks.json
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"projectName": "metal-kompanion-mcp",
|
"projectName": "metal-kompanion-mcp",
|
||||||
"projectDescription": "MCP backend for Kompanion: memory/context/embedding provider over MCP, built from scratch (qtmcp-based) to persist conversation state and serve embeddings + retrieval to avoid forgetting across threads.",
|
"projectDescription": "MCP backend for Kompanion: memory/context/embedding provider over MCP, built from scratch (qtmcp-based) to persist conversation state and serve embeddings + retrieval to avoid forgetting across threads.",
|
||||||
"lastTaskId": 20,
|
"lastTaskId": 23,
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
|
@ -441,6 +441,169 @@
|
||||||
"message": "Task created with title: \"Claude Code integration rescue plan\""
|
"message": "Task created with title: \"Claude Code integration rescue plan\""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"title": "DAL Phase 1: libpq/pqxx wiring + SQL calls",
|
||||||
|
"description": "Link pqxx, implement PgDal against Postgres+pgvector: connect/tx, ensureNamespace, upsertItem/Chunks/Embeddings, searchText (FTS/trgm), searchVector (<->). Provide DSN via env; add cmake find + link.",
|
||||||
|
"status": "todo",
|
||||||
|
"priority": 510,
|
||||||
|
"priorityDisplay": "P0",
|
||||||
|
"dependsOn": [],
|
||||||
|
"createdAt": "2025-10-14T00:29:55.327Z",
|
||||||
|
"updatedAt": "2025-10-14T00:29:55.327Z",
|
||||||
|
"subtasks": [
|
||||||
|
{
|
||||||
|
"id": "21.1",
|
||||||
|
"title": "CMake: find_package(pqxx) and link; CI env var DSN",
|
||||||
|
"status": "todo",
|
||||||
|
"createdAt": "2025-10-14T00:30:00.856Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:00.857Z",
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:00.856Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Subtask created with title: \"CMake: find_package(pqxx) and link; CI env var DSN\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21.2",
|
||||||
|
"title": "PgDal: implement connect/tx + prepared statements",
|
||||||
|
"status": "todo",
|
||||||
|
"createdAt": "2025-10-14T00:30:06.138Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:06.138Z",
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:06.138Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Subtask created with title: \"PgDal: implement connect/tx + prepared statements\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21.3",
|
||||||
|
"title": "SQL: ensureNamespace, upsertItem/Chunks/Embeddings",
|
||||||
|
"status": "todo",
|
||||||
|
"createdAt": "2025-10-14T00:30:11.519Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:11.519Z",
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:11.519Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Subtask created with title: \"SQL: ensureNamespace, upsertItem/Chunks/Embeddings\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "21.4",
|
||||||
|
"title": "Search: FTS/trgm + vector <-> with filters (namespace/thread/tags)",
|
||||||
|
"status": "todo",
|
||||||
|
"createdAt": "2025-10-14T00:30:17.290Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:17.290Z",
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:17.290Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Subtask created with title: \"Search: FTS/trgm + vector <-> with filters (namespace/thread/tags)\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastSubtaskIndex": 4,
|
||||||
|
"relatedFiles": [],
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:29:55.327Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Task created with title: \"DAL Phase 1: libpq/pqxx wiring + SQL calls\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"title": "Handlers → DAL integration",
|
||||||
|
"description": "Wire kom.memory.v1.upsert_memory/search_memory to IDatabase. Parse JSON with a real parser, validate against schemas, enforce scope and sensitivity rules.",
|
||||||
|
"status": "todo",
|
||||||
|
"priority": 490,
|
||||||
|
"priorityDisplay": "P0",
|
||||||
|
"dependsOn": [],
|
||||||
|
"createdAt": "2025-10-14T00:30:26.285Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:26.285Z",
|
||||||
|
"subtasks": [
|
||||||
|
{
|
||||||
|
"id": "22.1",
|
||||||
|
"title": "Replace ad-hoc JSON with parser (nlohmann/json or simdjson)",
|
||||||
|
"status": "todo",
|
||||||
|
"createdAt": "2025-10-14T00:30:33.761Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:33.761Z",
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:33.761Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Subtask created with title: \"Replace ad-hoc JSON with parser (nlohmann/json or simdjson)\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "22.2",
|
||||||
|
"title": "Validate request bodies against schemas before DAL calls",
|
||||||
|
"status": "todo",
|
||||||
|
"createdAt": "2025-10-14T00:30:39.868Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:39.868Z",
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:39.868Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Subtask created with title: \"Validate request bodies against schemas before DAL calls\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "22.3",
|
||||||
|
"title": "Scope & sensitivity enforcement (namespace/user + skip secret embeddings)",
|
||||||
|
"status": "todo",
|
||||||
|
"createdAt": "2025-10-14T00:30:45.261Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:45.261Z",
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:45.261Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Subtask created with title: \"Scope & sensitivity enforcement (namespace/user + skip secret embeddings)\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastSubtaskIndex": 3,
|
||||||
|
"relatedFiles": [],
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:26.285Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Task created with title: \"Handlers → DAL integration\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"title": "Contract tests: DAL-backed tools",
|
||||||
|
"description": "Expand CTest to cover DAL-backed upsert/search and backup export/import; include error cases and schema violations; run against build-komhands.",
|
||||||
|
"status": "todo",
|
||||||
|
"priority": 511,
|
||||||
|
"priorityDisplay": "P1",
|
||||||
|
"dependsOn": [],
|
||||||
|
"createdAt": "2025-10-14T00:30:51.716Z",
|
||||||
|
"updatedAt": "2025-10-14T00:30:51.716Z",
|
||||||
|
"subtasks": [],
|
||||||
|
"lastSubtaskIndex": 0,
|
||||||
|
"relatedFiles": [],
|
||||||
|
"activityLog": [
|
||||||
|
{
|
||||||
|
"timestamp": "2025-10-14T00:30:51.716Z",
|
||||||
|
"type": "log",
|
||||||
|
"message": "Task created with title: \"Contract tests: DAL-backed tools\""
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -8,25 +8,18 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
# find_package(Qt6 COMPONENTS Core Network REQUIRED)
|
# find_package(Qt6 COMPONENTS Core Network REQUIRED)
|
||||||
# find_package(qtmcp REQUIRED)
|
# find_package(qtmcp REQUIRED)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
add_subdirectory(src/dal)
|
||||||
pkg_check_modules(PQXX REQUIRED libpqxx)
|
|
||||||
|
|
||||||
add_executable(kom_mcp
|
add_executable(kom_mcp
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/dal/PgDal.cpp
|
|
||||||
)
|
)
|
||||||
# target_link_libraries(kom_mcp PRIVATE Qt6::Core Qt6::Network qtmcp)
|
target_include_directories(kom_mcp PRIVATE src)
|
||||||
target_include_directories(kom_mcp PRIVATE src ${PQXX_INCLUDE_DIRS})
|
target_link_libraries(kom_mcp PRIVATE kom_dal)
|
||||||
target_link_libraries(kom_mcp PRIVATE ${PQXX_LIBRARIES})
|
|
||||||
|
|
||||||
install(TARGETS kom_mcp RUNTIME DESTINATION bin)
|
install(TARGETS kom_mcp RUNTIME DESTINATION bin)
|
||||||
|
|
||||||
add_executable(test_mcp_tools
|
option(BUILD_TESTS "Build tests" ON)
|
||||||
tests/contract/test_mcp_tools.cpp
|
if (BUILD_TESTS)
|
||||||
src/dal/PgDal.cpp
|
enable_testing()
|
||||||
)
|
add_subdirectory(tests)
|
||||||
target_include_directories(test_mcp_tools PRIVATE src ${PQXX_INCLUDE_DIRS})
|
endif()
|
||||||
target_link_libraries(test_mcp_tools PRIVATE ${PQXX_LIBRARIES})
|
|
||||||
|
|
||||||
enable_testing()
|
|
||||||
add_test(NAME contract_mcp_tools COMMAND test_mcp_tools)
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
-- Kompanion quick checks
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
-- placeholder: see chat for full Branch Embeddings schema
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
add_library(kom_dal STATIC
|
||||||
|
PgDal.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_features(kom_dal PUBLIC cxx_std_20)
|
||||||
|
target_include_directories(kom_dal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
@ -3,11 +3,11 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
|
||||||
|
|
||||||
> ## 📈 Project Summary
|
> ## 📈 Project Summary
|
||||||
>
|
>
|
||||||
> **✅ Done**: 0 | **🔄 In Progress**: 0 | **⬜ Todo**: 19 | **❌ Blocked**: 0
|
> **✅ Done**: 0 | **🔄 In Progress**: 0 | **⬜ Todo**: 29 | **❌ Blocked**: 0
|
||||||
>
|
>
|
||||||
> **Progress**: 0% `░░░░░░░░░░░░░░░░░░░░` 0/19 tasks
|
> **Progress**: 0% `░░░░░░░░░░░░░░░░░░░░` 0/29 tasks
|
||||||
>
|
>
|
||||||
> **Priorities**: 🚨 **Critical**: 0 | 🔴 **High**: 1 | 🟡 **Medium**: 20 | 🟢 **Low**: 0
|
> **Priorities**: 🚨 **Critical**: 0 | 🔴 **High**: 1 | 🟡 **Medium**: 30 | 🟢 **Low**: 0
|
||||||
|
|
||||||
## Tasks
|
## Tasks
|
||||||
|
|
||||||
|
|
@ -33,6 +33,9 @@ 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... |
|
| #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... |
|
| #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... |
|
| #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... |
|
||||||
|
| #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... |
|
||||||
|
|
||||||
|
|
||||||
### Task #2: Design MCP memory/context API - Subtasks
|
### Task #2: Design MCP memory/context API - Subtasks
|
||||||
|
|
@ -41,3 +44,20 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
|
||||||
|:--:|:------:|:------|
|
|:--:|:------:|:------|
|
||||||
| #2.1 | ⬜ todo | Write JSON Schemas for tools (done) |
|
| #2.1 | ⬜ todo | Write JSON Schemas for tools (done) |
|
||||||
|
|
||||||
|
### Task #21: DAL Phase 1: libpq/pqxx wiring + SQL calls - Subtasks
|
||||||
|
|
||||||
|
| 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.3 | ⬜ todo | SQL: ensureNamespace, upsertItem/Chunks/Embeddings |
|
||||||
|
| #21.4 | ⬜ todo | Search: FTS/trgm + vector <-> with filters (namespace/thread/tags) |
|
||||||
|
|
||||||
|
### Task #22: Handlers → DAL integration - Subtasks
|
||||||
|
|
||||||
|
| ID | Status | Title |
|
||||||
|
|:--:|:------:|:------|
|
||||||
|
| #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) |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
add_executable(test_mcp_tools
|
||||||
|
contract/test_mcp_tools.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(test_mcp_tools PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(test_mcp_tools PRIVATE kom_dal)
|
||||||
|
|
||||||
|
add_test(NAME contract_mcp_tools COMMAND test_mcp_tools)
|
||||||
|
|
||||||
|
add_library(contract_memory STATIC
|
||||||
|
contract_memory.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(contract_memory PRIVATE ${PROJECT_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(contract_memory PRIVATE kom_dal)
|
||||||
|
|
@ -20,15 +20,34 @@ int main() {
|
||||||
KomMcpServer server;
|
KomMcpServer server;
|
||||||
register_default_tools(server);
|
register_default_tools(server);
|
||||||
|
|
||||||
const std::string upsertReq = R"({"namespace":"tests","items":[{"text":"hello world"},{"text":"hola mundo"}]})";
|
const std::string upsertReq = R"({"namespace":"tests","items":[{"text":"hello world","tags":["greeting"],"embedding":[0.1,0.2,0.3]},{"text":"hola mundo","embedding":[0.05,0.1,0.15]}]})";
|
||||||
std::string upsertResp = server.dispatch("kom.memory.v1.upsert_memory", upsertReq);
|
std::string upsertResp = server.dispatch("kom.memory.v1.upsert_memory", upsertReq);
|
||||||
if (!expect_contains(upsertResp, "\"upserted\":2", "upsert_memory count")) return 1;
|
if (!expect_contains(upsertResp, "\"upserted\":2", "upsert_memory count")) return 1;
|
||||||
if (!expect_contains(upsertResp, "\"status\":\"ok\"", "upsert_memory status")) return 1;
|
if (!expect_contains(upsertResp, "\"status\":\"ok\"", "upsert_memory status")) return 1;
|
||||||
|
|
||||||
|
std::string firstId;
|
||||||
|
const std::string idsAnchor = "\"ids\":[\"";
|
||||||
|
auto idsPos = upsertResp.find(idsAnchor);
|
||||||
|
if (idsPos != std::string::npos) {
|
||||||
|
idsPos += idsAnchor.size();
|
||||||
|
auto endPos = upsertResp.find("\"", idsPos);
|
||||||
|
if (endPos != std::string::npos) {
|
||||||
|
firstId = upsertResp.substr(idsPos, endPos - idsPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (firstId.empty()) {
|
||||||
|
std::cerr << "Failed to extract first item id from upsert response: " << upsertResp << "\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string searchReq = R"({"namespace":"tests","query":{"text":"hello","k":3}})";
|
const std::string searchReq = R"({"namespace":"tests","query":{"text":"hello","k":3}})";
|
||||||
std::string searchResp = server.dispatch("kom.memory.v1.search_memory", searchReq);
|
std::string searchResp = server.dispatch("kom.memory.v1.search_memory", searchReq);
|
||||||
if (!expect_contains(searchResp, "\"matches\"", "search_memory matches key")) return 1;
|
if (!expect_contains(searchResp, "\"matches\"", "search_memory matches key")) return 1;
|
||||||
if (!expect_contains(searchResp, "\"text\":\"hello\"", "search_memory echo text")) return 1;
|
if (!expect_contains(searchResp, "\"text\":\"hello world\"", "search_memory returns stored text")) return 1;
|
||||||
|
|
||||||
|
const std::string vectorReq = R"({"namespace":"tests","query":{"embedding":[0.1,0.2,0.3],"k":1}})";
|
||||||
|
std::string vectorResp = server.dispatch("kom.memory.v1.search_memory", vectorReq);
|
||||||
|
if (!expect_contains(vectorResp, "\"id\":\""+firstId+"\"", "vector search returns stored id")) return 1;
|
||||||
|
|
||||||
const std::string exportReq = R"({"namespace":"tests","destination":"/tmp/example.enc"})";
|
const std::string exportReq = R"({"namespace":"tests","destination":"/tmp/example.enc"})";
|
||||||
std::string exportResp = server.dispatch("kom.local.v1.backup.export_encrypted", exportReq);
|
std::string exportResp = server.dispatch("kom.local.v1.backup.export_encrypted", exportReq);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include "dal/PgDal.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static void contract_pgdal_basic() {
|
||||||
|
kom::PgDal dal;
|
||||||
|
dal.connect("stub://memory");
|
||||||
|
auto ns = dal.ensureNamespace("tests");
|
||||||
|
static_cast<void>(ns);
|
||||||
|
|
||||||
|
kom::ItemRow item;
|
||||||
|
item.namespace_id = "tests";
|
||||||
|
item.text = std::string("example");
|
||||||
|
item.tags = {"alpha", "beta"};
|
||||||
|
item.id = dal.upsertItem(item);
|
||||||
|
|
||||||
|
kom::ChunkRow chunk;
|
||||||
|
chunk.item_id = item.id;
|
||||||
|
chunk.text = "chunk-text";
|
||||||
|
auto chunkIds = dal.upsertChunks(std::vector<kom::ChunkRow>{chunk});
|
||||||
|
if (!chunkIds.empty()) {
|
||||||
|
chunk.id = chunkIds.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
kom::EmbeddingRow embedding;
|
||||||
|
embedding.chunk_id = chunk.id;
|
||||||
|
embedding.model = "stub-model";
|
||||||
|
embedding.dim = 3;
|
||||||
|
embedding.vector = {0.1f, 0.2f, 0.3f};
|
||||||
|
dal.upsertEmbeddings(std::vector<kom::EmbeddingRow>{embedding});
|
||||||
|
|
||||||
|
static_cast<void>(dal.searchText("tests", "chunk", 5));
|
||||||
|
static_cast<void>(dal.searchVector("tests", embedding.vector, 5));
|
||||||
|
static_cast<void>(dal.getItemById(item.id));
|
||||||
|
static_cast<void>(dal.hybridSearch(embedding.vector, "stub-model", "tests", "chunk", 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const bool contract_pgdal_compiles = [] {
|
||||||
|
contract_pgdal_basic();
|
||||||
|
return true;
|
||||||
|
}();
|
||||||
Loading…
Reference in New Issue