This release is a pre-release and may not be stable for production use.
Agent Framework DuckDB vector store
Store and search typed records in DuckDB using the
Microsoft Agent Framework
vector-store APIs. This alpha package uses the DuckDB Python client only:
local files, DuckDB-supported connection URIs (including MotherDuck), and
caller-provided duckdb.DuckDBPyConnection objects use the same implementation.
Installation
pip install agent-framework-duckdb --pre
Python 3.10+ and DuckDB 1.4.1–1.5.x are required. The package also installs
pytz, which DuckDB uses when returning timezone-aware timestamps. Import
DuckDBStore and DuckDBCollection directly from agent_framework_duckdb;
this alpha package is not part of agent-framework-core[all].
Connections and persistence
DuckDBStore() (and a directly constructed DuckDBCollection) defaults to
agent-framework.duckdb in the current working directory. It is a normal
persistent database file, not an in-memory database or temporary file. Give
each application a suitable writable location with
connection_string="path/to/vectors.duckdb"; parent directories must already
exist. The connection is opened lazily on the first async operation.
aclose() or an async context manager drains pending operations and releases
the file. Opening a new store against the same filename restores its tables
and records.
DUCKDB_CONNECTION_STRING can instead provide a filename or a URI. Settings
precedence is explicit connection_string > selected .env file >
environment > persistent default. Select a file with env_file_path and
optionally env_file_encoding; .env files are never discovered implicitly.
An explicitly empty connection string is an error. The URI is held as an AF
SecretString and never printed or logged by the connector.
To use MotherDuck,
configure the same DuckDB client with a md: URI and its usual credentials,
for example:
export DUCKDB_CONNECTION_STRING='md:my_db'
export MOTHERDUCK_TOKEN='<your access token>'
from agent_framework_duckdb import DuckDBStore
async def list_remote_tables() -> None:
async with DuckDBStore() as store:
print(await store.list_collection_names())
For an application-managed token, pass
DuckDBStore(connection_string="md:my_db", config={"motherduck_token": SecretString(token)}) (import SecretString
from agent_framework). config is forwarded to duckdb.connect; it is
not loaded from the connector's .env file. Keep tokens outside source control
and avoid embedding them in a URI. Other DuckDB-supported services use their
documented URI/configuration or an injected client; no service-specific
adapter is installed. DuckDB/MotherDuck may need network access and compatible
client/extension versions; authentication and service availability are managed
by DuckDB, not by this connector.
Alternatively, use DuckDBStore(client=duckdb.connect(...)) or
DuckDBCollection(Record, client=...) to borrow an existing connection.
The caller owns and closes that connection; client cannot be combined with
connection_string, config, or .env options. Collections obtained from a
store borrow its client, so keep the store open while using them. All connector
operations on one store/collection run serially on one worker thread, not on
the event loop. Coordinate any external use of an injected connection
yourself. Cancelling an awaiter does not stop a DuckDB query already running
on that thread; aclose() waits for it to finish.
Example
from dataclasses import dataclass
from typing import Annotated
from agent_framework import Filter, VectorStoreField, vectorstoremodel
from agent_framework_duckdb import DuckDBStore
@vectorstoremodel(collection_name="articles")
@dataclass
class Article:
id: Annotated[str, VectorStoreField("key")]
text: Annotated[str, VectorStoreField("data")]
embedding: Annotated[list[float] | None, VectorStoreField("vector", dimensions=3)] = None
async def save_and_search() -> None:
async with DuckDBStore(connection_string="articles.duckdb") as store:
collection = store.get_collection(Article)
await collection.ensure_collection_exists()
await collection.upsert(
[Article("one", "DuckDB persists records", [1, 0, 0])],
generate_vectors=False,
)
async with DuckDBStore(connection_string="articles.duckdb") as reopened:
collection = reopened.get_collection(Article)
assert (await collection.get(["one"]))[0].text == "DuckDB persists records"
results = await collection.search(
vector=[1, 0, 0],
filter=Filter("text", "contains_text", "persists"),
)
async for result in results:
print(result["record"].text, result["score"])
Run a complete local example with
uv run --package agent-framework-duckdb python packages/duckdb/samples/duckdb_vectors.py
from the python/ directory. It writes a default database file, reopens it,
and cleans up only its sample table.
Capabilities and limits
- Tables are created by
ensure_collection_exists();collection_exists(),list_collection_names(), andensure_collection_deleted()provide the corresponding lifecycle. Existing tables are not migrated or reindexed. Identifiers are quoted, values are parameterized, and collection names refer to tables in the connection's current database and schema. - Batch upsert, retrieval, filtered/paged listing, and delete support string,
signed 64-bit integer, and UUID keys; string and UUID keys can be generated
when declared auto-generated. Records support string, integer, float,
boolean, UUID, bytes, date, timezone-aware datetime, JSON list/dict data,
and nullable dense vectors. Use
generate_vectors=Falsefor supplied embeddings or pass a localembedding_generator. Retrieval excludes vectors unlessinclude_vectors=True. - Exact SQL vector search supports
DEFAULT/cosine_distance,cosine_similarity,euclidean_distance,dot_prod, andnegative_dot_prod. The default score is cosine distance, so lower is better andscore_thresholdis a maximum; similarity/dot-product scores use a minimum threshold. Null vectors are omitted. Results are filtered and paged in DuckDB, with the primary key as a stable tie-breaker. - Portable filters support scalar equality/inequality, null/presence checks,
ordered scalar comparisons,
in/not_in, text contains/prefix/suffix, and AND/OR/NOT groups. They do not coerce booleans into numbers, and text wildcards are literal. JSON equality/collection membership and nested paths are not supported. - No approximate vector indexes, keyword/hybrid search, full-text or explicit data indexes, binary/sparse vectors, auto-generated integer keys, or server-side vectorization are provided. Unsupported options raise errors. DuckDB is an in-process database with file locking: multiple writer processes cannot concurrently write the same local file. Local records are unencrypted; choose and protect the database file appropriately. Connector-created batch upserts are transactional. On a borrowed connection the connector does not start/commit/roll back a caller transaction; without one, a failed batch may have partially persisted.
Metadata
Release files for agent-framework-duckdb 1.0.0a261002
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| agent_framework_duckdb-1.0.0a261002.tar.gz | 16.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agent_framework_duckdb-1.0.0a261002-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.9 kB
Release files / agent_framework_duckdb-1.0.0a261002.tar.gz
| Download URL | agent_framework_duckdb-1.0.0a261002.tar.gz |
|---|---|
| Size | 16.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
515eb6d5885f22a518ce3450a940450d1e1a1f6fbbe438b800110d2c514c41dc
|
|
BLAKE2b-256 checksum How to use checksums |
7fd3e83230026fd1e5a1a2dd59bc6621f26ba6c2f3a92a0bcde84bdb9b3b54b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / agent_framework_duckdb-1.0.0a261002-py3-none-any.whl
| Download URL | agent_framework_duckdb-1.0.0a261002-py3-none-any.whl |
|---|---|
| Size | 15.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d3377ffb421df00e52c1712a0c169af9d8a656d79244640dd4b65e593e55479a
|
|
BLAKE2b-256 checksum How to use checksums |
2541de27424542f804fc46d3eb4326996613950c75fcf6d02677488c9dee5fc7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.22 {"installer":{"name":"uv","version":"0.12.22","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|