Skip to content

Memory Refactor: Simplify Memory Interfaces, Remove Unnecessary Generics, Move Vector Stores to Connectors - #308

Merged
Adrian Bonar (adrianwyatt) merged 50 commits into
microsoft:mainfrom
awharrison-28:memory_refactor
Apr 6, 2023
Merged

Adrian Bonar (adrianwyatt) merged 50 commits into
microsoft:mainfrom
awharrison-28:memory_refactor

Conversation

@awharrison-28

@awharrison-28 Abby Harrison (awharrison-28) commented Apr 4, 2023 •

Copy link
Copy Markdown
Contributor

Motivation and Context

Existing memory interfaces are overly complicated, leading to confusion about how to implement a custom Memory Store.

This PR condenses IMemoryStore + IEmbeddingIndex + IDataStore -> IMemoryStore and eliminates the use of generics spread by the Embedding struct. It makes the codebase much easier to read and IMemoryStore easier to implement. As part of the effort, SqliteMemoryStore was properly implemented (it was not previously).

Additionally, this PR moves the MemoryStore 'skills' to the connectors namespace.

Addresses several points called out in #202

Description

  • QdrantMemoryStore was moved from Skills to Connectors. Unit tests were added for the memory store object. Created an IQdrantVectorDbClient interface to better enable testing of the memory store through mocks.
  • Modified QdrantVectorDbClient calls to default batched calls to reduced the amount of HTTP traffic when possible. GetVectorById -> GetVectorsById, UpsertVector -> UpsertVectors, DeleteVector -> DeleteVectors.
  • Generally, for a vector store, the unique id passed by the user to associate with a saved memory will become the indexed ID for the vector store as well. However, Qdrant enforces a user-provided GUID or hex string as the index key. Added a nullable key field to MemoryRecord to support vector storage services that act similarly. Depending on the backing storage, this key is not necessary to pass to subsequent calls. SemanticTextMemory does not utilized this parameter. For now, this is only for direct interaction with the QdrantMemoryStore. Note: It is understood that Qdrant will be enabling service-side GUID generation in the near future, significantly reducing the stress on the client to produce unique values for every data entry.
  • Eliminated the dependency on the core kernel in the connectors by replacing Embeddings struct references with IEnumerable<float>.
  • SqliteMemoryStore was moved from Skills to Connectors. SqliteMemoryStore now properly implements IMemoryStore and the unit tests were updated to reflect this fact.
  • The generic TEmbedding was removed from all Kernel and Connector files. If a memory store expects a non-FP32 vector, it is currently up to the IMemoryStore implementation to perform the necessary conversions.
  • DataEntry is now a base class that enforces nullable Key and Timestamp fields. It is not strictly used for anything other than MemoryRecord. It may serve useful for connecting to data stores that are for structures other than embeddings, or vector stores that calculate embeddings server-side.
  • Removed existing collection checks for Upsert calls. Depending on the implementation, this has the potential to drastically increase HTTP traffic or local compute resources when making batch calls. The call has been moved to SemanticTextMemory for now to avoid additional breaking changes, but will be addressed in a better fashion in a follow-up PR.
  • Added a very simple example to KernelSyntaxExamples on how to create a read-only IMemoryStore that imports vectors from a json string.
  • Renamed VectorDbException -> QdrantMemoryException. Ensured that these exceptions can only be thrown by the QdrantMemoryStore and can accept an inner exception as a paramter.

Additional small edits:

  • removed unnecessary namespace from KernelSyntaxExamples Example21 and Example22
  • Removed unused references in RestApiOperations.cs and JsonPathSkill.cs

---------------- OUT OF SCOPE, COMING SOON ----------------

  • The addition of a value string to MemoryRecord. This is very important for metadata flexibility, but it will come in a follow-up PR.
  • The ability to optionally return the vector to the user. In many cases, the vector is not important for get/search calls, thus it makes no sense to pass such large amounts of data around the kernel application.
  • Disambiguation of SemanticTextMemory interfaces, classes, and constructors.
  • Unit tests for QdrantVectorDbClient. Qdrant will be making breaking changes to their API very soon. TBD whether integration tests will be added as well.

Contribution Checklist

@awharrison-28 Abby Harrison (awharrison-28) added enhancement PR: in progress Under development and/or addressing feedback .NET Issue or Pull requests regarding .NET code labels Apr 4, 2023
@awharrison-28 Abby Harrison (awharrison-28) added PR: ready for review All feedback addressed, ready for reviews and removed PR: feedback to address Waiting for PR owner to address comments/questions labels Apr 5, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with a few other comments but otherwise LGTM overall.

