Skip to content
This repository was archived by the owner on May 27, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 44 additions & 9 deletions docs/vectorizer/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -982,24 +982,59 @@ This function is used to create an embedding configuration object that is passed
SELECT ai.create_vectorizer(
'my_table'::regclass,
embedding => ai.embedding_voyageai(
'voyage-3-lite',
512,
api_key_name => "TEST_API_KEY"
'voyage-3.5-lite', -- or 'voyage-3.5', 'voyage-3-large', etc.
1024, -- default dimensions for voyage-3.5 models
api_key_name => "TEST_API_KEY",
output_dimension => 512 -- Optional: use 256, 512, 1024, or 2048
),
-- other parameters...
);
```

**Example with flexible dimensions (Matryoshka embeddings):**
```sql
-- Use 256 dimensions for faster search and less storage
SELECT ai.create_vectorizer(
'articles'::regclass,
embedding => ai.embedding_voyageai(
'voyage-3-large',
1024, -- Schema dimensions
output_dimension => 256 -- Actual embedding dimensions
),
destination => ai.destination_table('articles_embeddings_256d')
);
```

#### Available Models

**Current Generation (Recommended):**
| Model | Purpose | Default Dimensions | Max Tokens/Request |
|-------|---------|-------------------|-------------------|
| `voyage-3.5-lite` | Cost & latency optimized | 1024 | 1M |
| `voyage-3.5` | General-purpose optimized | 1024 | 320K |
| `voyage-3-large` | Best for general-purpose & multilingual | 1024 | 120K |
| `voyage-code-3` | Code retrieval specialized | 1024 | 120K |
| `voyage-finance-2` | Finance domain | 1024 | 120K |
| `voyage-law-2` | Legal documents | 1024 | 120K |

**Older Models:**
| Model | Purpose | Default Dimensions | Max Tokens/Request |
|-------|---------|-------------------|-------------------|
| `voyage-3-lite` | General-purpose (older) | 512 | 120K |
| `voyage-2` | General-purpose (legacy) | - | 320K |

#### Parameters

The function takes several parameters to customize the Voyage AI embedding configuration:

| Name | Type | Default | Required | Description |
|--------------|---------|------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| model | text | - | ✔ | Specify the name of the [Voyage AI model](https://docs.voyageai.com/docs/embeddings#model-choices) to use. |
| dimensions | int | - | ✔ | Define the number of dimensions for the embedding vectors. This should match the output dimensions of the chosen model. |
| input_type | text | 'document' | ✖ | Type of the input text, null, 'query', or 'document'. |
| api_key_name | text | `VOYAGE_API_KEY` | ✖ | Set the name of the environment variable that contains the Voyage AI API key. This allows for flexible API key management without hardcoding keys in the database. On Timescale Cloud, you should set this to the name of the secret that contains the Voyage AI API key. |
| Name | Type | Default | Required | Description |
|------------------|---------|------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| model | text | - | ✔ | Specify the name of the [Voyage AI model](https://docs.voyageai.com/docs/embeddings) to use. See table above for available models. |
| dimensions | int | - | ✔ | Define the number of dimensions for the embedding vectors. This should match the output dimensions of the chosen model (typically 1024 for voyage-3.x models). |
| input_type | text | 'document' | ✖ | Type of the input text: null, 'query', or 'document'. Setting this improves retrieval quality by allowing the model to optimize the embedding. |
| api_key_name | text | `VOYAGE_API_KEY` | ✖ | Set the name of the environment variable that contains the Voyage AI API key. This allows for flexible API key management without hardcoding keys in the database. On Timescale Cloud, you should set this to the name of the secret that contains the Voyage AI API key. |
| output_dimension | int | null | ✖ | Set the output dimension for embeddings. Supports 256, 512, 1024, or 2048 for voyage-3.x models. Lower dimensions reduce storage (up to 75%) and improve search speed with minimal accuracy loss. Uses Matryoshka embeddings technique. |
| output_dtype | text | 'float' | ✖ | Set the output data type for embeddings. Options: 'float' (default), 'int8', 'uint8', 'binary', 'ubinary'. Quantized types (int8, uint8) reduce network bandwidth and API costs. Binary types (binary, ubinary) provide maximum compression with 1/8 the dimensions. Embeddings are automatically converted to float for storage in PostgreSQL. |

#### Returns

Expand Down
138 changes: 133 additions & 5 deletions docs/vectorizer/quick-start-voyage.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,69 @@ Now you can create and run a vectorizer. A vectorizer is a pgai concept, it proc
'blog'::regclass,
loading => ai.loading_column('contents'),
embedding => ai.embedding_voyageai(
'voyage-3-lite',
512
'voyage-3.5-lite', -- or 'voyage-3.5', 'voyage-3-large', 'voyage-code-3', etc.
1024 -- default dimensions for voyage-3.5-lite
),
destination => ai.destination_table('blog_contents_embeddings')
);
```

