diff --git a/sessions/session-2026-02-10.md b/sessions/session-2026-02-10.md deleted file mode 100644 index b0f54bd..0000000 --- a/sessions/session-2026-02-10.md +++ /dev/null @@ -1,70 +0,0 @@ -# Session Summary - 2026-02-10 - -## Project: sqlite-memory - -SQLite extension for AI agent memory with semantic search, hybrid retrieval, and offline-first sync between agents. - ---- - -## Work Completed - -### 1. Documentation Updates - -Updated `README.md` and `API.md` to reflect changes to the `memory_search` virtual table: - -- **Renamed column**: `score` → `ranking` in all query examples and documentation -- **Documented columns**: `path`, `snippet`, `ranking` properly described -- **Preserved settings**: `min_score` setting name unchanged (configuration option, not column) - -### 2. GitHub Project Description - -Created project descriptions for GitHub: - -**Short (About field):** -> SQLite extension for AI agent memory with semantic search, hybrid retrieval, and offline-first sync between agents - -**Full description:** -> A SQLite extension that gives AI agents persistent, searchable memory. Features hybrid semantic search (vector similarity + FTS5), markdown-aware chunking, and local embedding via llama.cpp. Memory databases can be synced between agents using offline-first technology—each agent works independently and syncs when connected, making it ideal for distributed AI systems, edge deployments, and collaborative agent architectures. - ---- - -## Key Files Modified - -| File | Changes | -|------|---------| -| `README.md` | Updated `memory_search` examples to use `ranking` column | -| `API.md` | Updated column documentation and examples for `memory_search` | - ---- - -## memory_search Virtual Table Schema - -```sql -SELECT * FROM memory_search WHERE query = 'search text'; -``` - -| Column | Type | Description | -|--------|------|-------------| -| `query` | TEXT (HIDDEN) | Search query (required in WHERE clause) | -| `hash` | INTEGER | Content hash identifier | -| `path` | TEXT | Source file path or generated UUID | -| `context` | TEXT | Context label (NULL if not set) | -| `snippet` | TEXT | Text snippet from matching chunk | -| `ranking` | REAL | Combined similarity score (0.0 - 1.0) | - ---- - -## Previous Session Context - -This session continued from earlier work that included: -- Implementing memory deletion, timestamps, and statistics features -- Building Makefile with conditional llama.cpp/remote engine support -- Adding support for both local (llama.cpp) and remote (vector.space) embedding -- Renaming `max_items` to `max_results` throughout codebase -- Creating comprehensive README.md and API.md documentation - ---- - -## Version - -sqlite-memory v0.5.1 diff --git a/src/sqlite-memory.c b/src/sqlite-memory.c index 4356404..823eba0 100644 --- a/src/sqlite-memory.c +++ b/src/sqlite-memory.c @@ -772,7 +772,7 @@ bool dbmem_context_load_vector (dbmem_context *ctx) { } if (ctx->dimension == 0) { - dbmem_context_set_error(ctx, "SQLite-vector extension cannot be loaded because embedding dimension is not specified"); + dbmem_context_set_error(ctx, "memory_search cannot run because no content has been indexed yet. Add content with memory_add_text(), memory_add_file(), or memory_add_directory() before searching."); return false; } @@ -1573,14 +1573,27 @@ static int dbmem_process_callback (const char *text, size_t len, size_t offset, } if (!cache_hit) { + if (!ctx->provider || !ctx->model) { + dbmem_context_set_error(ctx, "memory_set_model must be called before adding content"); + return SQLITE_ERROR; + } + // compute embedding if (ctx->is_custom) { + if (!ctx->custom_engine || !ctx->custom_provider.compute) { + dbmem_context_set_error(ctx, "memory_set_model must be called before adding content"); + return SQLITE_ERROR; + } rc = dbmem_context_custom_compute(ctx, text, (int)len, &result); if (rc != 0) return rc; } else if (ctx->is_local) { #ifndef DBMEM_OMIT_LOCAL_ENGINE + if (!ctx->l_engine) { + dbmem_context_set_error(ctx, "memory_set_model must be called before adding content"); + return SQLITE_ERROR; + } rc = dbmem_local_compute_embedding(ctx->l_engine, text, (int)len, &result); if (rc != 0) return rc; #else @@ -1591,6 +1604,10 @@ static int dbmem_process_callback (const char *text, size_t len, size_t offset, else { #ifndef DBMEM_OMIT_REMOTE_ENGINE + if (!ctx->r_engine) { + dbmem_context_set_error(ctx, "memory_set_model must be called before adding content"); + return SQLITE_ERROR; + } rc = dbmem_remote_compute_embedding(ctx->r_engine, text, (int)len, &result); if (rc != 0) return rc; #else diff --git a/src/sqlite-memory.h b/src/sqlite-memory.h index 0ee29bc..7a057a3 100644 --- a/src/sqlite-memory.h +++ b/src/sqlite-memory.h @@ -26,7 +26,7 @@ extern "C" { #endif -#define SQLITE_DBMEMORY_VERSION "1.2.1" +#define SQLITE_DBMEMORY_VERSION "1.2.2" // public API SQLITE_DBMEMORY_API int sqlite3_memory_init (sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi); diff --git a/test/unittest.c b/test/unittest.c index 6f17745..28889de 100644 --- a/test/unittest.c +++ b/test/unittest.c @@ -2730,6 +2730,21 @@ TEST(sqlite_custom_provider_set_model) { sqlite3_close(db); } +TEST(sqlite_memory_add_text_requires_model) { + sqlite3 *db = open_test_db(); + ASSERT(db != NULL); + + sqlite3_stmt *stmt = NULL; + int rc = sqlite3_prepare_v2(db, "SELECT memory_add_text('Hello world, this is a test.');", -1, &stmt, NULL); + ASSERT_EQ(rc, SQLITE_OK); + rc = sqlite3_step(stmt); + ASSERT_EQ(rc, SQLITE_ERROR); + ASSERT(strstr(sqlite3_errmsg(db), "memory_set_model must be called before adding content") != NULL); + sqlite3_finalize(stmt); + + sqlite3_close(db); +} + TEST(sqlite_custom_provider_add_text) { sqlite3 *db = open_test_db(); ASSERT(db != NULL); @@ -3231,6 +3246,7 @@ int main(int argc, char *argv[]) { printf("\nCustom provider tests:\n"); RUN_TEST(sqlite_custom_provider_register); RUN_TEST(sqlite_custom_provider_set_model); + RUN_TEST(sqlite_memory_add_text_requires_model); RUN_TEST(sqlite_custom_provider_add_text); RUN_TEST(sqlite_custom_provider_skips_whitespace_only_text); RUN_TEST(sqlite_custom_provider_persists_truncated_metadata);