Release 2.10.1 — local-first performance#69
Merged
Conversation
added 7 commits
July 12, 2026 19:24
… schema errors on legacy DBs The 7 tables carrying an arbitrary-key metadata/config object (neuron, fiber, brain, typed_memory, source, alerts, brain_versions) were declared SCHEMAFULL with FLEXIBLE object fields. On any DB whose tables were first created SCHEMALESS (every pre-existing/upgraded install), the non-convergent `DEFINE TABLE X SCHEMAFULL` is swallowed as already-exists, so the table stays SCHEMALESS and the following `DEFINE FIELD ... FLEXIBLE` fails every ensure_schema()/consolidate with 'FLEXIBLE can only be used in SCHEMAFULL tables' (8 warnings per run). Converting those live tables to SCHEMAFULL is unsafe: it bricks UPDATEs on any row holding a legacy field absent from the schema (verified). A SCHEMALESS table already accepts arbitrary nested object keys WITHOUT FLEXIBLE, so declare these 7 tables SCHEMALESS and use plain `TYPE object` — correct and identical on fresh and upgraded DBs. synapse + retrieval_trace stay SCHEMAFULL (a migration drops & recreates them, so their FLEXIBLE fields are always valid). Verified: applying the full SCHEMA_SQL to a fresh DB and to a legacy-schemaless DB both yield 0 FLEXIBLE errors and accept nested metadata writes.
…eout on large brains) _prune fetched per-source outgoing synapses via storage.get_synapses_for_neurons() for tens of thousands of candidate source neurons. The SurrealDB backend does not override that method, so the base implementation fanned out into one query PER neuron — ~50k sequential round-trips on a 66k-neuron / 390k-synapse brain — blowing the 120s per-strategy prune budget (consolidate reported 'prune timed out after 120.0s'). prune already holds every synapse in all_synapses, so build the source->synapses map in-memory (O(n), measured 0.04s) instead of asking storage. Same bridge-detection semantics (map values = all outgoing synapses per source). Read-path drops from ~110s (N+1) to ~15s. 46 consolidation/perf/pinned unit tests pass unchanged.
…DING_ENDPOINT The reranker can be pointed at a local OpenAI-compatible server via SURREAL_MEMORY_RERANKER_ENDPOINT, but the embedder had no equivalent — the openai-provider factory sites all construct OpenAIEmbedding(model=...) with no base_url, so a local bge-m3 (llamastash, http://127.0.0.1:11435/v1) could not be targeted without hijacking the shared OPENAI_BASE_URL. OpenAIEmbedding.__init__ now defaults base_url from SURREAL_MEMORY_EMBEDDING_ENDPOINT when none is passed, and supplies a placeholder api-key for keyless local servers. Every factory site (semantic_discovery, dashboard_api, stop-hook) picks this up with no further change. Added bge-m3/bge-large (1024) to the known-dimensions map.
…le embedder The stop hook's semantic-dedup fast-path only treated 'ollama' as a cheap local embedding provider and skipped everything else to stay fast. A local llama.cpp / llamastash bge-m3 served over the OpenAI-compatible API (127.0.0.1:11435, ~15ms/ embed on GPU) is just as cheap, but was being skipped — so session-end dedup fell back to simhash only. Recognize provider openai/openrouter with a loopback SURREAL_MEMORY_EMBEDDING_ENDPOINT as local-and-cheap; remote/cloud bases stay skipped to keep the hook fast.
… works immediately Embeddings were only ever populated by a batch `smem reindex`, so a just-saved memory was keyword-recallable but NOT semantically recallable until the next reindex — 41 neurons created in a 2h window had zero embedding_vec. Root cause: the encode pipeline never calls the embedder; add_neuron only stores a vector if the neuron already carries `_embedding` in metadata, which nothing on the write path set. encode() now finalizes with _embed_created_neurons(): one embed_batch over the non-ephemeral, non-TIME, content-bearing neurons it created, then update_neuron with the vector. Fail-soft — embeddings disabled or provider unavailable → memories stay keyword-only (no error, no slowdown), exactly the prior behaviour, so tests and offline setups are unaffected. With a local endpoint (bge-m3 via llamastash, ~15ms) the added cost is small. Verified against a live 66k-neuron brain: a permanent remember now stores a 1024-dim embedding_vec on the created neurons (was NONE); 162 encoder/pipeline unit tests pass unchanged (fail-soft with no provider in the test env).
Bump 2.10.0 -> 2.10.1 (pyproject, __init__, npm + vscode manifests) and document the 2.10.1 changes: configurable local embedding endpoint (SURREAL_MEMORY_EMBEDDING_ENDPOINT), inline embedding on write, prune N+1 timeout fix, SCHEMALESS metadata tables, and stop-hook local-embedder recognition.
…bump version test; format - OpenAIEmbedding supplied a placeholder api-key whenever ANY base_url was set, which included subclasses passing their own remote base_url — OpenRouterEmbedding (base_url=openrouter.ai) stopped requiring a key, breaking test_requires_api_key. Gate the placeholder to the local-endpoint env knob only (SURREAL_MEMORY_EMBEDDING_ ENDPOINT + no explicit base_url), so remote subclasses still require a real key. - Update test_version_is_current to 2.10.1. - ruff format encoder.py.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Performance + correctness patch for the SurrealDB backend, focused on large brains and local (self-hosted) embedding. No schema migration; recall ranking is unchanged unless you change your embedding config.
Five changes:
SURREAL_MEMORY_EMBEDDING_ENDPOINTlets theopenaiprovider target a local OpenAI-compatible server (bge-m3 via llama.cpp / llamastash), mirroring the reranker'sSURREAL_MEMORY_RERANKER_ENDPOINT. Placeholder api-key for keyless local servers.smem reindex, so semantic recall works immediately. Fail-soft (embeddings off / no provider → keyword-only, no error, no slowdown).prunebridge-detection issued one query per candidate source neuron (tens of thousands on a 66k-neuron brain), blowing the 120s per-strategy budget. Now groups the already-loaded synapses in memory (0 extra queries); read path ~110s → seconds.metadata/configtables are declaredSCHEMALESS(accept nested keys withoutFLEXIBLE), stoppingFLEXIBLE can only be used in SCHEMAFULL tableserrors on databases whose tables were first createdSCHEMALESS.synapse+retrieval_tracestaySCHEMAFULL.SURREAL_MEMORY_EMBEDDING_ENDPOINT(llamastash bge-m3) as local-and-cheap instead of falling back to simhash.Verification
SCHEMA_SQLapplied to fresh + legacy-schemaless DBs → 0 FLEXIBLE errors, nestedmetadatawrites work.remembernow stores a 1024-dimembedding_vec(wasNONE).Test plan