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.6.8 and pydantic-ai-harness >= 0.29.0.
This is an interoperability bridge. Native Pixeltable agents still use a TableModel and computed columns.
Installation
pip install pydantic-ai-pixeltable
Quick Start
The usual pairing is FileStore Memory (a notebook you can open) plus Pixeltable catalog tools (a corpus you can search). Search inside Memory is lexical; similarity_search on catalog tables uses the embedding index.
from pydantic_ai import Agent
from pydantic_ai_harness import Memory, ToolOutputLimits
from pydantic_ai_harness.memory import FileStore
from pydantic_ai_pixeltable import Pixeltable
agent = Agent(
"openai:gpt-4o-mini",
capabilities=[
Memory(FileStore(".agent-memory")),
Pixeltable(tables=["my_app.doc_chunks"], read_only=True),
ToolOutputLimits(),
],
)
Use PixeltableMemoryStore when the notebook should live in the catalog:
from pydantic_ai_harness import Memory
from pydantic_ai_pixeltable import PixeltableMemoryStore
Memory(PixeltableMemoryStore(table_name="harness.memory"))
tables is a required allowlist of table paths or directory prefixes. Pass tables=["*"] to allow the whole catalog.
Two Pixeltable(...) instances share id="pixeltable" and merge. 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"}); media, array, and binary columns reject non-null filters.similarity_searchcallscolumn.similarity(string=query)and needs an embedding index on that column.- Output is bounded by
max_rowsandmax_chars; the minimal{"table", "rows", "truncated"}envelope is always returned, even whenmax_charsis set below its size. Default columns skip media, array, and binary; a named media column returns a file URL, not a blob. read_only=Falseis not implemented.
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 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": "openai:gpt-4o-mini",
"capabilities": [{"Pixeltable": {"tables": ["my_app.doc_chunks"]}}],
},
custom_capability_types=[Pixeltable],
)
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. - Receipts are a second write after the file mutation, not one SQL transaction. A crash between them can raise
MemoryConflictErroron replay instead ofreplayed=True. Memory(PixeltableMemoryStore)is Python-only. Harness YAML backends arememory,file, andsqlite.
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.
Tables created by pre-release revisions with an Int version column are rejected on first use; drop and recreate them.
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 over 20k files |
~12 ms |
search (lexical), 100-file scan bound |
~8 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.3 s |
Against the previous revision: cached table handles remove one catalog lookup per call, catalog tools fetch metadata once per call instead of twice, and UUID versions remove the shared generation-row update, cutting contended CAS write time ~20% (1.26 s to 1.02 s for 200 attempts).
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.1.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.1.0.tar.gz | 28.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pydantic_ai_pixeltable-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.3 kB
Release files / pydantic_ai_pixeltable-0.1.0.tar.gz
| Download URL | pydantic_ai_pixeltable-0.1.0.tar.gz |
|---|---|
| Size | 28.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c24c92595c1575681b643fc9c21e64287ff16c54cfa0598189ad0a159e422ad4
|
|
BLAKE2b-256 checksum How to use checksums |
e8e699a0ce42c61b126539a136b654b8dc9aba031bd9c04e6301d4c178c6d971
|
| 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.1.0-py3-none-any.whl
| Download URL | pydantic_ai_pixeltable-0.1.0-py3-none-any.whl |
|---|---|
| Size | 18.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
726dfb7f80c3af9408c4c973753b9db3b6490a5700c629d6a031deb9794236d2
|
|
BLAKE2b-256 checksum How to use checksums |
9792d699936736544568e23c99c897f5526b97a4d66ce617b9a2c4fcd600bc07
|
| 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