Observed since 20 Oct, 21:59 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
DuckDB vector stores for Microsoft Agent Framework.
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.
pip install agent-framework-duckdb --pre
Python 3.10–3.14 and DuckDB 1.4.1–1.5.x are officially supported. DuckDB does
not yet publish Python 3.15 wheels; installation may build it from source, but
this connector does not advertise Python 3.15 support until upstream wheels are
available. 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].
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.
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.
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.generate_vectors=False for supplied
embeddings or pass a local embedding_generator. Retrieval excludes vectors
unless include_vectors=True.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.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.Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.0.0a261015 | 15 Oct 2026 | 3 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 21:59 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:71e7dfa17607a587ab3d8e389ec0f698e41a2cd64ec934b0900699f4fc57f5d4