Skip to content

Commit 912d011

Browse files
pgvector examples
1 parent 3fd4d70 commit 912d011

File tree

28 files changed

+1365
-220
lines changed

28 files changed

+1365
-220
lines changed

postgres/Makefile

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ ifndef UV_VERSION
1212
UV_VERSION := 0.7.21
1313
endif
1414

15-
.PHONY: uv_check venv sync lock update format lint migrations example
15+
.PHONY: uv_check venv sync lock update format lint migrations example example-index example-generate example-delete example-search database database-stop
1616

1717

1818
# Check installed UV version and install if needed
@@ -81,3 +81,32 @@ migrations:
8181

8282
example:
8383
@python -B -m example
84+
85+
example-index:
86+
@python -B -m example.vector
87+
88+
example-generate:
89+
@python -B -m example.vector.generate $(SIZE)
90+
91+
example-search:
92+
@python -B -m example.vector.search $(QUERY)
93+
94+
example-delete:
95+
@python -B -m example.vector.delete
96+
97+
database:
98+
@echo '# Starting PostgreSQL database with pgvector...'
99+
@docker run -d \
100+
--name postgres-draive-example \
101+
-e POSTGRES_USER=$(POSTGRES_USER) \
102+
-e POSTGRES_PASSWORD=$(POSTGRES_PASSWORD) \
103+
-e POSTGRES_DB=$(POSTGRES_DATABASE) \
104+
-p $(POSTGRES_PORT):5432 \
105+
pgvector/pgvector:pg16 \
106+
|| docker start postgres-draive-example
107+
@echo '...database started!'
108+
109+
database-stop:
110+
@echo '# Stopping PostgreSQL database...'
111+
@docker stop postgres-draive-example || true
112+
@echo '...database stopped!'

postgres/pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ build-backend = "uv_build"
55
[project]
66
name = "postgres-example"
77
version = "0.1.0"
8-
requires-python = ">=3.12"
9-
dependencies = ["draive[openai, postgres]~=0.87.3"]
8+
requires-python = ">=3.13"
9+
dependencies = ["draive[openai, postgres]>=0.91.1", "pgvector"]
1010

1111
[project.urls]
1212
Homepage = "https://miquido.com"
@@ -18,7 +18,7 @@ dev = ["bandit~=1.7", "pyright~=1.1", "ruff~=0.12"]
1818
namespace = true
1919

2020
[tool.ruff]
21-
target-version = "py312"
21+
target-version = "py313"
2222
line-length = 100
2323
extend-exclude = [".venv", ".git", ".cache"]
2424
lint.select = ["E", "F", "A", "I", "B", "PL", "W", "C", "RUF", "UP"]
@@ -28,7 +28,7 @@ lint.ignore = ["A005"]
2828
"__init__.py" = ["F401", "E402"]
2929

3030
[tool.pyright]
31-
pythonVersion = "3.12"
31+
pythonVersion = "3.13"
3232
venvPath = "./.venv"
3333
include = ["./src"]
3434
exclude = ["**/node_modules", "**/__pycache__"]

postgres/src/example/__main__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
from draive import (
44
Conversation,
55
ConversationMessage,
6-
Instructions,
6+
Template,
77
ctx,
88
)
99
from draive.openai import OpenAI, OpenAIResponsesConfig
1010
from draive.postgres import (
1111
PostgresConfigurationRepository,
1212
PostgresConnectionPool,
13-
PostgresInstructionsRepository,
13+
PostgresTemplatesRepository,
1414
PostgresModelMemory,
1515
)
1616

