New long-term episodic memory (named EventMemory to differentiate) based on VectorStore and (new) SegmentStore#1205
Conversation
a724574 to
ced2263
Compare
5498137 to
e540bde
Compare
e540bde to
a6954a9
Compare
5d451f6 to
29d090d
Compare
|
Still needs some performance fixes. |
|
Maybe lazy ownership transfer is more performant. |
29d090d to
e3961e2
Compare
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
…iness Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
|
Wiring for SQLAlchemySegmentStore will be added in #1304 if this is merged. |
| class MessageContext(BaseModel): | ||
| """The content is communicated by a source.""" | ||
|
|
||
| type: Literal["message"] = "message" |
There was a problem hiding this comment.
It is clearer to use messagecontext
There was a problem hiding this comment.
Updated to be ProducerContext and context_type: "producer". Removed CitationContext (no use case yet).
Having "context" in the value for the discriminator is redundant and more error-prone than "context" in the discriminator key.
| segment_uuid: UUID | ||
| timestamp: datetime | ||
| context: Context = Field(default_factory=NullContext) | ||
| text: str |
There was a problem hiding this comment.
It is better to use block instead of plain str here
There was a problem hiding this comment.
The original intent was for Derivatives to be text-only since multimodal embedding models are uncommon. The solution for supporting multimodal embeddings is probably to make Derivatives text if the embedding reports no support for multimodality.
| f"{', '.join(sorted(reserved_fields))}" | ||
| ) | ||
|
|
||
| missing_fields = ( |
There was a problem hiding this comment.
This has been checked in the constructor
There was a problem hiding this comment.
This checks that input events for ingestion are not missing fields, which cannot be done at construction time.
Unless you mean it is checked at Event construction time? In that case I think it's safer to check close to where the condition is required.
There was a problem hiding this comment.
I do not think the code is working that way. It does not check the property of the incoming events.
There was a problem hiding this comment.
Sorry, I think that (or checking that the schema includes all fields required by events) was the behavior at some point but it changed and it was not deduplicated. I will remove.
|
|
||
| events = sorted( | ||
| events, | ||
| key=lambda event: (event.timestamp, event.uuid), |
There was a problem hiding this comment.
Since the UUID is random, there is no sense to sort by uuid here. Or do we require special UUID generator?
There was a problem hiding this comment.
The implied behavior is that UUID can be used to order events that have the same timestamp, so behavior with UUIDv7 vs. UUIDv4 works differently.
This is not documented, so I can remove it.
There was a problem hiding this comment.
I think the original motivation for sorting at all was to make temporal derivative eviction easier -- simulating the order of occurrence.
| """Event memory system.""" | ||
|
|
||
| # System-defined metadata field names. Reserved. | ||
| _SEGMENT_UUID_FIELD_NAME = "_segment_uuid" |
There was a problem hiding this comment.
Are they enough? Event source should be indexed
There was a problem hiding this comment.
Event source (along with all Context fields) is LLM-friendly private data that should not be indexed in a multi-tenant system where we may have encryption. If the user/consumer feels that they need to filter on this and it is not sensitive information, they may add it to the properties schema.
It is safer to completely prohibit indexing anything in Context because Context can reveal things like the user's name, interests (if adding content from something like a book), sensitive information like location and time they were there (which may reveal where they live), etc in plaintext (which may be required for most/all filters to work).
If it needs to be system-defined, the application may add _producer_id as a non-LLM-friendly system-reserved field in properties (EventMemory is not responsible for reserved prefix validation, and it allows for creating upper-level system-reserved fields.). In this proposed case, producer_id is a property that exposes no sensitive information (it is just a user or agent id like user_123 or a UUID or something).
Another challenge that we avoid by not indexing this is that semantically different fields (potentially with different types) with the same name do not collide. Because Context is a discriminated union, enforcing this would be complicated and each Context implementation would have to consider what other implementations have already reserved.
Originally I agreed with this idea. But the above considerations made me reconsider. See 41f2fc1, 7582233 for changes. Also the code was a lot more complicated when supporting filtering by context.
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
| f"{', '.join(sorted(reserved_fields))}" | ||
| ) | ||
|
|
||
| missing_fields = ( |
There was a problem hiding this comment.
I do not think the code is working that way. It does not check the property of the incoming events.
| t_embed - t_derive, | ||
| t_seg_store - t_embed, | ||
| t_v_store - t_seg_store, | ||
| t_v_store - t_start, |
There was a problem hiding this comment.
Add the stats to metrics
There was a problem hiding this comment.
Done. Renamed/reorganized.
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
| label_names=("phase",), | ||
| ) | ||
|
|
||
| self._text_splitter = RecursiveCharacterTextSplitter( |
There was a problem hiding this comment.
In our discussion, I think we agreed to move this out of the event memory
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
Signed-off-by: Edwin Yu <[email protected]>
|
Before merge, I would like to do another manual verification run on LoCoMo/LongMemEval, since there have been quite a few changes since last runs. |
|
LoCoMo score is good enough. Used SQLite for both segment store and vector store (sqlite-vec). Happy path works. |
|
No breaking changes since 0.3.7 so I'm changing the milestone to 0.3.8. Wiring determines if 0.4.0 is needed. |
Signed-off-by: Edwin Yu <[email protected]>
Purpose of the change
Motivation:
Description
For wiring into server, #1304 is required.
Choices:
Changes:
- introduce derivative eviction system for duplicate-heavy online-ingested data (cannot pre-dedup, need to maintain index quality)each segment comes from exactly one episode
each derivative comes from exactly one segment
alternative considered: many-to-many segments-to-derivatives
Approach to deletion:
Scoping:
by partition key: should allow sharding and horizontal scaling
this memory will be parallel to
DeclarativeMemoryDecisions to make:
POC:
Type of change
[Please delete options that are not relevant.]
How Has This Been Tested?
Checklist
Maintainer Checklist