> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lancedb.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Managing Embeddings

> Use the embedding API in LanceDB -- registry, functions, schemas, and multi-language SDK support.

export const RsEmbeddingFunction = "use std::{borrow::Cow, sync::Arc};\n\nuse arrow_array::{Array, FixedSizeListArray, Float32Array};\nuse arrow_schema::{DataType, Field, Schema};\nuse lancedb::{\n    connect,\n    embeddings::{EmbeddingDefinition, EmbeddingFunction},\n    Result,\n};\n\n#[derive(Debug, Clone)]\nstruct MyTextEmbedder {\n    dim: usize,\n}\n\nimpl EmbeddingFunction for MyTextEmbedder {\n    fn name(&self) -> &str {\n        \"my-embedder\"\n    }\n\n    fn source_type(&self) -> Result<Cow<'_, DataType>> {\n        Ok(Cow::Owned(DataType::Utf8))\n    }\n\n    fn dest_type(&self) -> Result<Cow<'_, DataType>> {\n        Ok(Cow::Owned(DataType::new_fixed_size_list(\n            DataType::Float32,\n            self.dim as i32,\n            true,\n        )))\n    }\n\n    fn compute_source_embeddings(&self, source: Arc<dyn Array>) -> Result<Arc<dyn Array>> {\n        let values = Arc::new(Float32Array::from(vec![1.0f32; source.len() * self.dim]));\n        let field = Arc::new(Field::new(\"item\", DataType::Float32, true));\n        Ok(Arc::new(FixedSizeListArray::new(\n            field,\n            self.dim as i32,\n            values,\n            None,\n        )))\n    }\n\n    fn compute_query_embeddings(&self, _input: Arc<dyn Array>) -> Result<Arc<dyn Array>> {\n        unimplemented!()\n    }\n}\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    let db = connect(\"./mydb\").execute().await?;\n    db.embedding_registry()\n        .register(\"my-embedder\", Arc::new(MyTextEmbedder { dim: 3 }))?;\n\n    let schema = Arc::new(Schema::new(vec![Field::new(\"text\", DataType::Utf8, false)]));\n    db.create_empty_table(\"mytable\", schema)\n        .add_embedding(EmbeddingDefinition::new(\n            \"text\",\n            \"my-embedder\",\n            Some(\"vector\"),\n        ))?\n        .execute()\n        .await?;\n\n    Ok(())\n}\n";

export const RsManualQuerySearch = "// query_vector is assumed to already be generated by your embedding function\nlet mut results = table.vector_search(query_vector)?.limit(5).execute().await?;\n\nwhile let Some(batch) = results.next().await {\n    println!(\"{:?}\", batch?);\n}\n";

export const RsCreateEmbeddingFunction = "use std::sync::Arc;\n\nuse lancedb::embeddings::openai::OpenAIEmbeddingFunction;\n\nlet api_key = std::env::var(\"OPENAI_API_KEY\").expect(\"OPENAI_API_KEY is not set\");\nlet embedding = Arc::new(\n    OpenAIEmbeddingFunction::new_with_model(api_key, \"text-embedding-3-small\")\n        .expect(\"failed to create OpenAI embedding function\"),\n);\n";

export const RsOpenaiEmbeddings = "use std::{iter::once, sync::Arc};\n\nuse arrow_array::{record_batch, StringArray};\nuse arrow_schema::{DataType, Field, Schema};\nuse futures::StreamExt;\nuse lancedb::{\n    connect,\n    embeddings::{openai::OpenAIEmbeddingFunction, EmbeddingDefinition, EmbeddingFunction},\n    query::{ExecutableQuery, QueryBase},\n    Result,\n};\n\n#[tokio::main]\nasync fn main() -> Result<()> {\n    let db = connect(\"./mydb\").execute().await?;\n    let api_key = std::env::var(\"OPENAI_API_KEY\").expect(\"OPENAI_API_KEY is not set\");\n    let embedding = Arc::new(OpenAIEmbeddingFunction::new_with_model(\n        api_key,\n        \"text-embedding-3-large\",\n    )?);\n\n    db.embedding_registry().register(\"openai\", embedding.clone())?;\n\n    let schema = Arc::new(Schema::new(vec![Field::new(\"text\", DataType::Utf8, false)]));\n    let table = db\n        .create_empty_table(\"mytable\", schema)\n        .add_embedding(EmbeddingDefinition::new(\"text\", \"openai\", Some(\"vector\")))?\n        .execute()\n        .await?;\n\n    table\n        .add(record_batch!((\"text\", Utf8, [\"This is a test.\", \"Another example.\"]))?)\n        .execute()\n        .await?;\n\n    let query = Arc::new(StringArray::from_iter_values(once(\"test example\")));\n    let query_vector = embedding.compute_query_embeddings(query)?;\n    let mut results = table.vector_search(query_vector)?.limit(5).execute().await?;\n\n    while let Some(batch) = results.next().await {\n        println!(\"{:?}\", batch?);\n    }\n\n    Ok(())\n}\n";

