Hosted with nbsanity. See source notebook on GitHub.

Open In Colab

Advent of Haystack: Day 2

In this challenge, your mission is to help a couple of fictional elves in the film “A Very Weaviate Christmas”. 1. Find out what’s happening in the film “A Very Weaviate Christmas” 2. This will lead you to a clue that will let you discover which Weaviate Collection to peak into. 3. While submitting the challenge, tell us what you find there!

Components to use:

  1. OpenAITextEmbedder
  2. OpenAIGenerator
  3. PromptBuilder
  4. WeaviateDocumentStore
  5. WeaviateEmbeddingRetriever

🎄 Your task is to complete steps 3 and 4. But make sure you run the code cells before. You should know what each prior step is doing.

1) Setup and Installation

!pip install -q haystack-ai weaviate-haystack
!pip install -q --upgrade openai # not to get the OpenAI proxies error: https://community.openai.com/t/error-with-openai-1-56-0-client-init-got-an-unexpected-keyword-argument-proxies/1040332/2

To get started, first provide your API keys below. We’re providing you with a read-only API Key for Weaviate.

For this challenge, we’ve prepared a Weaviate Collection for you which contains lots of movies and their overviews.

import os
from getpass import getpass

os.environ["WEAVIATE_API_KEY"] = "b3jhGwa4NkLGjaq3v1V1vh1pTrlKjePZSt91"

if "OPENAI_API_KEY" not in os.environ:
    os.environ["OPENAI_API_KEY"] = getpass("Enter OpenAI API key:")
Enter OpenAI API key:··········

2) Weaviate Setup

Next, you can connect to the right WeaviateDocumentStore (we’ve already added the right code for you below with the client URL in place).

In this document store, there are many movies, their titles and their overviews.

from haystack_integrations.document_stores.weaviate import (
    WeaviateDocumentStore,
    AuthApiKey,
)
import os


auth_client_secret = AuthApiKey()

document_store = WeaviateDocumentStore(
    url="https://zgvjwlycsr6p5j1ziuyea.c0.europe-west3.gcp.weaviate.cloud",
    auth_client_secret=auth_client_secret,
)
!pip install wat-inspector
from wat import wat
Collecting wat-inspector
  Downloading wat_inspector-0.4.3-py3-none-any.whl.metadata (14 kB)
Downloading wat_inspector-0.4.3-py3-none-any.whl (15 kB)
Installing collected packages: wat-inspector
Successfully installed wat-inspector-0.4.3
document_store | wat
value: <haystack_integrations.document_stores.weaviate.document_store.WeaviateDocumentStore object at 0x7e0665713520>
type: haystack_integrations.document_stores.weaviate.document_store.WeaviateDocumentStore