**Available Voyage AI Models:**
- `voyage-3.5-lite`: Cost & latency optimized, 1024 dims (1M tokens/request) - **Recommended**
- `voyage-3.5`: General-purpose optimized, 1024 dims (320K tokens/request)
- `voyage-3-large`: Best for general-purpose & multilingual, 1024 dims (120K tokens/request)
- `voyage-code-3`: Specialized for code retrieval, 1024 dims (120K tokens/request)
- `voyage-finance-2`: Finance domain optimized, 1024 dims
- `voyage-law-2`: Legal document optimized, 1024 dims
- `voyage-3-lite`: Older model, 512 dims (120K tokens/request)

**Flexible Dimensions (New!):**
For voyage-3.x models, you can specify `output_dimension` to reduce storage and improve performance:
```sql
-- Use 256 dimensions for 75% storage reduction
SELECT ai.create_vectorizer(
'blog'::regclass,
loading => ai.loading_column('contents'),
embedding => ai.embedding_voyageai(
'voyage-3.5-lite',
1024, -- Schema dimensions
output_dimension => 256 -- Actual embedding dimensions
),
destination => ai.destination_table('blog_embeddings_compact')
);
```

**Dimension Trade-offs:**
- **256 dims**: Fastest search, 75% less storage, minimal accuracy loss
- **512 dims**: Balanced performance and accuracy
- **1024 dims**: Default, best accuracy (recommended for most use cases)
- **2048 dims**: Maximum accuracy for complex tasks

**Quantization (New!):**
Use `output_dtype` to reduce network bandwidth and API costs:
```sql
-- Use int8 quantization for 4x bandwidth reduction
SELECT ai.create_vectorizer(
'blog'::regclass,
loading => ai.loading_column('contents'),
embedding => ai.embedding_voyageai(
'voyage-3.5-lite',
1024,
output_dtype => 'int8' -- Options: float, int8, uint8, binary, ubinary
),
destination => ai.destination_table('blog_embeddings_quantized')
);
```

**Quantization Options:**
- **float**: Default, no compression (4 bytes per dimension)
- **int8**: Integer quantization, 4x smaller transfer (~1 byte per dim)
- **uint8**: Unsigned integer quantization, 4x smaller
- **binary**: Maximum compression, 32x smaller (1 bit per dim)
- **ubinary**: Unsigned binary, 32x smaller

Note: Quantized embeddings are automatically converted to float for storage in PostgreSQL, so you get bandwidth savings but not storage savings.