@adrianwyatt Adrian Bonar (adrianwyatt) added PR: feedback to address Waiting for PR owner to address comments/questions and removed PR: ready for review All feedback addressed, ready for reviews labels Apr 5, 2023
…emoryExceptions are only thrown in the QdrantMemoryStore; passed inner exceptions to QdrantMemoryException where applicable
@adrianwyatt
Adrian Bonar (adrianwyatt) enabled auto-merge (squash) April 6, 2023 00:22
@adrianwyatt
Adrian Bonar (adrianwyatt) merged commit 1e72692 into microsoft:main Apr 6, 2023
@awharrison-28
Abby Harrison (awharrison-28) deleted the memory_refactor branch April 10, 2023 16:19
Desmond Howard (dehoward) pushed a commit to lemillermicrosoft/semantic-kernel that referenced this pull request Jun 1, 2023
…ics, Move Vector Stores to Connectors (microsoft#308)

### Motivation and Context
Existing memory interfaces are overly complicated, leading to confusion
about how to implement a custom Memory Store.

This PR condenses `IMemoryStore` + `IEmbeddingIndex` + `IDataStore `->
**`IMemoryStore`** and eliminates the use of generics spread by the
Embedding struct. It makes the codebase much easier to read and
IMemoryStore easier to implement. As part of the effort,
SqliteMemoryStore was properly implemented (it was not previously).

Additionally, this PR moves the MemoryStore 'skills' to the connectors
namespace.

Addresses several points called out in
microsoft#202

### Description

- `QdrantMemoryStore` was moved from Skills to Connectors. Unit tests
were added for the memory store object. Created an IQdrantVectorDbClient
interface to better enable testing of the memory store through mocks.
- Modified QdrantVectorDbClient calls to default batched calls to
reduced the amount of HTTP traffic when possible. `GetVectorById ->
GetVectorsById`, `UpsertVector -> UpsertVectors`, `DeleteVector ->
DeleteVectors`.
- Generally, for a vector store, the unique id passed by the user to
associate with a saved memory will become the indexed ID for the vector
store as well. However, Qdrant enforces a user-provided GUID or hex
string as the index key. Added a nullable key field to `MemoryRecord` to
support vector storage services that act similarly. Depending on the
backing storage, this key is not necessary to pass to subsequent calls.
`SemanticTextMemory` does not utilized this parameter. For now, this is
only for direct interaction with the `QdrantMemoryStore`. **Note**: It
is understood that Qdrant will be enabling service-side GUID generation
in the near future, significantly reducing the stress on the client to
produce unique values for every data entry.
- Eliminated the dependency on the core kernel in the connectors by
replacing `Embeddings` struct references with `IEnumerable<float>`.
- `SqliteMemoryStore` was moved from Skills to Connectors.
`SqliteMemoryStore` now properly implements `IMemoryStore` and the unit
tests were updated to reflect this fact.
- The generic `TEmbedding` was removed from all Kernel and Connector
files. **If a memory store expects a non-FP32 vector, it is currently up
to the `IMemoryStore` implementation to perform the necessary
conversions.**
- `DataEntry` is now a base class that enforces nullable Key and
Timestamp fields. It is not strictly used for anything other than
MemoryRecord. It may serve useful for connecting to data stores that are
for structures other than embeddings, or vector stores that calculate
embeddings server-side.
- Removed existing collection checks for `Upsert` calls. Depending on
the implementation, this has the potential to drastically increase HTTP
traffic or local compute resources when making batch calls. The call has
been moved to SemanticTextMemory for now to avoid additional breaking
changes, but will be addressed in a better fashion in a follow-up PR.
- Added a very simple example to `KernelSyntaxExamples `on how to create
a read-only `IMemoryStore` that imports vectors from a json string.
- Renamed `VectorDbException` -> `QdrantMemoryException`. Ensured that
these exceptions can only be thrown by the `QdrantMemoryStore` and can
accept an inner exception as a paramter.

Additional small edits:
- removed unnecessary namespace from KernelSyntaxExamples Example21 and
Example22
- Removed unused references in `RestApiOperations.cs` and
`JsonPathSkill.cs`

---------------- OUT OF SCOPE, COMING SOON ----------------
- The addition of a value string to MemoryRecord. This is very important
for metadata flexibility, but it will come in a follow-up PR.
- The ability to optionally return the vector to the user. In many
cases, the vector is not important for get/search calls, thus it makes
no sense to pass such large amounts of data around the kernel
application.
- Disambiguation of SemanticTextMemory interfaces, classes, and
constructors.
- Unit tests for QdrantVectorDbClient. Qdrant will be making breaking
changes to their API very soon. TBD whether integration tests will be
added as well.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Issue or Pull requests regarding .NET code PR: feedback to address Waiting for PR owner to address comments/questions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants