Skip to content
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
86 changes: 86 additions & 0 deletions docs/guides/Postgres.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,92 @@ Use the memory helper when you need stateful chat sessions, per-user progressive
auditable interaction logs. Set `recall_limit` to bound the amount of context loaded back into
generation pipelines.

## VectorIndex implementation (pgvector)

The `PostgresVectorIndex` helper persists dense embeddings in Postgres using the
[pgvector](https://github.com/pgvector/pgvector) extension. Each indexed `DataModel` maps to its own
table, derived by converting the model class name to snake case (for example, `Chunk` → `chunk`).

### Enable pgvector and create tables

Install the extension once per database and create a table for every data model you plan to index.
The implementation expects three columns and manages timestamps automatically. For a `Chunk` model
the migration could look like this (adjust the vector dimension to match your embedding provider):

```sql
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE chunk (
id UUID NOT NULL DEFAULT gen_random_uuid(),
embedding VECTOR(1536) NOT NULL,
payload JSONB NOT NULL,
meta JSONB NOT NULL,
created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- Optional ANN index (requires pgvector >= 0.4.0)
CREATE INDEX IF NOT EXISTS chunk_embedding_idx
ON chunk
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
```

The helper stores the serialized `DataModel` instance inside `payload`, so the JSON schema mirrors
the model definition. It writes monotonically increasing `created` timestamps to preserve insertion
order for non-similarity queries.

### Wiring the index

Construct the index with `PostgresVectorIndex()` and reuse the shared `Postgres` state inside an
active context scope. The optional `mmr_multiplier` argument controls how many rows are fetched
before applying Maximal Marginal Relevance re-ranking when `rerank=True`.

```python
from collections.abc import Sequence
from draive import ctx
from draive.parameters import DataModel, Field
from draive.postgres import PostgresConnectionPool, PostgresVectorIndex
from draive.utils import VectorIndex


class Chunk(DataModel):
identifier: str = Field(alias="id")
text: str


async with ctx.scope(
"pgvector-demo",
PostgresVectorIndex(),
disposables=(
PostgresConnectionPool.of(dsn="postgresql://draive:secret@localhost:5432/draive"),
),
):
await VectorIndex.index(
Chunk,
values=[Chunk(identifier="doc-1", text="hello world")],
attribute=Chunk._.text,
)

results: Sequence[Chunk] = await VectorIndex.search(
Chunk,
query="hello",
limit=3,
score_threshold=0.6, # optional cosine similarity cutoff
rerank=True,
)
```

Queries can be strings, `TextContent`, `ResourceContent` (text or image), or pre-computed vectors.
When `score_threshold` is provided the helper converts it to the cosine distance cutoff used by
pgvector. Set `rerank=False` to return rows ordered solely by the database similarity operator.

### Payload filtering and requirements

Search and deletion accept `AttributeRequirement` instances which are evaluated against the stored
payload JSON. Requirements are translated to SQL expressions (for example, `AttributeRequirement.equal`
becomes `payload #>> '{text}' = $2`). Unsupported operators raise `NotImplementedError`, ensuring the
query surface remains explicit.

## Putting it together

Combine these adapters with higher-level Draive components to centralise operational data in
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "uv_build"
[project]
name = "draive"
description = "Framework designed to simplify and accelerate the development of LLM-based applications."
version = "0.87.8"
version = "0.88.0"
readme = "README.md"
maintainers = [
{ name = "Kacper Kaliński", email = "kacper.kalinski@miquido.com" },
Expand Down
2 changes: 2 additions & 0 deletions src/draive/postgres/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from draive.postgres.configuration import PostgresConfigurationRepository
from draive.postgres.instructions import PostgresInstructionsRepository
from draive.postgres.memory import PostgresModelMemory
from draive.postgres.vector_index import PostgresVectorIndex

__all__ = (
"Postgres",
Expand All @@ -21,4 +22,5 @@
"PostgresModelMemory",
"PostgresRow",
"PostgresValue",
"PostgresVectorIndex",
)
Loading