export const TsRegisterSecret = "const registry = getRegistry();\nregistry.setVar(\"api_key\", \"sk-...\");\n\nconst func = registry.get(\"openai\")!.create({\n  apiKey: \"$var:api_key\",\n});\n";

export const TsRegisterModelFallback = "const registry = getRegistry();\nregistry.setVar(\"openai_model\", \"text-embedding-3-large\");\n\nconst func = registry.get(\"openai\")!.create({\n  model: \"$var:openai_model:text-embedding-3-small\",\n});\n";

export const TsEmbeddingFunction = "const db = await lancedb.connect(databaseDir);\n\n@register(\"my_embedding\")\nclass MyEmbeddingFunction extends EmbeddingFunction<string> {\n  constructor(optionsRaw = {}) {\n    super();\n    const options = this.resolveVariables(optionsRaw);\n    // Initialize using options\n  }\n  ndims() {\n    return 3;\n  }\n  protected getSensitiveKeys(): string[] {\n    return [];\n  }\n  embeddingDataType(): Float {\n    return new Float32();\n  }\n  async computeQueryEmbeddings(_data: string) {\n    // This is a placeholder for a real embedding function\n    return [1, 2, 3];\n  }\n  async computeSourceEmbeddings(data: string[]) {\n    // This is a placeholder for a real embedding function\n    return Array.from({ length: data.length }).fill([\n      1, 2, 3,\n    ]) as number[][];\n  }\n}\n\nconst func = new MyEmbeddingFunction();\n\nconst data = [{ text: \"pepperoni\" }, { text: \"pineapple\" }];\n\n// Option 1: manually specify the embedding function\nconst table = await db.createTable(\"vectors\", data, {\n  embeddingFunction: {\n    function: func,\n    sourceColumn: \"text\",\n    vectorColumn: \"vector\",\n  },\n  mode: \"overwrite\",\n});\n\n// Option 2: provide the embedding function through a schema\n\nconst schema = LanceSchema({\n  text: func.sourceField(new Utf8()),\n  vector: func.vectorField(),\n});\n\nconst table2 = await db.createTable(\"vectors2\", data, {\n  schema,\n  mode: \"overwrite\",\n});\n";

export const TsManualQuerySearch = "// queryVector is assumed to already be generated by your embedding function\nconst actual = (await tbl.search(queryVector).limit(1).toArray())[0];\n";

export const TsCreateEmbeddingFunction = "const func = getRegistry().get(\"openai\")!.create({\n  model: \"text-embedding-3-small\",\n});\n";

export const TsOpenaiEmbeddings = "const db = await lancedb.connect(databaseDir);\nconst func = getRegistry()\n  .get(\"openai\")\n  ?.create({ model: \"text-embedding-ada-002\" }) as EmbeddingFunction;\n\nconst wordsSchema = LanceSchema({\n  text: func.sourceField(new Utf8()),\n  vector: func.vectorField(),\n});\nconst tbl = await db.createEmptyTable(\"words\", wordsSchema, {\n  mode: \"overwrite\",\n});\nawait tbl.add([{ text: \"hello world\" }, { text: \"goodbye world\" }]);\n\nconst query = \"greetings\";\nconst actual = (await tbl.search(query).limit(1).toArray())[0];\n";

export const PyRegisterSecret = "registry = get_registry()\nregistry.set_var(\"api_key\", \"sk-...\")\n\nfunc = registry.get(\"openai\").create(api_key=\"$var:api_key\")\n";

export const PyRegisterDevice = "import torch\n\nregistry = get_registry()\nif torch.cuda.is_available():\n    registry.set_var(\"device\", \"cuda\")\n\nfunc = registry.get(\"huggingface\").create(device=\"$var:device:cpu\")\n";

export const PyEmbeddingFunction = "from functools import cached_property\n\nfrom lancedb.embeddings import TextEmbeddingFunction, register\n\nclass MyEmbeddingModel:\n    def __init__(self, model_name: str):\n        self.model_name = model_name\n\n    def encode(self, texts: list[str]) -> list[list[float]]:\n        return [[1.0, 2.0, 3.0] for _ in texts]\n\n@register(\"my-embedder\")\nclass MyTextEmbedder(TextEmbeddingFunction):\n    model_name: str = \"my-model\"\n\n    def generate_embeddings(self, texts: list[str]) -> list[list[float]]:\n        # Your embedding logic here\n        return self._model.encode(texts)\n\n    def ndims(self) -> int:\n        # Return the dimensionality of the embeddings\n        return len(self.generate_embeddings([\"test\"])[0])\n\n    @cached_property\n    def _model(self) -> MyEmbeddingModel:\n        # Initialize your model once\n        return MyEmbeddingModel(self.model_name)\n";

