Skip to main content
Pre-release

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(), and ensure_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=False for supplied embeddings or pass a local embedding_generator. Retrieval excludes vectors unless include_vectors=True.
  • Exact SQL vector search supports DEFAULT/cosine_distance, cosine_similarity, euclidean_distance, dot_prod, and negative_dot_prod. The default score is cosine distance, so lower is better and score_threshold is 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)

Source distribution for agent-framework-duckdb 1.0.0a261002
File Size Uploaded
agent_framework_duckdb-1.0.0a261002.tar.gz 16.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-framework-duckdb 1.0.0a261002
File Interpreter ABI Platform
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}

Release history Release notifications | RSS feed

This release

1.0.0a261002 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page