Public attributes:
  client: weaviate.client.WeaviateClient = <weaviate.client.WeaviateClient object at 0x7e06657abfa0>
  collection: weaviate.collections.collection.sync.Collection = <weaviate.Collection config={…

  def count_documents() -> int # Returns the number of documents present in the DocumentStore.
  def delete_documents(document_ids: List[str]) -> None # Deletes all documents with matching document_ids from the DocumentStore.…
  def filter_documents(filters: Optional[Dict[str, Any]] = None) -> List[haystack.dataclasses.document.Document] # Returns the documents that match the filters provided.…
  def from_dict(data: Dict[str, Any]) -> 'WeaviateDocumentStore' # Deserializes the component from a dictionary.…
  def to_dict() -> Dict[str, Any] # Serializes the component to a dictionary.…
  def write_documents(documents: List[haystack.dataclasses.document.Document], policy: haystack.document_stores.types.policy.DuplicatePolicy = <DuplicatePolicy.NONE: 'none'>) -> int # Writes documents to Weaviate using the specified policy.…

Private attributes:
  _additional_config: NoneType = None
  _additional_headers: NoneType = None
  _auth_client_secret: haystack_integrations.document_stores.weaviate.auth.AuthApiKey = AuthApiKey(api_key=EnvVarSecret(_env_vars=('WEAVIATE_API_KEY',), _strict=True, _type=<SecretT…
  _client: NoneType = None
  _collection: NoneType = None
  _collection_settings: dict = {…
  _embedded_options: NoneType = None
  _grpc_port: int = 50051
  _grpc_secure: bool = False
  _url: str = 'https://zgvjwlycsr6p5j1ziuyea.c0.europe-west3.gcp.weaviate.cloud'

  def _batch_write(documents: List[haystack.dataclasses.document.Document]) -> int # Writes document to Weaviate in batches.…
  def _bm25_retrieval(query: str, filters: Optional[Dict[str, Any]] = None, top_k: Optional[int] = None) -> List[haystack.dataclasses.document.Document]
  def _clean_connection_settings()
  def _embedding_retrieval(query_embedding: List[float], filters: Optional[Dict[str, Any]] = None, top_k: Optional[int] = None, distance: Optional[float] = None, certainty: Optional[float] = None) -> List[haystack.dataclasses.document.Document]
  def _query() -> List[Dict[str, Any]]
  def _query_with_filters(filters: Dict[str, Any]) -> List[Dict[str, Any]]
  def _to_data_object(document: haystack.dataclasses.document.Document) -> Dict[str, Any] # Converts a Document to a Weaviate data object ready to be saved.
  def _to_document(data: weaviate.collections.classes.data.DataObject[typing.Dict[str, typing.Any], NoneType]) -> haystack.dataclasses.document.Document # Converts a data object read from Weaviate into a Document.
  def _write(documents: List[haystack.dataclasses.document.Document], policy: haystack.document_stores.types.policy.DuplicatePolicy) -> int # Writes documents to Weaviate using the specified policy.…
document_store.count_documents()
681

3) The RAG Pipeline

Now, you’re on your own. Complete the code blocks below.

First, create a RAG pipeline that can answer questions based on the overviews of the movies in your document_store.

⭐️ You should then be able to run the pipeline and answer the questions “What happens in the film ‘A Very Weaviate Christmas’?”

💚 Hint 1: The embedding model that was used to populate the vectors was text-embedding-3-small by OpenAI.

💙 Hint 2: We’ve added an import to the OpenAIGenerator but feel free to use something else!

from haystack import Pipeline
from haystack.components.embedders import OpenAITextEmbedder
from haystack.components.generators import OpenAIGenerator
from haystack.components.builders import PromptBuilder
from haystack_integrations.components.retrievers.weaviate import (
    WeaviateEmbeddingRetriever,
)
text_embedder = OpenAITextEmbedder(model="text-embedding-3-small")
retriever = WeaviateEmbeddingRetriever(document_store=document_store)

template = """
Given the following information, answer the question.

Context:
{% for document in documents %}
    {{ document.content }}
{% endfor %}

Question: {{question}}
Answer:
"""
prompt_builder = PromptBuilder(template=template)

llm = OpenAIGenerator(model="gpt-4o-mini")
# Change the Logging Level
# This way, not only warnings but also information messages are displayed in the console output.
import logging

logging.basicConfig(
    format="%(levelname)s - %(name)s -  %(message)s", level=logging.WARNING
)
logging.getLogger("haystack").setLevel(logging.DEBUG)
rag = Pipeline()

rag.add_component("text_embedder", text_embedder)
rag.add_component("retriever", retriever)
rag.add_component("prompt_builder", prompt_builder)
rag.add_component("llm", llm)

rag.connect("text_embedder.embedding", "retriever.query_embedding")
rag.connect("retriever", "prompt_builder")
rag.connect("prompt_builder.prompt", "llm")
DEBUG:haystack.core.pipeline.base:Adding component 'text_embedder' (<haystack.components.embedders.openai_text_embedder.OpenAITextEmbedder object at 0x7e052d05b670>

Inputs:
  - text: str
Outputs:
  - embedding: List[float]
  - meta: Dict[str, Any])
DEBUG:haystack.core.pipeline.base:Adding component 'retriever' (<haystack_integrations.components.retrievers.weaviate.embedding_retriever.WeaviateEmbeddingRetriever object at 0x7e052d059960>

Inputs:
  - query_embedding: List[float]
  - filters: Optional[Dict[str, Any]]
  - top_k: Optional[int]
  - distance: Optional[float]
  - certainty: Optional[float]
Outputs:
  - documents: List[Document])
DEBUG:haystack.core.pipeline.base:Adding component 'prompt_builder' (<haystack.components.builders.prompt_builder.PromptBuilder object at 0x7e052d059ff0>

Inputs:
  - documents: Any
  - question: Any
  - template: Optional[str]
  - template_variables: Optional[Dict[str, Any]]
Outputs:
  - prompt: str)
DEBUG:haystack.core.pipeline.base:Adding component 'llm' (<haystack.components.generators.openai.OpenAIGenerator object at 0x7e052d7a9e40>

Inputs:
  - prompt: str
  - system_prompt: Optional[str]
  - streaming_callback: Optional[Callable[]]
  - generation_kwargs: Optional[Dict[str, Any]]
Outputs:
  - replies: List[str]
  - meta: List[Dict[str, Any]])
DEBUG:haystack.core.pipeline.base:Connecting 'text_embedder.embedding' to 'retriever.query_embedding'
DEBUG:haystack.core.pipeline.base:Connecting 'retriever.documents' to 'prompt_builder.documents'
DEBUG:haystack.core.pipeline.base:Connecting 'prompt_builder.prompt' to 'llm.prompt'
<haystack.core.pipeline.pipeline.Pipeline object at 0x7e052d059a20>
🚅 Components
  - text_embedder: OpenAITextEmbedder
  - retriever: WeaviateEmbeddingRetriever
  - prompt_builder: PromptBuilder
  - llm: OpenAIGenerator
🛤️ Connections
  - text_embedder.embedding -> retriever.query_embedding (List[float])
  - retriever.documents -> prompt_builder.documents (List[Document])
  - prompt_builder.prompt -> llm.prompt (str)
rag.show()
DEBUG:haystack.core.pipeline.draw:Mermaid diagram:

%%{ init: {'theme': 'neutral' } }%%

graph TD;

text_embedder["<b>text_embedder</b><br><small><i>OpenAITextEmbedder</i></small>"]:::component -- "embedding -> query_embedding<br><small><i>List[float]</i></small>" --> retriever["<b>retriever</b><br><small><i>WeaviateEmbeddingRetriever<br><br>Optional inputs:<ul style='text-align:left;'><li>filters (Optional[Dict[str, Any]])</li><li>top_k (Optional[int])</li><li>distance (Optional[float])</li><li>certainty (Optional[float])</li></ul></i></small>"]:::component
retriever["<b>retriever</b><br><small><i>WeaviateEmbeddingRetriever<br><br>Optional inputs:<ul style='text-align:left;'><li>filters (Optional[Dict[str, Any]])</li><li>top_k (Optional[int])</li><li>distance (Optional[float])</li><li>certainty (Optional[float])</li></ul></i></small>"]:::component -. "documents -> documents (opt.)<br><small><i>List[Document]</i></small>" .-> prompt_builder["<b>prompt_builder</b><br><small><i>PromptBuilder<br><br>Optional inputs:<ul style='text-align:left;'><li>question (Any)</li><li>template (Optional[str])</li><li>template_variables (Optional[Dict[str, Any]])</li></ul></i></small>"]:::component
prompt_builder["<b>prompt_builder</b><br><small><i>PromptBuilder<br><br>Optional inputs:<ul style='text-align:left;'><li>question (Any)</li><li>template (Optional[str])</li><li>template_variables (Optional[Dict[str, Any]])</li></ul></i></small>"]:::component -- "prompt -> prompt<br><small><i>str</i></small>" --> llm["<b>llm</b><br><small><i>OpenAIGenerator<br><br>Optional inputs:<ul style='text-align:left;'><li>system_prompt (Optional[str])</li><li>streaming_callback (Optional[Callable[]])</li><li>generation_kwargs (Optional[Dict[str, Any]])</li></ul></i></small>"]:::component
i{&ast;}--"text<br><small><i>str</i></small>"--> text_embedder["<b>text_embedder</b><br><small><i>OpenAITextEmbedder</i></small>"]:::component
text_embedder["<b>text_embedder</b><br><small><i>OpenAITextEmbedder</i></small>"]:::component--"meta<br><small><i>Dict[str, Any]</i></small>"--> o{&ast;}
llm["<b>llm</b><br><small><i>OpenAIGenerator<br><br>Optional inputs:<ul style='text-align:left;'><li>system_prompt (Optional[str])</li><li>streaming_callback (Optional[Callable[]])</li><li>generation_kwargs (Optional[Dict[str, Any]])</li></ul></i></small>"]:::component--"replies<br><small><i>List[str]</i></small>"--> o{&ast;}
llm["<b>llm</b><br><small><i>OpenAIGenerator<br><br>Optional inputs:<ul style='text-align:left;'><li>system_prompt (Optional[str])</li><li>streaming_callback (Optional[Callable[]])</li><li>generation_kwargs (Optional[Dict[str, Any]])</li></ul></i></small>"]:::component--"meta<br><small><i>List[Dict[str, Any]]</i></small>"--> o{&ast;}

classDef component text-align:center;

DEBUG:haystack.core.pipeline.draw:Rendering graph at https://mermaid.ink/img/CiUleyBpbml0OiB7J3RoZW1lJzogJ25ldXRyYWwnIH0gfSUlCgpncmFwaCBURDsKCnRleHRfZW1iZWRkZXJbIjxiPnRleHRfZW1iZWRkZXI8L2I+PGJyPjxzbWFsbD48aT5PcGVuQUlUZXh0RW1iZWRkZXI8L2k+PC9zbWFsbD4iXTo6OmNvbXBvbmVudCAtLSAiZW1iZWRkaW5nIC0+IHF1ZXJ5X2VtYmVkZGluZzxicj48c21hbGw+PGk+TGlzdFtmbG9hdF08L2k+PC9zbWFsbD4iIC0tPiByZXRyaWV2ZXJbIjxiPnJldHJpZXZlcjwvYj48YnI+PHNtYWxsPjxpPldlYXZpYXRlRW1iZWRkaW5nUmV0cmlldmVyPGJyPjxicj5PcHRpb25hbCBpbnB1dHM6PHVsIHN0eWxlPSd0ZXh0LWFsaWduOmxlZnQ7Jz48bGk+ZmlsdGVycyAoT3B0aW9uYWxbRGljdFtzdHIsIEFueV1dKTwvbGk+PGxpPnRvcF9rIChPcHRpb25hbFtpbnRdKTwvbGk+PGxpPmRpc3RhbmNlIChPcHRpb25hbFtmbG9hdF0pPC9saT48bGk+Y2VydGFpbnR5IChPcHRpb25hbFtmbG9hdF0pPC9saT48L3VsPjwvaT48L3NtYWxsPiJdOjo6Y29tcG9uZW50CnJldHJpZXZlclsiPGI+cmV0cmlldmVyPC9iPjxicj48c21hbGw+PGk+V2VhdmlhdGVFbWJlZGRpbmdSZXRyaWV2ZXI8YnI+PGJyPk9wdGlvbmFsIGlucHV0czo8dWwgc3R5bGU9J3RleHQtYWxpZ246bGVmdDsnPjxsaT5maWx0ZXJzIChPcHRpb25hbFtEaWN0W3N0ciwgQW55XV0pPC9saT48bGk+dG9wX2sgKE9wdGlvbmFsW2ludF0pPC9saT48bGk+ZGlzdGFuY2UgKE9wdGlvbmFsW2Zsb2F0XSk8L2xpPjxsaT5jZXJ0YWludHkgKE9wdGlvbmFsW2Zsb2F0XSk8L2xpPjwvdWw+PC9pPjwvc21hbGw+Il06Ojpjb21wb25lbnQgLS4gImRvY3VtZW50cyAtPiBkb2N1bWVudHMgKG9wdC4pPGJyPjxzbWFsbD48aT5MaXN0W0RvY3VtZW50XTwvaT48L3NtYWxsPiIgLi0+IHByb21wdF9idWlsZGVyWyI8Yj5wcm9tcHRfYnVpbGRlcjwvYj48YnI+PHNtYWxsPjxpPlByb21wdEJ1aWxkZXI8YnI+PGJyPk9wdGlvbmFsIGlucHV0czo8dWwgc3R5bGU9J3RleHQtYWxpZ246bGVmdDsnPjxsaT5xdWVzdGlvbiAoQW55KTwvbGk+PGxpPnRlbXBsYXRlIChPcHRpb25hbFtzdHJdKTwvbGk+PGxpPnRlbXBsYXRlX3ZhcmlhYmxlcyAoT3B0aW9uYWxbRGljdFtzdHIsIEFueV1dKTwvbGk+PC91bD48L2k+PC9zbWFsbD4iXTo6OmNvbXBvbmVudApwcm9tcHRfYnVpbGRlclsiPGI+cHJvbXB0X2J1aWxkZXI8L2I+PGJyPjxzbWFsbD48aT5Qcm9tcHRCdWlsZGVyPGJyPjxicj5PcHRpb25hbCBpbnB1dHM6PHVsIHN0eWxlPSd0ZXh0LWFsaWduOmxlZnQ7Jz48bGk+cXVlc3Rpb24gKEFueSk8L2xpPjxsaT50ZW1wbGF0ZSAoT3B0aW9uYWxbc3RyXSk8L2xpPjxsaT50ZW1wbGF0ZV92YXJpYWJsZXMgKE9wdGlvbmFsW0RpY3Rbc3RyLCBBbnldXSk8L2xpPjwvdWw+PC9pPjwvc21hbGw+Il06Ojpjb21wb25lbnQgLS0gInByb21wdCAtPiBwcm9tcHQ8YnI+PHNtYWxsPjxpPnN0cjwvaT48L3NtYWxsPiIgLS0+IGxsbVsiPGI+bGxtPC9iPjxicj48c21hbGw+PGk+T3BlbkFJR2VuZXJhdG9yPGJyPjxicj5PcHRpb25hbCBpbnB1dHM6PHVsIHN0eWxlPSd0ZXh0LWFsaWduOmxlZnQ7Jz48bGk+c3lzdGVtX3Byb21wdCAoT3B0aW9uYWxbc3RyXSk8L2xpPjxsaT5zdHJlYW1pbmdfY2FsbGJhY2sgKE9wdGlvbmFsW0NhbGxhYmxlW11dKTwvbGk+PGxpPmdlbmVyYXRpb25fa3dhcmdzIChPcHRpb25hbFtEaWN0W3N0ciwgQW55XV0pPC9saT48L3VsPjwvaT48L3NtYWxsPiJdOjo6Y29tcG9uZW50Cml7JmFzdDt9LS0idGV4dDxicj48c21hbGw+PGk+c3RyPC9pPjwvc21hbGw+Ii0tPiB0ZXh0X2VtYmVkZGVyWyI8Yj50ZXh0X2VtYmVkZGVyPC9iPjxicj48c21hbGw+PGk+T3BlbkFJVGV4dEVtYmVkZGVyPC9pPjwvc21hbGw+Il06Ojpjb21wb25lbnQKdGV4dF9lbWJlZGRlclsiPGI+dGV4dF9lbWJlZGRlcjwvYj48YnI+PHNtYWxsPjxpPk9wZW5BSVRleHRFbWJlZGRlcjwvaT48L3NtYWxsPiJdOjo6Y29tcG9uZW50LS0ibWV0YTxicj48c21hbGw+PGk+RGljdFtzdHIsIEFueV08L2k+PC9zbWFsbD4iLS0+IG97JmFzdDt9CmxsbVsiPGI+bGxtPC9iPjxicj48c21hbGw+PGk+T3BlbkFJR2VuZXJhdG9yPGJyPjxicj5PcHRpb25hbCBpbnB1dHM6PHVsIHN0eWxlPSd0ZXh0LWFsaWduOmxlZnQ7Jz48bGk+c3lzdGVtX3Byb21wdCAoT3B0aW9uYWxbc3RyXSk8L2xpPjxsaT5zdHJlYW1pbmdfY2FsbGJhY2sgKE9wdGlvbmFsW0NhbGxhYmxlW11dKTwvbGk+PGxpPmdlbmVyYXRpb25fa3dhcmdzIChPcHRpb25hbFtEaWN0W3N0ciwgQW55XV0pPC9saT48L3VsPjwvaT48L3NtYWxsPiJdOjo6Y29tcG9uZW50LS0icmVwbGllczxicj48c21hbGw+PGk+TGlzdFtzdHJdPC9pPjwvc21hbGw+Ii0tPiBveyZhc3Q7fQpsbG1bIjxiPmxsbTwvYj48YnI+PHNtYWxsPjxpPk9wZW5BSUdlbmVyYXRvcjxicj48YnI+T3B0aW9uYWwgaW5wdXRzOjx1bCBzdHlsZT0ndGV4dC1hbGlnbjpsZWZ0Oyc+PGxpPnN5c3RlbV9wcm9tcHQgKE9wdGlvbmFsW3N0cl0pPC9saT48bGk+c3RyZWFtaW5nX2NhbGxiYWNrIChPcHRpb25hbFtDYWxsYWJsZVtdXSk8L2xpPjxsaT5nZW5lcmF0aW9uX2t3YXJncyAoT3B0aW9uYWxbRGljdFtzdHIsIEFueV1dKTwvbGk+PC91bD48L2k+PC9zbWFsbD4iXTo6OmNvbXBvbmVudC0tIm1ldGE8YnI+PHNtYWxsPjxpPkxpc3RbRGljdFtzdHIsIEFueV1dPC9pPjwvc21hbGw+Ii0tPiBveyZhc3Q7fQoKY2xhc3NEZWYgY29tcG9uZW50IHRleHQtYWxpZ246Y2VudGVyOwo=?type=png

query = "What happens in the film 'A Very Weaviate Christmas'?"
reply = rag.run(
    {"text_embedder": {"text": query}, "prompt_builder": {"question": query}}
)
INFO:haystack.core.pipeline.pipeline:Running component text_embedder
INFO:haystack.core.pipeline.pipeline:Running component retriever
INFO:haystack.core.pipeline.pipeline:Running component prompt_builder
INFO:haystack.core.pipeline.pipeline:Running component llm
reply
{'text_embedder': {'meta': {'model': 'text-embedding-3-small',
   'usage': {'prompt_tokens': 13, 'total_tokens': 13}}},
 'llm': {'replies': ['In the film \'A Very Weaviate Christmas,\' we follow the adventures of two of Santa\'s elves, Daniel and Philip, as they embark on a mission to recover stolen vectors that have been hidden by an intruder in an unknown Collection. The elves race against time to find what is concealed in "Santas_Grotto" before Christmas Day arrives. The story is filled with excitement and holiday spirit as they navigate challenges and obstacles to ensure that the Christmas magic is preserved. Starring the Weaviate DevRel and Growth teams, the film blends festive themes with a thrilling quest.'],
  'meta': [{'model': 'gpt-4o-mini-2024-07-18',
    'index': 0,
    'finish_reason': 'stop',
    'usage': {'completion_tokens': 119,
     'prompt_tokens': 755,
     'total_tokens': 874,
     'completion_tokens_details': CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0),
     'prompt_tokens_details': PromptTokensDetails(audio_tokens=0, cached_tokens=0)}}]}}
from IPython.display import display, Markdown

display(Markdown(reply["llm"]["replies"][0]))

In the film ‘A Very Weaviate Christmas,’ we follow the adventures of two of Santa’s elves, Daniel and Philip, as they embark on a mission to recover stolen vectors that have been hidden by an intruder in an unknown Collection. The elves race against time to find what is concealed in “Santas_Grotto” before Christmas Day arrives. The story is filled with excitement and holiday spirit as they navigate challenges and obstacles to ensure that the Christmas magic is preserved. Starring the Weaviate DevRel and Growth teams, the film blends festive themes with a thrilling quest.

4) Solve the Mystery

By this point, you should know what’s happening.. There is a Collection where everything has been hidden.

Complete the code cell below by providing the right Collection name, and tell us the following:

  1. Who is the culprit? Watch out, because there may be decoys.
  2. What have they stolen?

💚 Hint: Once you’ve connected to the right collection, take a look at all the Objects in there. Then, you may be able to use filters to avoid the decoys!

import weaviate

from weaviate.classes.init import Auth

headers = {"X-OpenAI-Api-Key": os.getenv("OPENAI_API_KEY")}
client = weaviate.connect_to_weaviate_cloud(
    cluster_url="https://zgvjwlycsr6p5j1ziuyea.c0.europe-west3.gcp.weaviate.cloud",
    auth_credentials=Auth.api_key(os.getenv("WEAVIATE_API_KEY")),
    headers=headers,
)
client.collections.list_all().keys()
dict_keys(['Default', 'Santas_Grotto'])
# Provide the name of the collection in client.collections.get() below 👇
plot = client.collections.get("Santas_Grotto")
plot | wat
str: <weaviate.Collection config={
  "name": "Santas_Grotto",
  "description": null,
  "generative_config": null,
  "inverted_index_config": {
    "bm25": {
      "b": 0.75,
      "k1": 1.2
    },
    "cleanup_interval_seconds": 60,
    "index_null_state": false,
    "index_property_length": false,
    "index_timestamps": false,
    "stopwords": {
      "preset": "en",
      "additions": null,
      "removals": null
    }
  },
  "multi_tenancy_config": {
    "enabled": false,
    "auto_tenant_creation": false,
    "auto_tenant_activation": false
  },
  "properties": [
    {
      "name": "plot",
      "description": null,
      "data_type": "text",
      "index_filterable": true,
      "index_range_filters": false,
      "index_searchable": true,
      "nested_properties": null,
      "tokenization": "word",
      "vectorizer_config": {
        "skip": false,
        "vectorize_property_name": true
      },
      "vectorizer": "text2vec-openai"
    },
    {
      "name": "decoy",
      "description": null,
      "data_type": "boolean",
      "index_filterable": true,
      "index_range_filters": false,
      "index_searchable": false,
      "nested_properties": null,
      "tokenization": null,
      "vectorizer_config": {
        "skip": false,
        "vectorize_property_name": true
      },
      "vectorizer": "text2vec-openai"
    }
  ],
  "references": [],
  "replication_config": {
    "factor": 3,
    "async_enabled": false,
    "deletion_strategy": "DeleteOnConflict"
  },
  "reranker_config": null,
  "sharding_config": {
    "virtual_per_physical": 128,
    "desired_count": 3,
    "actual_count": 3,
    "desired_virtual_count": 384,
    "actual_virtual_count": 384,
    "key": "_id",
    "strategy": "hash",
    "function": "murmur3"
  },
  "vector_index_config": {
    "quantizer": null,
    "cleanup_interval_seconds": 300,
    "distance_metric": "cosine",
    "dynamic_ef_min": 100,
    "dynamic_ef_max": 500,
    "dynamic_ef_factor": 8,
    "ef": -1,
    "ef_construction": 128,
    "filter_strategy": "sweeping",
    "flat_search_cutoff": 40000,
    "max_connections": 32,
    "skip": false,
    "vector_cache_max_objects": 1000000000000
  },
  "vector_index_type": "hnsw",
  "vectorizer_config": {
    "vectorizer": "text2vec-openai",
    "model": {
      "baseURL": "https://api.openai.com",
      "model": "text-embedding-3-small"
    },
    "vectorize_collection_name": true
  },
  "vectorizer": "text2vec-openai",
  "vector_config": null
}>
repr: <weaviate.collections.collection.sync.Collection object at 0x7e052d4109a0>
type: weaviate.collections.collection.sync.Collection
parents: weaviate.collections.collection.base._CollectionBase, typing.Generic
len: 3

Public attributes:
  aggregate: weaviate.collections.aggregate._AggregateCollection = <weaviate.collections.aggregate._AggregateCollection object at 0x7e052d413820>…
  backup: weaviate.collections.backups.sync._CollectionBackup = <weaviate.collections.backups.sync._CollectionBackup object at 0x7e052d410550>…
  batch: weaviate.collections.batch.collection._BatchCollectionWrapper = <weaviate.collections.batch.collection._BatchCollectionWrapper object at 0x7e052d4117b…
  config: weaviate.collections.config.sync._ConfigCollection = <weaviate.collections.config.sync._ConfigCollection object at 0x7e052d410fd0>[…
  consistency_level: NoneType = None
  data: weaviate.collections.data.sync._DataCollection = <weaviate.collections.data.sync._DataCollection object at 0x7e052d4100a0>
  generate: weaviate.collections.generate._GenerateCollection = <weaviate.collections.generate._GenerateCollection object at 0x7e052d412e30>…
  name: str = 'Santas_Grotto'
  query: weaviate.collections.query._QueryCollection = <weaviate.collections.query._QueryCollection object at 0x7e052d412dd0>
  tenant: NoneType = None
  tenants: weaviate.collections.tenants.sync._Tenants = <weaviate.collections.tenants.sync._Tenants object at 0x7e052d413d90>

  def exists() -> bool # Check if the collection exists in Weaviate.
  def iterator(include_vector: bool = False, return_metadata: Union[List[Literal['creation_time', 'last_update_time', 'distance', 'certainty', 'score', 'explain_score', 'is_consistent']], weaviate.collections.classes.grpc.MetadataQuery, NoneType] = None, *, return_properties: Union[Sequence[Union[str, weaviate.collections.classes.grpc.QueryNested]], str, weaviate.collections.classes.grpc.QueryNested, bool, Type[~TProperties], NoneType] = None, return_references: Union[weaviate.collections.classes.grpc._QueryReference, Sequence[weaviate.collections.classes.grpc._QueryReference], Type[~TReferences], NoneType] = None, after: Union[str, uuid.UUID, NoneType] = None, cache_size: Optional[int] = None) -> Union[weaviate.collections.iterator._ObjectIterator[~Properties, ~References], weaviate.collections.iterator._ObjectIterator[~Properties, Mapping[str, weaviate.collections.classes.internal._CrossReference[Mapping[str, Union[NoneType, str, bool, int, float, datetime.datetime, uuid.UUID, weaviate.collections.classes.types.GeoCoordinate, weaviate.collections.classes.types.PhoneNumber, weaviate.collections.classes.types._PhoneNumber, Mapping[str, ForwardRef('WeaviateField')], Sequence[str], Sequence[bool], Sequence[int], Sequence[float], Sequence[datetime.datetime], Sequence[uuid.UUID], Sequence[Mapping[str, ForwardRef('WeaviateField')]]]], ForwardRef('CrossReferences')]]], weaviate.collections.iterator._ObjectIterator[~Properties, ~TReferences], weaviate.collections.iterator._ObjectIterator[~TProperties, ~References], weaviate.collections.iterator._ObjectIterator[~TProperties, Mapping[str, weaviate.collections.classes.internal._CrossReference[Mapping[str, Union[NoneType, str, bool, int, float, datetime.datetime, uuid.UUID, weaviate.collections.classes.types.GeoCoordinate, weaviate.collections.classes.types.PhoneNumber, weaviate.collections.classes.types._PhoneNumber, Mapping[str, ForwardRef('WeaviateField')], Sequence[str], Sequence[bool], Sequence[int], Sequence[float], Sequence[datetime.datetime], Sequence[uuid.UUID], Sequence[Mapping[str, ForwardRef('WeaviateField')]]]], ForwardRef('CrossReferences')]]], weaviate.collections.iterator._ObjectIterator[~TProperties, ~TReferences]] # Use this method to return an iterator over the objects in the collection.…
  def shards() -> List[weaviate.collections.classes.cluster.Shard] # Get the statuses of all the shards of this collection.…
  def with_consistency_level(consistency_level)
  def with_tenant(tenant)

Private attributes:
  _CollectionBase__consistency_level: NoneType = None
  _CollectionBase__properties: NoneType = None
  _CollectionBase__references: NoneType = None
  _CollectionBase__tenant: NoneType = None
  _Collection__cluster: weaviate.collections.cluster.sync._Cluster = <weaviate.collections.cluster.sync._Cluster object at 0x7e052d411300>
  _config: weaviate.collections.config.config._ConfigCollectionAsync = <weaviate.collections.config.config._ConfigCollectionAsync object at 0x7e052d413b80…
  _connection: weaviate.connect.v4.ConnectionV4 = <weaviate.connect.v4.ConnectionV4 object at 0x7e052d80fa30>
  _is_protocol: bool = False
  _query: weaviate.collections.query._QueryCollectionAsync = <weaviate.collections.query._QueryCollectionAsync object at 0x7e052d4109d0>
  _validate_arguments: bool = True
for item in plot.iterator():
    print(item.uuid, item.properties)
2713b638-12fd-48ea-99d1-0a852a7cf241 {'plot': 'Tuana is here with not just all the vectors but also all the presents that are supposed to be delivered around the World!', 'decoy': False}
8139bf36-d69e-41f1-bc01-a80d188995f9 {'plot': "Sebastian is here, but he seems unsure what's going on", 'decoy': True}
b1612fad-dfd2-44b1-8f20-0ac6b2786243 {'plot': "JP is here, looks like he's feasting on cookies", 'decoy': True}
from weaviate.classes.query import Filter

response = plot.query.fetch_objects(
    filters=Filter.by_property("decoy").equal(False), limit=3
)
response
QueryReturn(objects=[Object(uuid=_WeaviateUUIDInt('2713b638-12fd-48ea-99d1-0a852a7cf241'), metadata=MetadataReturn(creation_time=None, last_update_time=None, distance=None, certainty=None, score=None, explain_score=None, is_consistent=None, rerank_score=None), properties={'plot': 'Tuana is here with not just all the vectors but also all the presents that are supposed to be delivered around the World!', 'decoy': False}, references=None, vector={}, collection='Santas_Grotto')])

Q: Who is the culprit?
Ans: Tuana is the culprit (Sebastian and JP are the decoys!)

Q: What have they stolen?
Ans: All the vectors and all the presents that are supposed to be delivered around the World