export const PyManualQuerySearch = "# query_vector is assumed to already be generated by your embedding function\nactual = table.search(query_vector).limit(1).to_pydantic(Words)[0]\nprint(actual.text)\n";

export const PyCreateEmbeddingFunction = "func = get_registry().get(\"openai\").create(\n    name=\"text-embedding-3-small\",\n    max_retries=7,\n)\n";

export const PyOpenaiEmbeddings = "db = lancedb.connect(\"/tmp/db\")\nfunc = get_registry().get(\"openai\").create(name=\"text-embedding-ada-002\")\n\nclass Words(LanceModel):\n    text: str = func.SourceField()\n    vector: Vector(func.ndims()) = func.VectorField()\n\ntable = db.create_table(\"words\", schema=Words, mode=\"overwrite\")\ntable.add([{\"text\": \"hello world\"}, {\"text\": \"goodbye world\"}])\n\nquery = \"greetings\"\nactual = table.search(query).limit(1).to_pydantic(Words)[0]\nprint(actual.text)\n";

Modern machine learning models can be trained to convert raw data into embeddings, which are vectors
of floating point numbers. The position of an embedding in vector space captures the semantics of
the data, so vectors that are close to each other are considered similar.

LanceDB provides an embedding function registry in OSS as well as its Enterprise versions
([see below](#embeddings-in-lancedb-enterprise))
that automatically generates vector embeddings during data ingestion. Automatic query-time embedding
generation is available in LanceDB OSS, with SDK-specific query ergonomics. The API abstracts
embedding generation, allowing you to focus on your application logic.

## Embedding Registry

You can get a supported embedding function from the registry, and then use it in your table schema.
Once configured, the embedding function will automatically generate embeddings when you insert data
into the table. Query-time behavior depends on SDK: Python/TypeScript can query with text directly,
while Rust examples typically compute query embeddings explicitly before vector search.

<CodeGroup>
  <CodeBlock filename="Python" language="python" icon="python">
    {PyOpenaiEmbeddings}
  </CodeBlock>

  <CodeBlock filename="TypeScript" language="typescript" icon="square-js">
    {TsOpenaiEmbeddings}
  </CodeBlock>

  <CodeBlock filename="Rust" language="rust" icon="rust">
    {RsOpenaiEmbeddings}
  </CodeBlock>
</CodeGroup>

### Using an embedding function

Create an embedding function before you attach it to table or schema metadata. Python and TypeScript fetch
provider implementations from the embedding registry, while Rust constructs the provider embedding function
directly and registers it on the connection before using it in an `EmbeddingDefinition`.

<CodeGroup>
  <CodeBlock filename="Python" language="python" icon="python">
    {PyCreateEmbeddingFunction}
  </CodeBlock>

  <CodeBlock filename="TypeScript" language="typescript" icon="square-js">
    {TsCreateEmbeddingFunction}
  </CodeBlock>

  <CodeBlock filename="Rust" language="rust" icon="rust">
    {RsCreateEmbeddingFunction}
  </CodeBlock>
</CodeGroup>

Provider configuration is SDK-specific, so copy the option names from the provider page for the SDK you use.
For example, the OpenAI model is selected with `name` in Python, `model` in TypeScript, and the model argument
to `OpenAIEmbeddingFunction::new_with_model` in Rust.

| Concept     | Python                                             | TypeScript                                         | Rust                                                |
| ----------- | -------------------------------------------------- | -------------------------------------------------- | --------------------------------------------------- |
| Model       | `name="text-embedding-3-small"`                    | `{ model: "text-embedding-3-small" }`              | `new_with_model(api_key, "text-embedding-3-small")` |
| Retry count | `max_retries=7`                                    | Provider/client-specific                           | Provider/client-specific                            |
| API key     | `api_key="..."`, environment variables, or `$var:` | `apiKey: "..."`, environment variables, or `$var:` | Constructor argument or environment variable        |
| Device      | Provider-specific, for example `device="cuda"`     | Provider-specific                                  | Provider-specific                                   |

For reusable runtime configuration, the registry also supports `$var:` placeholders in embedding-function config.
This is useful for provider secrets and environment-specific settings in Python and TypeScript.

* Python uses `registry.set_var(...)`.
* TypeScript uses `registry.setVar(...)`.
* You can provide a fallback with `$var:name:default`.
* Sensitive values such as API keys should be passed through registry variables instead of hardcoding them in config.

<CodeGroup>
  <CodeBlock filename="Python" language="python" icon="python">
    {PyRegisterSecret}
  </CodeBlock>

  <CodeBlock filename="TypeScript" language="typescript" icon="square-js">
    {TsRegisterSecret}
  </CodeBlock>
</CodeGroup>

For non-sensitive settings such as inference device selection, you can also use a default fallback:

<CodeGroup>
  <CodeBlock filename="Python" language="python" icon="python">
    {PyRegisterDevice}
  </CodeBlock>

  <CodeBlock filename="TypeScript" language="typescript" icon="square-js">
    {TsRegisterModelFallback}
  </CodeBlock>
</CodeGroup>

Find the full list of arguments for each provider in the [integrations](/integrations/embedding) section.

## Multiple embedding columns

A single table can include more than one embedding definition when you want to store multiple semantic views
of the same data, or generate embeddings from different source columns. In practice, each embedding definition
maps one source column to one vector column, and the table schema can contain multiple such pairs.

The exact setup differs by SDK, but the underlying pattern is the same: define a distinct source/vector pair
for each embedding function you want applied during ingest.

## Embedding model providers

LanceDB supports most popular embedding providers.

### Text embeddings

| Provider              | Model ID                | Default Model            |
| --------------------- | ----------------------- | ------------------------ |
| OpenAI                | `openai`                | `text-embedding-ada-002` |
| Sentence Transformers | `sentence-transformers` | `all-MiniLM-L6-v2`       |
| Hugging Face          | `huggingface`           | `colbert-ir/colbertv2.0` |
| Cohere                | `cohere`                | `embed-english-v3.0`     |
| ...                   | ...                     | ...                      |

### Multimodal embedding

| Provider  | Model ID    | Supported Inputs           |
| --------- | ----------- | -------------------------- |
| OpenCLIP  | `open-clip` | Text, Images               |
| ImageBind | `imagebind` | Text, Images, Audio, Video |
| ...       | ...         | ...                        |

You can find all supported embedding models in the [integrations](/integrations/embedding) section.

## Embeddings in LanceDB Enterprise

<Badge color="red">Enterprise</Badge>

In LanceDB Enterprise, embedding generation during data ingestion is client-side and the resulting vectors are
stored on the remote table.

<Info>
  The Enterprise server does not currently generate embeddings from query text on its own. Any automatic
  query-time embedding happens on the client side.
</Info>

### How string queries are interpreted

For the Python remote client, `table.search("hello")` can take two different paths:

* If the selected vector column has embedding metadata
  (i.e., the table schema stores the source-column, vector-column, and
  embedding-function mapping created from fields like `SourceField()` and
  `VectorField()` during table creation), then the embeddings are computed in the Python client process.
  The client uses the same local LanceDB embedding registry used by OSS tables to
  reconstruct the embedding function from schema metadata, compute the query vector in
  the client process, and send that vector to Enterprise for search.
  ```python theme={"theme":{"light":"vitesse-light","dark":"catppuccin-mocha"}}
  result = table.search("hello").limit(1).to_list()
  # The Python client computes the query embedding locally, then sends a vector search.
  ```
* If the table does not have embedding metadata for that search, `table.search("hello")` in `auto` mode is
  treated as an FTS query instead.
  ```python theme={"theme":{"light":"vitesse-light","dark":"catppuccin-mocha"}}
  result = table.search("hello").limit(5).to_list()
  # In auto mode this is treated as an FTS query, not a vector query.
  ```

If you want explicit vector or hybrid behavior and the client cannot resolve an embedding function from the
table metadata, generate the query embedding yourself and pass the vector directly.

The manual query-embedding flow below works across Enterprise SDKs and is an explicit path you can use when you
want full control over query-time behavior.

<CodeGroup>
  <CodeBlock filename="Python" language="python" icon="python">
    {PyManualQuerySearch}
  </CodeBlock>

  <CodeBlock filename="TypeScript" language="typescript" icon="square-js">
    {TsManualQuerySearch}
  </CodeBlock>

  <CodeBlock filename="Rust" language="rust" icon="rust">
    {RsManualQuerySearch}
  </CodeBlock>
</CodeGroup>

## Custom Embedding Functions

You can always implement your own embedding function:

* Python/TypeScript: subclass `TextEmbeddingFunction` (text) or `EmbeddingFunction` (multimodal).
* Rust: implement the `EmbeddingFunction` trait.

<CodeGroup>
  <CodeBlock filename="Python" language="python" icon="python">
    {PyEmbeddingFunction}
  </CodeBlock>

  <CodeBlock filename="TypeScript" language="typescript" icon="square-js">
    {TsEmbeddingFunction}
  </CodeBlock>

  <CodeBlock filename="Rust" language="rust" icon="rust">
    {RsEmbeddingFunction}
  </CodeBlock>
</CodeGroup>