1. **Check the vectorizer worker logs**
```shell
docker compose logs -f vectorizer-worker
Expand All @@ -118,7 +174,7 @@ Now you can create and run a vectorizer. A vectorizer is a pgai concept, it proc
```sql
SELECT
chunk,
embedding <=> ai.voyageai_embed('voyage-3-lite', 'good food') as distance
embedding <=> ai.voyageai_embed('voyage-3.5-lite', 'good food') as distance
FROM blog_contents_embeddings
ORDER BY distance;
```
Expand All @@ -134,6 +190,78 @@ The results look like:
| Cloud computing has revolutionized the way businesses operate... | 0.9131323552491029 |


That's it, you're done. You now have a table in Postgres that pgai vectorizer automatically creates
and syncs embeddings for. You can use this vectorizer for semantic search, RAG or any other AI
## Reranking with Voyage AI

Voyage AI also provides reranking capabilities to improve search result relevance. Reranking takes your initial search results and reorders them based on relevance to your query.

### Using the Reranker

**Basic reranking:**
```sql
SELECT *
FROM ai.voyageai_rerank_simple(
'rerank-2.5',
'What are best practices for healthy eating?',
ARRAY[
'Maintaining a healthy diet can be challenging for busy professionals...',
'Blogging can be a great way to share your thoughts and expertise...',
'PostgreSQL is a powerful, open source object-relational database system...',
'As we look towards the future, artificial intelligence continues to evolve...',
'Cloud computing has revolutionized the way businesses operate...'
],
api_key => 'your-api-key'
)
ORDER BY relevance_score DESC;
```

**Results:**
| index | document | relevance_score |
|-------|----------|-----------------|
| 0 | Maintaining a healthy diet can be challenging... | 0.9156 |
| 1 | Blogging can be a great way to share... | 0.2341 |
| 4 | Cloud computing has revolutionized... | 0.1023 |
| ... | ... | ... |

**Limit results with top_k:**
```sql
SELECT *
FROM ai.voyageai_rerank_simple(
'rerank-2.5-lite',
'healthy eating',
ARRAY['...'],
api_key => 'your-api-key',
top_k => 3
)
ORDER BY relevance_score DESC;
```

### Available Reranker Models

**Current Generation (Recommended):**
| Model | Context Length | Best For |
|-------|---------------|----------|
| `rerank-2.5` | 32K tokens | Quality with multilingual/instruction support |
| `rerank-2.5-lite` | 32K tokens | Latency & quality balance |

**Older Models:**
| Model | Context Length | Notes |
|-------|---------------|-------|
| `rerank-2` | 16K tokens | Legacy |
| `rerank-2-lite` | 8K tokens | Legacy |
| `rerank-1` | 8K tokens | Legacy |
| `rerank-lite-1` | 4K tokens | Legacy |

### Reranker vs Semantic Search

- **Semantic Search** (embeddings): Fast initial retrieval from large datasets
- **Reranking**: Precise relevance scoring for top-k results from semantic search

**Typical workflow:**
1. Use semantic search to get top 100 candidates
2. Use reranker to get the most relevant 5-10 results

---

That's it, you're done. You now have a table in Postgres that pgai vectorizer automatically creates
and syncs embeddings for. You can use this vectorizer for semantic search, RAG or any other AI
app you can think of! If you have any questions, reach out to us on [Discord](https://discord.gg/KRdHVXAmkp).
41 changes: 41 additions & 0 deletions projects/extension/ai/voyageai.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,53 @@ def embed(
api_key: str,
input_type: str | None = None,
truncation: bool | None = None,
output_dimension: int | None = None,
output_dtype: str | None = None,
) -> Generator[tuple[int, list[float]], None, None]:
client = voyageai.Client(api_key=api_key)
args = {}
if truncation is not None:
args["truncation"] = truncation
if output_dimension is not None:
args["output_dimension"] = output_dimension
if output_dtype is not None:
args["output_dtype"] = output_dtype
response = client.embed(input, model=model, input_type=input_type, **args)
if not hasattr(response, "embeddings"):
return None
yield from enumerate(response.embeddings)


def rerank(
model: str,
query: str,
documents: list[str],
api_key: str,
top_k: int | None = None,
truncation: bool | None = None,
) -> dict:
"""
Rerank documents using Voyage AI reranker API.
Returns the response as a dictionary for JSON serialization.
"""
client = voyageai.Client(api_key=api_key)
args = {}
if top_k is not None:
args["top_k"] = top_k
if truncation is not None:
args["truncation"] = truncation

response = client.rerank(model=model, query=query, documents=documents, **args)

# Convert response to dict for JSON serialization
return {
"results": [
{
"index": r.index,
"document": r.document,
"relevance_score": r.relevance_score,
}
for r in response.results
],
"total_tokens": response.total_tokens,
}
80 changes: 78 additions & 2 deletions projects/extension/sql/idempotent/017-voyageai.sql
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,87 @@ as $python$
args["input_type"] = input_type

with ai.utils.VerboseRequestTrace(plpy, "voyageai.embed()", verbose):
results = ai.voyageai.embed(model, input_texts, api_key=api_key_resolved, **args)
results = ai.voyageai.embed(model, input_texts, api_key=api_key_resolved, **args)

for tup in results:
yield tup
$python$
language plpython3u immutable parallel safe security invoker
set search_path to pg_catalog, pg_temp
;

-------------------------------------------------------------------------------
-- voyageai_rerank
-- https://docs.voyageai.com/docs/reranker
create or replace function ai.voyageai_rerank
( model text
, query text
, documents text[]
, api_key text default null
, api_key_name text default null
, top_k integer default null
, truncation boolean default null
, verbose boolean default false
) returns jsonb
as $python$
#ADD-PYTHON-LIB-DIR
import ai.voyageai
import ai.secrets
import ai.utils
import json
api_key_resolved = ai.secrets.get_secret(plpy, api_key, api_key_name, ai.voyageai.DEFAULT_KEY_NAME, SD)

args = {}
if top_k is not None:
args["top_k"] = top_k
if truncation is not None:
args["truncation"] = truncation

with ai.utils.VerboseRequestTrace(plpy, "voyageai.rerank()", verbose):
response = ai.voyageai.rerank(model, query, documents, api_key_resolved, **args)

return json.dumps(response)
$python$ language plpython3u immutable parallel safe security invoker
set search_path to pg_catalog, pg_temp
;

-------------------------------------------------------------------------------
-- voyageai_rerank_simple
-- https://docs.voyageai.com/docs/reranker
create or replace function ai.voyageai_rerank_simple
( model text
, query text
, documents text[]
, api_key text default null
, api_key_name text default null
, top_k integer default null
, truncation boolean default null
, verbose boolean default false
) returns table
( "index" int
, "document" text
, relevance_score float8
)
as $func$
select
x."index"
, d.document
, x.relevance_score
from pg_catalog.jsonb_to_recordset
(
ai.voyageai_rerank
( model
, query
, documents
, api_key=>api_key
, api_key_name=>api_key_name
, top_k=>top_k
, truncation=>truncation
, verbose=>"verbose"
) operator(pg_catalog.->) 'results'
) x("index" int, relevance_score float8)
inner join unnest(documents) with ordinality d (document, ord)
on (x."index" = (d.ord - 1))
$func$ language sql immutable parallel safe security invoker
set search_path to pg_catalog, pg_temp
;
Loading
Loading