vecs Python Client

repository·main·Indexed 43 days ago

https://github.com/supabase/vecs

A Python client for managing and querying vector stores in PostgreSQL using the pgvector extension. vecs allows developers to create collections, upsert vector records with metadata, and perform similarity searches. It supports high-performance indexing via HNSW and IVFFlat, multiple distance measures (cosine, L2, L1, and max inner product), and an Adapter system for automating text chunking and embedding using sentence-transformers.

Tokens
15.6K
Snippets
60
Records
74
Agent score
54%

What's inside vecs

  1. What are indexes and why use them?

    main

    Indexes are heuristic data structures used to optimize query performance for a collection.

    While you can query a collection without an index, doing so will emit a Python warning and is extremely slow in production because every query vector must be checked against every single record in the collection. An index pre-computes distances between key points in the vector space, allowing for much faster traversal and searching.

    Key constraints:

    • Only one index may exist per collection.
    • An index optimizes a collection for a specific distance measure.
    • Index creation time grows with the number of records and vector dimensions. Expect sub-minute responses for a few thousand records, but several minutes for larger collections.
  2. What are Adapters in vecs

    main

    Adapters are an optional feature used to transform data before it is added to or queried from a collection. They allow you to interact with collections using your preferred data type (e.g., raw text) instead of manually managing vector conversions.

    Key characteristics:

    • Lazy Transformation: Transformations are applied lazily.
    • Efficiency: They can internally batch operations, making them more memory and CPU efficient than manual transformations.
    • Pipelines: Adapters can be composed of multiple AdapterStep objects to create multi-stage preprocessing pipelines (e.g., chunking text followed by embedding it).
    • Data Interface: Adapters process data in the form of Iterable[Tuple[str, Any, Optional[Dict]]], where each tuple represents (id, media, metadata). While primarily used to transform the media part, they can also modify id or metadata.
  3. How collections map to Postgres tables

    main

    Every vecs collection is backed by a standard Postgres table where rows map 1:1 to vector records. While you can safely perform SELECT queries on these tables from outside the vecs client, you should avoid issuing DDL (Data Definition Language) commands directly to the underlying tables to prevent corruption of the collection state.

    The underlying table schema follows this pattern:

    create table <collection_name> (
        id string primary key,
        vec vector(<dimension>),
        metadata jsonb
    )
  4. Supported metadata types

    main

    Metadata is stored as binary JSON. You can use the following JSON primitive types for your metadata values:

    • Boolean
    • String
    • Number

    Note on limits and performance:

    • The technical limit for a metadata field associated with a vector is 1GB.
    • For optimal performance, keep metadata fields as small as possible and use scalar key-value pairs.
  5. Associate metadata with vectors

    main
    In vecs, you can associate key-value pairs of metadata with both indexes and IDs within your collections. This metadata is stored as binary JSON, allowing you to attach descriptive attributes to your vectors and use them as filters during queries.
  6. Understand the structure of a vector record

    main

    In vecs, a collection consists of vector records. Each record is composed of three primary fields:

    • id: A unique String identifier.
    • vec: A list of numbers (Numeric[]) representing the vector embedding.
    • metadata: A JSON object containing arbitrary key-value pairs for filtering and additional context.

    Example of a record structure: ("vec1", [0.1, 0.2, 0.3], {"year": 1990})

    # Example record representation
    ("vec1", [0.1, 0.2, 0.3], {"year": 1990})
  7. Use Adapters to handle raw text

    main

    Adapters allow you to interact with collections using native data types (like raw text) instead of manually managing embeddings. This requires installing the text embedding dependencies:

    pip install "vecs[text_embedding]"

    When a collection is initialized with an adapter, you can upsert text and query using text directly.

    import vecs
    from vecs.adapter import Adapter, ParagraphChunker, TextEmbedding
    
    # Create a collection with a text-to-embedding adapter
    docs = vx.get_or_create_collection(
        name="docs",
        adapter=Adapter(
            [
                ParagraphChunker(skip_during_query=True),
                TextEmbedding(model='all-MiniLM-L6-v2'),
            ]
        )
    )
    
    # Upsert raw text instead of vectors
    docs.upsert(
        records=[
            ("vec0", "four score and ....", {"year": 1973}),
            ("vec1", "hello, world!", {"year": "2012"})
        ]
    )
    
    # Query using raw text
    results = docs.query(data="foo bar")
  8. Query vecs for most similar sentences

    main

    To perform a similarity search, embed your query string using the same Hugging Face endpoint used for the dataset. Then, use the sentences.query() method, passing the query embedding, a limit, and optionally include_value=True to retrieve the original text/values alongside the distance.

    query_sentence = "A quick animal jumps over a lazy one."
    
    # create an embedding for the query sentence
    response = requests.post(
        huggingface_endpoint_url,
        headers={
            "Authorization": f"Bearer {huggingface_api_key}",
            "Content-Type": "application/json"
        },
        json={"inputs": query_sentence}
    )
    query_embedding = response.json()["embeddings"]
    
    # query the 'sentences' collection for the most similar sentences
    results = sentences.query(
        data=query_embedding,
        limit=3,
        include_value = True
    )
    
    # print the results
    for result in results:
        print(result)
  9. Store Hugging Face embeddings in vecs

    main

    Once you have generated embeddings, use the vecs.Client to connect to your PostgreSQL database. You can then create a collection with a specific dimension (e.g., 384 for sentence-transformers/all-MiniLM-L6-v2) and upsert your records. It is recommended to call create_index() on the collection to enable fast similarity searches.

    import vecs
    
    DB_CONNECTION = "postgresql://<user>:<password>@<host>:<port>/<db_name>"
    
    # create vector store client
    vx = vecs.Client(DB_CONNECTION)
    
    # create a collection named 'sentences' with 384 dimensional vectors
    sentences = vx.get_or_create_collection(name="sentences", dimension=384)
    
    # upsert the embeddings into the 'sentences' collection
    sentences.upsert(records=records)
    
    # create an index for the 'sentences' collection
    sentences.create_index()