pydantic-ai-pixeltable
Pydantic AI Harness integration for Pixeltable. Two independent layers:
PixeltableMemoryStore: persist HarnessMemoryin a Pixeltable table (sameMemory(store)slot asFileStore)Pixeltable: read-only catalog tools (list_tables,describe_table,query_table,similarity_search) over tables you already have
Neither requires the other. Requires Pixeltable >= 0.7.8 and pydantic-ai-harness >= 0.29.0.
This is an interoperability bridge. Native Pixeltable agents still use a TableModel and computed columns.
How it fits together
Native backend (FileStore) This package (Pixeltable)
Memory(FileStore) Memory(PixeltableMemoryStore)
└ .agent-memory/ └ pxt table 'harness.memory'
├ main/MEMORY.md ├ kind='file' rows path, content, version
├ ... (one .md per memory path) └ kind='op' rows '__op__/<id>' receipts
└ .memory-store.sqlite3 same catalog as your data; inspect via store.table
(versions + operation journal)
Pixeltable(tables=[...]) (optional, independent)
└ your existing tables/views, incl. pxt.Document and
embedding indexes -> query_table, similarity_search
Unique to this backend: memory rows are ordinary catalog rows. store.search stays lexical (the Harness contract), but through store.table you can add an embedding index on content and run Pixeltable similarity queries over memory, or join it with the rest of the catalog.
Installation
pip install pydantic-ai-pixeltable
The examples use an Anthropic model; add its client with pip install "pydantic-ai-slim[anthropic]".
Quick Start
Memory in the catalog, plus read-only tools over the tables you already have:
from pydantic_ai import Agent
from pydantic_ai_harness import Memory, ToolOutputLimits
from pydantic_ai_pixeltable import Pixeltable, PixeltableMemoryStore
agent = Agent(
"anthropic:claude-sonnet-4-6",
capabilities=[
Memory(PixeltableMemoryStore(table_name="harness.memory")),
Pixeltable(tables=["my_app.doc_chunks"]),
ToolOutputLimits(),
],
)
Prefer FileStore (or PostgresMemoryStore) when the notebook does not need to live in the catalog; the choice is per project.
tables is a required allowlist of table paths or directory prefixes. Pass tables=["*"] to allow the whole catalog, including the memory table and its __op__ receipt payloads.
Two Pixeltable(...) instances share id="pixeltable" and merge by narrowing: an entry survives only when every capability covers it, and a disjoint merge raises. A capability passed to a single run replaces the agent's rather than merging with it, so it can widen the allowlist. pydantic-ai-chdb also registers list_tables and describe_table; wrap one side in PrefixTools when you pair them.
Catalog tools
Pixeltable does not create tables or insert rows. Point it at a table or view that already has data, plus an embedding index for similarity_search.
query_tablefilters with equality only ({"status": "open"}); timestamp, date, and UUID values are ISO strings. Media, array, and binary columns reject non-null filters, and computed columns that are not stored reject all filters.similarity_searchcallscolumn.similarity(string=query)and needs an embedding index on that column.- Output is bounded by
max_rowsandmax_chars: an oversized value is cut (ending in...) rather than dropping its row, and the minimal{"table", "rows", "truncated"}envelope is always returned, even whenmax_charsis set below its size. Default columns skip media, array, binary, and computed columns that are not stored (they would recompute per query); a named media column returns a file URL, not a blob.
Declare the tables and index on a TableModel in app.py; pxt schema update creates them:
import pixeltable as pxt
import pixeltable.functions as pxtf
from pixeltable.functions.huggingface import sentence_transformer
TableModel = pxt.model_base()
embed_fn = sentence_transformer.using(model_id="intfloat/multilingual-e5-large-instruct")
class Docs(TableModel, name="docs"):
document: pxt.Document
class Chunks(
TableModel,
name="doc_chunks",
base=Docs,
iterator=pxtf.document.document_splitter(Docs.document, separators="paragraph"),
):
__indexes__ = [pxt.EmbeddingIndex(text, embedding=embed_fn, name="chunks_embed")] # type: ignore[name-defined]
pxt init
pxt schema update app.py my_app
A notebook or REPL can still call create_table, create_view, and add_embedding_index. Do not put those calls in app.py.
YAML spec
agent = Agent.from_spec(
{
"model": "anthropic:claude-sonnet-4-6",
"capabilities": [{"Pixeltable": {"tables": ["my_app.doc_chunks"]}}],
},
custom_capability_types=[Pixeltable],
)
The short form {"Pixeltable": ["my_app.doc_chunks"]} passes the allowlist alone. Without custom_capability_types=[Pixeltable], Agent.from_spec does not know the class.
Memory store
- The table is created on first use; you do not run
pxt schema updatefor it. - Each path is one row (
kind == "file"); operation receipts are rows under__op__/. Path roots__meta__and__op__are reserved. - Compare-and-set:
expected_versionmust equal the row's version. Versions are unique UUID strings, not monotonic. A stale version raisesMemoryConflictError. - Operation receipts are journaled
__op__rows: the intended mutation is recorded before it is applied, so a mid-write crash rolls forward or replays cleanly instead of double-applying. - Paths are limited to 255 characters, since the primary-key index covers the first 256.
- Pixeltable keeps old row versions for every update and delete, and receipts are never pruned, so the table grows with history.
Memory(PixeltableMemoryStore)is Python-only. Harness YAML backends arememory,file, andsqlite.- This package emits no telemetry of its own; the
memory.*spans come from the HarnessMemorycapability.
Escape hatch: .table
store = PixeltableMemoryStore(table_name="harness.memory")
t = store.table
t.where(t.kind == "file").select(t.path, t.content, t.version).collect()
Use .table to query. A raw t.update of content is not a Memory write: the version does not change, and the next compare-and-set can overwrite it.
Measured results
Pixeltable 0.7.8 on embedded PostgreSQL, ~20k rows per test, laptop hardware (pytest tests/test_stress.py -v -m expensive):
| Operation | Time |
|---|---|
list_paths, limit 50, prefix holding 18k of 20k files |
~115 ms |
search (lexical), 100-file scan bound, same prefix |
~125 ms |
| CAS write under contention (200 tasks, 4 paths) | ~5 ms per attempt, exactly 4 winners |
query_table equality filter, limit 20 over 20k rows |
~10 ms |
similarity_search top-3 over 20k indexed rows |
~8 ms |
| Embedding index build over 20k rows | ~2.8 s |
list_paths and search sort every path under the prefix in Python, because database ordering depends on collation, so their cost grows with the files in that namespace, not with the whole table.
Development
pip install -e ".[dev]"
pytest tests/ -v
pytest tests/test_stress.py -v -m expensive
ruff check . && ruff format --check .
pytest tests/ -v skips @pytest.mark.expensive (~20k-row Memory and catalog volume).
License
Apache 2.0
Metadata
Release files for pydantic-ai-pixeltable 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pydantic_ai_pixeltable-0.3.0.tar.gz | 37.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pydantic_ai_pixeltable-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.8 kB
Release files / pydantic_ai_pixeltable-0.3.0.tar.gz
| Download URL | pydantic_ai_pixeltable-0.3.0.tar.gz |
|---|---|
| Size | 37.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b66608678eca3add7f3a9a5bc12adb56b6b743b4a116118b25bba1270280f3c1
|
|
BLAKE2b-256 checksum How to use checksums |
32c624d3f18881d1a0f4e1635e219e6edbd2db2336dd14aa6291b68cb871dc59
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / pydantic_ai_pixeltable-0.3.0-py3-none-any.whl
| Download URL | pydantic_ai_pixeltable-0.3.0-py3-none-any.whl |
|---|---|
| Size | 23.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
35405905e2967f7d723e066bf996dbc30290db1fc6bb58a054847c14b85aac52
|
|
BLAKE2b-256 checksum How to use checksums |
915aa49eccc0259245d47a4dfae585f31e24085101f31aa2153b3fa8047db171
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency log