Skip to main content
Pixeltable ships embedding functions for OpenAI, Hugging Face, Jina, Voyage, and more. When your model is not one of them, wrap it in a UDF and declare it in __indexes__. The index then loads with the existing rows and updates as new rows arrive.

Quick start

A UDF that returns a fixed-width array is usable as an embedding. Declare it on the model:
pxt schema update app.py my_app creates the table and the index. Query it with similarity():
Application code declares indexes in __indexes__. In a notebook or a test, call t.add_embedding_index('text', string_embed=embed) instead.

Your own model

.using() covers a built-in function with a fixed parameter. For a model Pixeltable does not ship, write the UDF yourself. The return type fixes the dimension, and the index requires it:
Declaring it is the same:

Load the model once

A UDF body runs per row. Loading weights inside it reloads them on every row. Cache the model at module scope so the cost is paid once per process:

Batch the calls

Embedding models are much faster on a batch than on single rows. A batched UDF takes and returns Batch, and Pixeltable groups rows for you up to batch_size:
The signature is the only change. The __indexes__ declaration stays the same.
This is how the built-in functions are written: pxtf.huggingface.sentence_transformer is a @pxt.udf(batch_size=32) over Batch[str].

Metric and precision

EmbeddingIndex takes metric (cosine, ip, or l2; default cosine) and precision (fp16 or fp32; default fp16). Match the metric to how your model was trained:

Additional resources

UDFs

Writing and batching UDFs

Embedding indexes

Built-in embeddings and search

Model hub

Find embedding models
Last modified on September 9, 2026