@@ -20,8 +20,8 @@ async def main() -> None:
2020
"example",
2121
# declare postgres as configuration provider
2222
PostgresConfigurationRepository(),
23-
# declare postgres as instructons repository
24-
PostgresInstructionsRepository(),
23+
# declare postgres as templates repository
24+
PostgresTemplatesRepository(),
2525
disposables=(
2626
OpenAI(), # use OpenAI for LLM
2727
PostgresConnectionPool(), # use postgres connection pool
@@ -31,7 +31,7 @@ async def main() -> None:
3131
memory = PostgresModelMemory("example_session")
3232
await memory.maintenance() # initialize session if needed
3333
result: ConversationMessage = await Conversation.completion(
34-
instructions=Instructions.of("example"),
34+
instructions=Template.of("example"),
3535
input="Hello!",
3636
# declare postgres as conversation memory
3737
# use actual session ID to distinct multiple sessions
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from draive import load_env, setup_logging
2+
3+
load_env()
4+
setup_logging("vector_index")
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""Vector Indexing and Search Example
2+
3+
This script demonstrates a simple end-to-end example of vector indexing and semantic search
4+
using PostgreSQL with pgvector extension. It's a quick demonstration that shows both indexing
5+
and searching in a single script.
6+
7+
What it does:
8+
1. Creates 5 sample text chunks about various technologies
9+
2. Converts them into vector embeddings using OpenAI
10+
3. Indexes the chunks in PostgreSQL with pgvector
11+
4. Performs a semantic search query: "What is Python?"
12+
5. Displays the top 3 most similar results
13+
14+
Use cases:
15+
- Quick demonstration of vector search capabilities
16+
- Testing vector indexing and search functionality
17+
- Learning how to use the VectorIndex API
18+
- Smoke test for database and OpenAI connectivity
19+
20+
Usage:
21+
# Run using make
22+
make example-index
23+
24+
# Run directly with Python
25+
python -m example.vector
26+
27+
Prerequisites:
28+
- PostgreSQL database with pgvector extension running (use 'make database' to start)
29+
- OpenAI API key configured in .env file:
30+
# OpenAI: Key for draive-examples
31+
OPENAI_API_KEY=sk-proj-xxxxxxxx
32+
OPENAI_MODEL=text-embedding-3-small
33+
- Dependencies installed (use 'make venv' or 'make sync')
34+
35+
Example output:
36+
The script will:
37+
1. Index 5 sample chunks with progress message
38+
2. Perform a search for "What is Python?"
39+
3. Display the 3 most relevant results
40+
41+
For more advanced examples:
42+
- Use 'make example-generate' to index larger datasets (10 to 100,000 documents)
43+
- Use 'make example-search' to perform custom searches
44+
- Use 'make example-delete' to clean up the vector index
45+
"""
46+
47+
from asyncio import run
48+
49+
from draive import ctx
50+
from draive.openai import OpenAI
51+
from draive.postgres import PostgresConnectionPool, PostgresVectorIndex
52+
from draive.utils import VectorIndex
53+
54+
from .common.connection import build_postgres_dsn, initialize_pgvector
55+
from .common.model import Chunk
56+
57+
58+
async def main() -> None:
59+
async with ctx.scope(
60+
"index",
61+
PostgresVectorIndex(),
62+
disposables=(
63+
OpenAI(), # use OpenAI for embeddings
64+
PostgresConnectionPool.of(
65+
dsn=build_postgres_dsn(), # you can pass custom dsn string ie. "postgresql://user:password@host:port/database"
66+
initialize=initialize_pgvector,
67+
), # use postgres connection pool with vector support
68+
),
69+
):
70+
# Sample chunks to index
71+
chunks = [
72+
Chunk(text="Python is a high-level programming language"),
73+
Chunk(text="PostgreSQL is a powerful relational database"),
74+
Chunk(text="Machine learning is a subset of artificial intelligence"),
75+
Chunk(text="Docker is a containerization platform"),
76+
Chunk(text="FastAPI is a modern Python web framework"),
77+
]
78+
79+
print("Indexing chunks...")
80+
await VectorIndex.index(
81+
Chunk,
82+
values=chunks,
83+
attribute=Chunk._.text,
84+
)
85+
print(f"...indexed {len(chunks)} chunks!")
86+
87+
# Search for similar chunks
88+
query = "What is Python?"
89+
print(f"\nSearching for: '{query}'")
90+
results = await VectorIndex.search(
91+
Chunk,
92+
query=query,
93+
limit=3,
94+
score_threshold=0.0,
95+
rerank=False,
96+
)
97+
98+
print(f"Found {len(results)} results:")
99+
for idx, result in enumerate(results, 1):
100+
print(f"{idx}. {result.text}")
101+
102+
103+
run(main())

postgres/src/example/vector/common/__init__.py

Whitespace-only changes.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .dsn import build_postgres_dsn
2+
from .initialize import initialize_pgvector
3+
4+
__all__ = ["build_postgres_dsn", "initialize_pgvector"]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from os import getenv
2+
3+
__all__ = ["build_postgres_dsn"]
4+
5+
6+
def build_postgres_dsn() -> str:
7+
"""Build PostgreSQL DSN from environment variables.
8+
9+
Reads connection parameters from environment variables:
10+
- POSTGRES_USER (default: postgres)
11+
- POSTGRES_PASSWORD (default: postgres)
12+
- POSTGRES_HOST (default: localhost)
13+
- POSTGRES_PORT (default: 5432)
14+
- POSTGRES_DATABASE (default: postgres)
15+
16+
Returns:
17+
PostgreSQL DSN in format: postgresql://user:password@host:port/database
18+
"""
19+
pg_user = getenv("POSTGRES_USER", "postgres")
20+
pg_password = getenv("POSTGRES_PASSWORD", "postgres")
21+
pg_host = getenv("POSTGRES_HOST", "localhost")
22+
pg_port = getenv("POSTGRES_PORT", "5432")
23+
pg_database = getenv("POSTGRES_DATABASE", "postgres")
24+
return f"postgresql://{pg_user}:{pg_password}@{pg_host}:{pg_port}/{pg_database}"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from asyncpg.connection import Connection
2+
from pgvector.asyncpg import register_vector
3+
4+
__all__ = ["initialize_pgvector"]
5+
6+
7+
async def initialize_pgvector(connection: Connection) -> None:
8+
"""Initialize pgvector extension for the connection.
9+
10+
This function registers the vector type with asyncpg to enable
11+
vector operations on PostgreSQL connections.
12+
"""
13+
await register_vector(connection)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"""Generator package for creating test documents and queries."""
2+
3+
from .document_generator import generate_documents
4+
from .query_generator import generate_query_variations
5+
6+
__all__ = ["generate_documents", "generate_query_variations"]

0 commit comments

Comments
 (0)