# PostgreSQL vector support for asyncpg
Adds PostgreSQL [vector](https://github.com/pgvector/pgvector) support for Python.
Registers data types `vector` and `halfvec` from the PostgreSQL extension `vector` to the asynchronous PostgreSQL client `asyncpg`, and marshals vector data to and from PostgreSQL database tables.
Internally, the data is packed into a Python `bytes` object, with single-precision float vectors stored on 4 bytes per item (for class `Vector`) and half-precision float vectors stored on 2 bytes per item (for class `HalfVector`). Data is (un)packed using `struct` from the standard library as necessary.
This module provides functionality similar to [pgvector-python](https://github.com/pgvector/pgvector-python) but imports minimum dependencies (e.g. no dependency on `numpy`).
## Setup
#### Install the package
```sh
pip install asyncpg_vector
```
#### Initialize
Register vector types with your database connection or connection pool:
**Connection**:
```python
from asyncpg_vector import register_vector
async def main() -> None:
...
await conn.execute("CREATE EXTENSION IF NOT EXISTS vector")
await register_vector(conn)
```
**Pool**:
```python
from asyncpg_vector import register_vector
async def init_connection(conn: asyncpg.Connection) -> None:
await conn.execute("CREATE EXTENSION IF NOT EXISTS vector")
await register_vector(conn)
async def main() -> None:
...
pool = await asyncpg.create_pool(..., init=init_connection)
```
#### Perform similarity search
First, create a table and an index:
```python
async def create(conn: asyncpg.Connection) -> None:
await conn.execute("""
CREATE TABLE IF NOT EXISTS items
(
id bigint NOT NULL GENERATED ALWAYS AS IDENTITY,
content text NOT NULL,
embedding halfvec(1536) NOT NULL,
CONSTRAINT pk_items PRIMARY KEY (id)
);
CREATE INDEX IF NOT EXISTS embedding_index ON items
USING hnsw (embedding halfvec_cosine_ops);
""")
```
Next, find documents in a knowledge base that match a search phrase using vector similarity with approximate nearest neighbor semantics:
```python
from asyncpg_vector import HalfVector
async def search(conn: asyncpg.Connection, phrase: str) -> list[str]:
...
embedding_response = await ai_client.embeddings.create(
input=phrase,
model="text-embedding-3-small",
encoding_format="base64"
)
embedding = HalfVector.from_float_base64(embedding_response.data[0].embedding)
query = """
SELECT
id,
content,
embedding <=> $1 AS distance
FROM items
ORDER BY distance
LIMIT 5
"""
rows = await conn.fetch(query, embedding)
...
```
Raw data
{
"_id": null,
"home_page": null,
"name": "asyncpg-vector",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": "Levente Hunyadi <hunyadi@gmail.com>",
"keywords": "asyncpg, postgresql-extension, vector, vector-search",
"author": null,
"author_email": "Levente Hunyadi <hunyadi@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/52/b9/18da43f39767b1b446dced41ebe756ee4f180e8416801a006936bface6a7/asyncpg_vector-0.1.0.tar.gz",
"platform": null,
"description": "# PostgreSQL vector support for asyncpg\r\n\r\nAdds PostgreSQL [vector](https://github.com/pgvector/pgvector) support for Python.\r\n\r\nRegisters data types `vector` and `halfvec` from the PostgreSQL extension `vector` to the asynchronous PostgreSQL client `asyncpg`, and marshals vector data to and from PostgreSQL database tables.\r\n\r\nInternally, the data is packed into a Python `bytes` object, with single-precision float vectors stored on 4 bytes per item (for class `Vector`) and half-precision float vectors stored on 2 bytes per item (for class `HalfVector`). Data is (un)packed using `struct` from the standard library as necessary.\r\n\r\nThis module provides functionality similar to [pgvector-python](https://github.com/pgvector/pgvector-python) but imports minimum dependencies (e.g. no dependency on `numpy`).\r\n\r\n## Setup\r\n\r\n#### Install the package\r\n\r\n```sh\r\npip install asyncpg_vector\r\n```\r\n\r\n#### Initialize\r\n\r\nRegister vector types with your database connection or connection pool:\r\n\r\n**Connection**:\r\n\r\n```python\r\nfrom asyncpg_vector import register_vector\r\n\r\nasync def main() -> None:\r\n ...\r\n\r\n await conn.execute(\"CREATE EXTENSION IF NOT EXISTS vector\")\r\n await register_vector(conn)\r\n```\r\n\r\n**Pool**:\r\n\r\n```python\r\nfrom asyncpg_vector import register_vector\r\n\r\nasync def init_connection(conn: asyncpg.Connection) -> None:\r\n await conn.execute(\"CREATE EXTENSION IF NOT EXISTS vector\")\r\n await register_vector(conn)\r\n\r\nasync def main() -> None:\r\n ...\r\n\r\n pool = await asyncpg.create_pool(..., init=init_connection)\r\n```\r\n\r\n#### Perform similarity search\r\n\r\nFirst, create a table and an index:\r\n\r\n```python\r\nasync def create(conn: asyncpg.Connection) -> None:\r\n await conn.execute(\"\"\"\r\n CREATE TABLE IF NOT EXISTS items\r\n (\r\n id bigint NOT NULL GENERATED ALWAYS AS IDENTITY,\r\n content text NOT NULL,\r\n embedding halfvec(1536) NOT NULL,\r\n CONSTRAINT pk_items PRIMARY KEY (id)\r\n );\r\n\r\n CREATE INDEX IF NOT EXISTS embedding_index ON items\r\n USING hnsw (embedding halfvec_cosine_ops);\r\n \"\"\")\r\n```\r\n\r\nNext, find documents in a knowledge base that match a search phrase using vector similarity with approximate nearest neighbor semantics:\r\n\r\n```python\r\nfrom asyncpg_vector import HalfVector\r\n\r\nasync def search(conn: asyncpg.Connection, phrase: str) -> list[str]:\r\n ...\r\n\r\n embedding_response = await ai_client.embeddings.create(\r\n input=phrase,\r\n model=\"text-embedding-3-small\",\r\n encoding_format=\"base64\"\r\n )\r\n embedding = HalfVector.from_float_base64(embedding_response.data[0].embedding)\r\n query = \"\"\"\r\n SELECT\r\n id,\r\n content,\r\n embedding <=> $1 AS distance\r\n FROM items\r\n ORDER BY distance\r\n LIMIT 5\r\n \"\"\"\r\n rows = await conn.fetch(query, embedding)\r\n\r\n ...\r\n```\r\n",
"bugtrack_url": null,
"license": null,
"summary": "PostgreSQL vector support for asyncpg",
"version": "0.1.0",
"project_urls": {
"Homepage": "https://github.com/hunyadi/asyncpg_vector",
"Source": "https://github.com/hunyadi/asyncpg_vector"
},
"split_keywords": [
"asyncpg",
" postgresql-extension",
" vector",
" vector-search"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "fda6a502940ae95bca7ff911e2c88b4f74fe03276be60418028e5bc68d5b5c25",
"md5": "42b8f751acb4aeb79641a65a0d3b7b09",
"sha256": "befe4248832098c9cac9e53b41360d0184a4eb1b4c8ea746599d08cb0cb2050b"
},
"downloads": -1,
"filename": "asyncpg_vector-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "42b8f751acb4aeb79641a65a0d3b7b09",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 5766,
"upload_time": "2025-11-01T09:48:13",
"upload_time_iso_8601": "2025-11-01T09:48:13.273144Z",
"url": "https://files.pythonhosted.org/packages/fd/a6/a502940ae95bca7ff911e2c88b4f74fe03276be60418028e5bc68d5b5c25/asyncpg_vector-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "52b918da43f39767b1b446dced41ebe756ee4f180e8416801a006936bface6a7",
"md5": "59c40c82fcb4513b84d61f66167e9d59",
"sha256": "2c2a3c431d5e71e9201f55e01d2d3c68034513ef6c3be1c3e6cf686c0b72475b"
},
"downloads": -1,
"filename": "asyncpg_vector-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "59c40c82fcb4513b84d61f66167e9d59",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 5551,
"upload_time": "2025-11-01T09:48:14",
"upload_time_iso_8601": "2025-11-01T09:48:14.625926Z",
"url": "https://files.pythonhosted.org/packages/52/b9/18da43f39767b1b446dced41ebe756ee4f180e8416801a006936bface6a7/asyncpg_vector-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-11-01 09:48:14",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "hunyadi",
"github_project": "asyncpg_vector",
"github_not_found": true,
"lcname": "asyncpg-vector"
}