chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Annotated
|
||||
from uuid import uuid4
|
||||
|
||||
from samples.concepts.memory.utils import print_record
|
||||
from samples.concepts.resources.utils import Colors, print_with_color
|
||||
from semantic_kernel.connectors.ai.open_ai import OpenAITextEmbedding
|
||||
from semantic_kernel.connectors.in_memory import InMemoryCollection
|
||||
from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel
|
||||
|
||||
# This is the most basic example of a vector store and collection
|
||||
# For a more complex example, using different collection types, see "complex_memory.py"
|
||||
# This sample uses openai text embeddings, so make sure to have your environment variables set up
|
||||
# it needs openai api key and embedding model id
|
||||
embedder = OpenAITextEmbedding(service_id="embedding")
|
||||
|
||||
# Next, you need to define your data structure
|
||||
# In this case, we are using a dataclass to define our data structure
|
||||
# you can also use a pydantic model, or a vanilla python class, see "data_models.py" for more examples
|
||||
# Inside the model we define which fields we want to use, and which fields are vectors
|
||||
# and for vector fields we define what kind of index we want to use, and what distance function we want to use
|
||||
# This has been done in constants here for simplicity, but you can also define them in the model itself
|
||||
# Next we create three records using that model
|
||||
|
||||
|
||||
@vectorstoremodel(collection_name="test")
|
||||
@dataclass
|
||||
class DataModel:
|
||||
content: Annotated[str, VectorStoreField("data")]
|
||||
id: Annotated[str, VectorStoreField("key")] = field(default_factory=lambda: str(uuid4()))
|
||||
vector: Annotated[
|
||||
list[float] | str | None,
|
||||
VectorStoreField("vector", dimensions=1536),
|
||||
] = None
|
||||
title: Annotated[str, VectorStoreField("data", is_full_text_indexed=True)] = "title"
|
||||
tag: Annotated[str, VectorStoreField("data", is_indexed=True)] = "tag"
|
||||
|
||||
def __post_init__(self):
|
||||
if self.vector is None:
|
||||
self.vector = self.content
|
||||
|
||||
|
||||
records = [
|
||||
DataModel(
|
||||
content="Semantic Kernel is awesome",
|
||||
id="e6103c03-487f-4d7d-9c23-4723651c17f4",
|
||||
title="Overview",
|
||||
tag="general",
|
||||
),
|
||||
DataModel(
|
||||
content="Semantic Kernel is available in dotnet, python and Java.",
|
||||
id="09caec77-f7e1-466a-bcec-f1d51c5b15be",
|
||||
title="Semantic Kernel Languages",
|
||||
tag="general",
|
||||
),
|
||||
DataModel(
|
||||
content="```python\nfrom semantic_kernel import Kernel\nkernel = Kernel()\n```",
|
||||
id="d5c9913a-e015-4944-b960-5d4a84bca002",
|
||||
title="Code sample",
|
||||
tag="code",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
async def main():
|
||||
print("-" * 30)
|
||||
# Create the collection here
|
||||
# by using the generic we make sure that IDE's understand what you need to pass in and get back
|
||||
# we also use the async with to open and close the connection
|
||||
# for the in memory collection, this is just a no-op
|
||||
# but for other collections, like Azure AI Search, this will open and close the connection
|
||||
async with InMemoryCollection[str, DataModel](
|
||||
record_type=DataModel,
|
||||
embedding_generator=embedder,
|
||||
) as record_collection:
|
||||
# Create the collection after wiping it
|
||||
print_with_color("Creating test collection!", Colors.CGREY)
|
||||
await record_collection.ensure_collection_exists()
|
||||
|
||||
# First add vectors to the records
|
||||
print_with_color("Adding records!", Colors.CBLUE)
|
||||
keys = await record_collection.upsert(records)
|
||||
print(f" Upserted {keys=}")
|
||||
print("-" * 30)
|
||||
|
||||
# Now we can get the records back
|
||||
print_with_color("Getting records!", Colors.CBLUE)
|
||||
results = await record_collection.get([records[0].id, records[1].id, records[2].id])
|
||||
if results and isinstance(results, Sequence):
|
||||
[print_record(record=result) for result in results]
|
||||
else:
|
||||
print("Nothing found...")
|
||||
print("-" * 30)
|
||||
|
||||
# Now we can search for records
|
||||
# First we define the options
|
||||
# The most important option is the vector_field_name, which is the name of the field that contains the vector
|
||||
# The other options are optional, but can be useful
|
||||
# The filter option is used to filter the results based on the tag field
|
||||
options = {
|
||||
"vector_property_name": "vector",
|
||||
"filter": lambda x: x.tag == "general",
|
||||
}
|
||||
query = "python"
|
||||
print_with_color(f"Searching for '{query}', with filter 'tag == general'", Colors.CBLUE)
|
||||
print_with_color(
|
||||
"Using vectorized search, the lower the score the better",
|
||||
Colors.CBLUE,
|
||||
)
|
||||
search_results = await record_collection.search(
|
||||
values=query,
|
||||
**options,
|
||||
)
|
||||
if search_results.total_count == 0:
|
||||
print("\nNothing found...\n")
|
||||
else:
|
||||
[print_record(result) async for result in search_results.results]
|
||||
print("-" * 30)
|
||||
|
||||
# lets cleanup!
|
||||
print_with_color("Deleting collection!", Colors.CBLUE)
|
||||
await record_collection.ensure_collection_deleted()
|
||||
print_with_color("Done!", Colors.CGREY)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user