Repository navigation
Conversation
Reverts the second commit of MemMachine#1606 (a8322a7), "Remove per-project filterable properties", and keeps its first, the OpenAPI regeneration under the locked FastAPI 0.141.1: docs/openapi.json is regenerated here with the same tool, so it differs from the pre-MemMachine#1606 document only by the ValidationError fields that regeneration added. A project declares `properties_schema`, a set of caller property keys with types, on its long-term memory configuration; the event backend merges it into the vector store collection's indexed schema and rejects filters on any other `m.<key>`. Under this design the schema is fixed for the life of the project, a schema change is a delete and a create, and the vector store maps every distinct schema to one physical collection that all projects declaring it share. What that costs, per backend, is written where a tenant will see it: a warning in the configuration docs and the field descriptions on the server configuration, the project API, the memory-configuration API and the Python SDK. Alternative to the one-collection chain (MemMachine#1622..MemMachine#1616): the two exclude each other. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This was referenced Sep 15, 2026
Contributor
Author
|
Reopen if stack chosen. |
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.
Alternative to the
[vector store N/13]chain (#1597, #1622..#1616): the two exclude each other. This chain keeps per-projectproperties_schema(user-defined metadata indexes) and conforms Qdrant to the incarnation lifecycle; SQLite and Milvus follow later. Every PR is a draft.Purpose of the change
Reverts the second commit of #1606 ("Remove per-project filterable properties") and keeps its first, the OpenAPI regeneration under the locked FastAPI 0.141.1:
docs/openapi.jsonis regenerated here with the same tool, so it differs from the pre-#1606 document only by theValidationErrorfields regeneration added.A project declares
properties_schema, a set of caller property keys with types, on its long-term memory configuration; the event backend merges it into the vector store collection's indexed schema and rejects filters on any otherm.<key>. Under this design the schema is fixed for the life of the project, a schema change is a delete and a create, and the vector store maps every distinct schema to one physical collection that all projects declaring it share. What that costs, per backend, is written where a tenant will see it: a warning in the configuration docs and the field descriptions on the server configuration, the project API, the memory-configuration API and the Python SDK; the section below is the same text.Tenant-created physical resources: limits and cross-tenant effects
Every project (tenant) declares its own
properties_schema, fixed for the life of the project (to change it, delete the project and create it again; its memories start empty). Filters may name anym.<key>: a declared key is indexed where the backend supports it, an undeclared key is evaluated without an index. On Qdrant and Milvus the vector store maps every distinct(namespace, vector dimensions, properties_schema)to one physical collection that all projects declaring that schema share, created the first time a project declares it and never dropped by this code. One tenant's choices therefore change what other tenants get. Verified 2026-09-15 against the vendor docs and, for the Qdrant planner, Qdrant'sdevsource (read_view/filtering.rs,read_view/dispatch.rs,sample_estimation.rs).max_payload_index_count100, rejection of unindexed filters) do not apply; collections created by an earlier MemMachine keep their own setting.is_tenantkeyword index, which is the planner's primary clause, so the candidates are the project's own points, never the physical collection. An undeclared condition estimates as unknown (min 0), so the planner brute-forces the project's posting list with one payload read per point when the project's count in a segment is belowfull_scan_threshold(10,000 KB / vector bytes: ~1,700 points at 1536 dims), and otherwise samples 1,000 points to choose between that brute force and HNSW traversal with a payload read per visited node, where the missingpayload_mlinks for the undeclared value fragment traversal under a restrictive condition (fewer results). Payload is in thecoldtier (disk) by default. The cost lands on the querying project; other tenants feel it only as load on the shared node.$meta, 65,536 bytes per row, all keys together) and again into thepropertiesJSON field, and the filter, tenant term included, is evaluated per row over the project's hash partition (1 of 16 by default), which holds every tenant hashing there. Milvus allows 64 fields and 1,024 partitions per collection, VARCHAR <= 65,535.sqlite_vectakes thelimitnearest neighbors and applies the filter afterwards (k = min(limit, 4096)), so a selective filter returns fewer results thanlimit, declared or not; the search-engine store evaluates the filter per visited candidate with one SQL statement each and indexes no key.Stack
Slice 1 of 9, every PR targeting
speedkick; merge bottom-up.This PR's own change is its commit
c23c3959e; the rest of its diff is the slices under it, and drops out as they merge. Sits directly onspeedkick; slice 2 is stacked on it.Verification
ruff check,ruff format --check,ty check,pytest packages/server/server_tests(integration deselected): server 1924 passed, 3 skipped; client 255 passed; run at this slice's own commit on 2026-09-15.🤖 Generated with Claude Code