Repository navigation
[user properties 2/2] Remove per-project filterable properties (port of #1606) - #1670
Conversation
71f3100 to
548494e
Compare
0e09327 to
4bc79e8
Compare
The event backend wrote every property of an event into its vector record and mapped the caller's whole filter onto the vector store, so a user key that a deployment never declared was both stored and filtered there: on Qdrant and Milvus as unindexed payload a filtered query scans for. The segment store already holds every property and already receives the whole filter for the context windows, so the vector side only duplicated work the segment store does anyway. The vector record now carries the keys the collection declares: EventMemory's reserved timestamp, and the `_`-prefixed system properties an adapter stamps on the event, which after MemMachine#1670 are exactly the collection's schema. The vector store is queried with the conjuncts of the filter that name only such fields; a conjunct is dropped whole when any field under it is a user property, so dropping only ever widens the vector search, and the segment store narrows it back on the windows. User keys never reach the vector store, so a tenant's properties cannot shape what it stores or scans. `filter_fields` joins the filter parser: every field name a tree addresses. Rebased onto MemMachine#1631, where the vector record no longer carries the segment uuid (the segment store maps a derivative to its segment). Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01ESpWYTmCR7X3bJEpoA8SAn
d156084 to
b65961f
Compare
b65961f to
ea69766
Compare
xiongzubiao
left a comment
There was a problem hiding this comment.
The PR description says: "The squash also carries #1606's side commit regenerating docs/openapi.json under the locked FastAPI (ValidationError gains input and ctx), seven lines of that file's diff". That isn't true of this diff. Its docs/openapi.json changes are 53 deletions and no additions. input and ctx are already on feat/horizontal-scaling (docs/openapi.json:5921-5924), added by d5ec7c7 (#1733). The seven added lines in #1606's version of that file are the only part of its diff that this PR doesn't have.
I found no references to properties_schema left anywhere in the repo at this head. That covers source, tests, both OpenAPI files, the TS client, integrations, examples and the sample configs; indexed_properties_schema is a separate setting.
| ), | ||
| ), | ||
| ] | ||
| properties_schema: Annotated[ |
There was a problem hiding this comment.
None of ProjectConfig, the memory-configuration update models, or the server config models set extra="forbid". So a properties_schema that a REST client still sends in create-project or the memory-configuration PUT is dropped silently, and the request returns 200. The same happens when an existing YAML or stored config still has the field. Python SDK callers get a TypeError for the removed keyword argument. REST callers get no sign that the field was ignored.
|
|
||
| def _check(field: str) -> str: | ||
| internal_name, is_user_metadata = normalize_filter_field(field) | ||
| _internal_name, is_user_metadata = normalize_filter_field(field) |
There was a problem hiding this comment.
Nothing reads _internal_name now; _, is_user_metadata = normalize_filter_field(field) would make that plain. Minor.
|
I think the order of this and #1702 should be reversed. |
ea69766 to
75a0d21
Compare
75a0d21 to
5873739
Compare
marvinyu-memverge
left a comment
There was a problem hiding this comment.
Approve at 5873739, on #1702's 6cdbe07. The removal is complete and nothing that depended on the per-project schema is left behind.
Verified at the head: the port commit is the same patch I read at ea69766 (git range-diff reports it unchanged), with long_term_memory.py and service_locator.py identical at both heads; no remaining use of the name in packages/ (server, common, client, ts-client), integrations/, examples/, evaluation/, tools/, docs/, design/, sample_configs/, deployments/ or the wizard; with #1702 beneath, a vector record carries only the keys the collection declares, which this PR reduces to _timestamp plus the EVENT_BACKEND_SYSTEM_FIELDS the service locator creates it with, so no caller key reaches the vector store, and a filter conjunct naming any m.<key> is dropped whole from the vector query and evaluated by the segment store, as the body says; the _-prefix reservation survives in LongTermMemory._episode_to_event, so system fields still cannot be spoofed through filterable_metadata; _validate_event_backend_filter checks bare names only and passes any m.<key>; the session collection open-or-create path is unchanged apart from the dropped spread; PROPERTY_TYPE_NAME_TO_PROPERTY_TYPE still has users (properties_json.py, vector_store/data_types.py). This also closes the REST case from my #1733 ask 1 for collections created from here on. CI: unit tests, ty, ruff, docs and the client package green; the eight red integration jobs are Docker Hub's unauthenticated pull limit (toomanyrequests, no image pulled, no test ran), the same failure #1702 shows at the same commit.
The event backend wrote every property of an event into its vector record and mapped the caller's whole filter onto the vector store, so a user key that a deployment never declared was both stored and filtered there: on Qdrant and Milvus as unindexed payload a filtered query scans for. The segment store already holds every property and already receives the whole filter for the context windows, so the vector side only duplicated work the segment store does anyway. The vector record now carries the keys the collection declares: EventMemory's reserved timestamp, the `_`-prefixed system properties an adapter stamps on the event, and the keys a project's `properties_schema` declares. The vector store is queried with the conjuncts of the filter that name only such fields; a conjunct is dropped whole when any field under it is undeclared, so dropping only ever widens the vector search, and the segment store narrows it back on the windows. An undeclared key never reaches the vector store, so a tenant's undeclared properties cannot shape what it stores or scans. `filter_fields` joins the filter parser: every field name a tree addresses. Rebased onto MemMachine#1631, where the vector record no longer carries the segment uuid (the segment store maps a derivative to its segment). Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01ESpWYTmCR7X3bJEpoA8SAn
…ck) (MemMachine#1606) * Regenerate the OpenAPI document under the locked FastAPI `docs/openapi.json` predates the FastAPI release in `uv.lock` (0.141.1), whose `ValidationError` component carries `input` and `ctx`; regenerating the document with `docs/tools/generate_openapi.py` adds the two fields and changes nothing else. Separate from the API changes above it so their diffs of this file show only what they change. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01ESpWYTmCR7X3bJEpoA8SAn * Remove per-project filterable properties A project could declare `properties_schema`, a set of caller property keys with types, on its long-term memory configuration; the event backend merged it into the vector store collection's indexed schema and rejected filters on any other `m.<key>`. That let a tenant create database resources (indexes, columns) by naming them in a request, which is what forced per-collection native resources named by a hash of their schema on the backends that limit them. The option is removed from the server configuration, the project API and the memory-configuration API, the Python SDK, the sample configurations, the configuration docs and the OpenAPI document. A filter may name any `m.<key>`; the stores evaluate it on the properties they hold. What a store indexes is decided by the deployment, not per project. A breaking API change on `speedkick`. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01ESpWYTmCR7X3bJEpoA8SAn Rebased onto MemMachine#1631: the per-project schema also leaves MemMachine#1631's service locator, which creates the session's collection in a retry loop, and the commented option goes from the event sample configuration MemMachine#1698 added. --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]> (cherry picked from commit a8322a7)
5873739 to
7992829
Compare
Port
Copy of #1606, merged into
speedkickasa8322a790, on #1702, beneath #1627, whose code depends on it: the same commit, with the per-project schema also removed from #1736's service locator, which creates the session's collection in a retry loop, and the commented option removed from the event sample configuration #1698 added. The squash also carries #1606's side commit regeneratingdocs/openapi.jsonunder the locked FastAPI (ValidationErrorgainsinputandctx), seven lines of that file's diff; the rest of it removesproperties_schema.Purpose of the change
A project could declare
properties_schema, a set of caller property keys with types, on its long-term memory configuration; the event backend merged it into the vector store collection's indexed schema and rejected filters on any otherm.<key>. That let a tenant create database resources (indexes, columns) by naming them in a request, which is what forced per-collection native resources named by a hash of their schema on the backends that limit them.The option is removed from the server configuration, the project API and the memory-configuration API, the Python SDK, the sample configurations, the configuration docs and the OpenAPI document. A filter may name any
m.<key>; #1702, beneath this, keeps such keys off the vector store, so the segment store evaluates it, and with the option gone no tenant key reaches the vector store at all. What a store indexes is decided by the deployment, not per project.A breaking API change on
main:properties_schemaleavesProjectConfig, the SDK'screate_projectandget_or_create_project, and the memory-configuration update route.Part of the fix for #1781.
Stack
21 open PRs: three independent PRs, and the vector store tree of short parallel branches. Every PR in the tree has
feat/horizontal-scalingas its GitHub base, and the independent PRs havemain. The branches are in a fork, and a pull request can target only this repository's branches, so the on column gives the order the PRs build on each other. A stacked PR's diff on GitHub includes the PRs under it until they merge.Independent of the vector store tree, directly on
main:mainmainmainThe vector store tree. Each PR builds on the one in its on column; PRs on the same parent are parallel branches and do not depend on each other. #1631 is closed, superseded by #1733–#1736, which hold its changes split in four, with review changes since. #1702 and #1670 sit beneath #1627, whose code depends on them. Until the PRs under it merge, their changes show in a stacked PR's diff.
mainfeat/horizontal-scaling)feat/horizontal-scalingfeat/horizontal-scaling)feat/horizontal-scalingfeat/horizontal-scaling)feat/horizontal-scalingfeat/horizontal-scaling)feat/horizontal-scalingfeat/horizontal-scaling)feat/horizontal-scalingfeat/horizontal-scaling)feat/horizontal-scalingfeat/horizontal-scaling)feat/horizontal-scalingfeat/horizontal-scalingfeat/horizontal-scalingThis PR is its one commit,
799282992, stacked on #1702. #1627 is stacked on it.Verification
At this PR's head
799282992, on 2026-10-09, onfeat/horizontal-scalingwith #1813 merged:ruff checkandruff format --checkclean;ty checkclean as CI runs it (uv run --frozen --all-extras ty check --project packages/server);uv lock --checkclean.Before the rebase onto #1813's merge: At this PR's head
587373950, on 2026-10-09:ruff checkandruff format --checkclean;ty checkclean as CI runs it (uv run --frozen --all-extras ty check --project packages/server);uv lock --checkclean. The full suites were not run at this head: the server suite without integration tests last passed at9031d25f4, on 2026-10-08, 2166 tests, and the integration tests of the vector stores, the resource manager, episodic memory, and semantic storage at9031d25f4, on 2026-10-08, 689 tests, against PostgreSQL 16, Neo4j, Qdrant 1.19.1, and Milvus 2.6.24 in containers. This head differs from those by #1779's change to the Qdrant store and the rebase onto the merged #1736.🤖 Generated with Claude Code
https://claude.ai/code/session_01ESpWYTmCR7X3bJEpoA8SAn