Repository navigation
Run on Pixeltable 0.7.6, add Memory.search(), and add CI - #3
Merged
Merged
Conversation
Document, audio and video memories raised on construction against any Pixeltable >= 0.5.6. config.py carried the 0.4.2 iterator parameter names, and those names are not cosmetic: they go through dataclasses.asdict() straight into the iterator call, so each one is an API contract. chunk_duration_sec/overlap_sec/min_chunk_duration_sec are now duration/overlap/min_segment_duration, and html_skip_tags is skip_tags. The pixeltable.iterators shims forward **kwargs verbatim with no name mapping, which is why these were hard errors rather than warnings; they are replaced by document_splitter / audio_splitter / string_splitter / frame_iterator. Also fixed: - the audio view output column is `audio_segment`, not `audio_chunk` - transcriptions() kwargs were splatted top-level; the signature is (audio, model, model_kwargs), so WhisperParams(language='en') raised - if_exists="replace_force" on five paths dropped and rebuilt every view and index on every construction, re-running Whisper and re-embedding everything; now "ignore", which the existing get_table fallbacks expect - non-nullable-by-default (0.7.3) made every field mandatory; Context gains `required` so a field can be optional - pxt.list_dirs() returns slash paths, so the dotted-namespace check never matched and nested namespaces failed on second construction; replaced by create_dir(parents=True, if_exists="ignore") - __getattr__ dereferenced self.resources from inside itself, so any __init__ failure turned the next attribute access into RecursionError - vision.py moves off the deprecated openai.vision to chat_completions, and the anthropic path gains its required max_tokens, base64-encodes the image instead of passing a raw column reference as the data value, and routes llm_kwargs through model_kwargs None of this was caught because there was no CI on any branch and uv.lock pinned pixeltable 0.4.2, so local installs resolved to a version where the code worked while `pip install pixelmemory` got a package that could not run its own README. The most recent commit on main was "Removed test". The new suite is construction-only, so it needs no API key, no model weights, no spaCy and no media fixtures; conftest.py pops the provider keys from the environment so the tests prove it. It asserts on get_metadata()['iterator_call'] (which catches the kwarg-rename class of bug precisely) and on view ids being stable across reconstruction (which catches replace_force). CI installs pixeltable unpinned with a weekly cron, so the next breaking release turns this build red instead of a user's install. Against the pre-fix source the suite reports 14 failures; against this commit, 19 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pixelmemory indexed your data and then left retrieval to you. Every caller
wrote the same block by hand:
chunk_view = memory.chunk_views["report"]
sim = chunk_view.text.similarity(query)
results = (chunk_view.order_by(sim, asc=False).limit(3)
.select(chunk_view.text, similarity=sim).collect())
That appeared eight times across the examples and README, and it did not
work: `chunk_views` is a list of dataclasses on `resources`, not a mapping on
`Memory`, so the first line raised AttributeError. It also used the
positional `.similarity(query)` form, deprecated since 0.5.7.
MemoryResources gains a `search_targets` list, populated at each of the seven
places an embedding index is created: the document chunk view, the audio
sentence view, chunked and unchunked strings, the direct string index, image
descriptions, and video frame captions. Video registers its frames under
"<col>_frames" so a caller can search transcript and visuals separately.
memory.search("feline animal", limit=2)
memory.search(query, on="report")
search() always passes idx=, because chunked text carries two indexes on one
column and a bare .similarity() there is ambiguous. views() returns the raw
table handles for queries search() does not cover, which is what the broken
`memory.chunk_views["report"]` was reaching for.
Errors name what is available rather than failing blankly: searching an
unknown context id lists the real ones, and searching a memory with no
embedding index says to pass embed=True.
Suite goes 19 -> 26. Most are metadata-only; two rank real embeddings, which
downloads a small sentence-transformers model but still needs no API key.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every multimodal and integration example opened its retrieval block with
`memory.chunk_views["..."]`, which raised AttributeError: chunk_views is a
list of dataclasses on `resources`, not a mapping on Memory. So audio.py,
pdf.py, website.py, video.py, crewai_agentic_rag.py and
langchain_financial_rag_agent.py all failed at the search step.
Most collapse to one line now:
results = memory.search(query, on="audio", limit=3)
video.py searches transcript and frame captions separately, which is what the
"<col>_frames" registration is for. text.py's `where(sim >= 0.1)` becomes
search(min_score=0.1).
Two keep a raw handle deliberately, because search() does not cover what they
do, and both say so in a comment: langchain_financial_rag_agent.py filters on
metadata as well as ranking, and images.py returns the image alongside its
caption. Both move to views() and the keyword `.similarity(string=..., idx=...)`.
The remaining positional `.similarity(query)` calls are gone, in the examples,
in the README, and in the FastAPI service. Two grep tests keep them gone: one
for positional similarity, one for chunk_views/frame_views access. They are
greps rather than execution tests because most examples need media files or a
provider key to run.
Suite 26 -> 28.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pierrebrunelle
added a commit
that referenced
this pull request
Sep 10, 2026
* chore: document the migration, scope the library, bump to 0.2.0 PyPI still serves 0.1.0 with an unbounded `pixeltable>=0.3.8`, so a fresh `pip install pixelmemory` resolves Pixeltable 0.7.6 and gets a package whose Document, Audio and Video memories raise on construction. #3 fixed the repo; this is what makes the fix reachable. Adds a "Migrating from 0.1.x" section covering the three caller-visible changes: Python 3.11+, the two renamed splitter parameter dataclasses, and search()/views() replacing the chunk_views/frame_views subscripts. The renames are called out as unavoidable, because those field names are passed straight through to Pixeltable's iterators and so are the API rather than a naming choice. Adds "When not to use pixelmemory". If the schema is fixed when you write the code, TableModel and FastAPIRouter do this natively and the wrapper is not earning anything; it is worth saying so rather than letting someone discover it later. What pixelmemory is actually for is a schema chosen at runtime, plus the multimodal ingest pipelines it wires in the right dependency order. The two grep gates now skip the migration section: a migration guide has to quote the old broken calls to show what replaces them, so scanning it for those calls failed. Verified the gates still fire on a violation injected outside that section. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix: regenerate uv.lock for 0.2.0, and make CI check it The version bump left uv.lock recording 0.1.0. CI did not notice, because it ran a bare `uv sync`, which quietly rewrites the lock instead of failing. pixelagent's workflow uses --locked and caught the same drift immediately, so this adopts it: a lockfile that disagrees with pyproject.toml is now a red build rather than a silent fixup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Document, audio and video memories raised on construction against any Pixeltable >= 0.5.6.
config.pycarried the 0.4.2 iterator parameter names, and those names are not cosmetic — they go throughdataclasses.asdict()straight into the iterator call, so each field name is an API contract:chunk_duration_secdurationoverlap_secoverlapmin_chunk_duration_secmin_segment_durationhtml_skip_tagsskip_tagsThe
pixeltable.iteratorsshims forward**kwargsverbatim with no name mapping, which is why these were hard errors rather than deprecation warnings.Separately, every retrieval example was dead code: they all opened with
memory.chunk_views["..."], andchunk_viewsis a list of dataclasses onresources, not a mapping onMemory.This went unnoticed because there is no CI on any branch and
uv.lockpinned pixeltable 0.4.2. The most recent commit onmainis766c5ba "Removed test".What is here
1. Green baseline + CI (
d5091a3)Iterator parameter renames and the deprecated shims swapped for
document_splitter/audio_splitter/string_splitter/frame_iterator. Plus:audio_segment, notaudio_chunktranscriptions()kwargs were splatted top-level; the signature is(audio, model, model_kwargs), soWhisperParams(language='en')raisedif_exists="replace_force"on five paths dropped and rebuilt every view and index on every construction, re-running Whisper and re-embedding everythingContextgainsrequiredpxt.list_dirs()returns slash paths, so the dotted-namespace check never matched and nested namespaces failed on the second construction__getattr__dereferencedself.resourcesfrom inside itself, so any__init__failure becameRecursionErrorvision.pyoff deprecatedopenai.vision; the anthropic path gains its requiredmax_tokensand base64-encodes the image instead of passing a raw column reference as thedatavalueThe suite is construction-only: no API key, no model weights, no spaCy, no media fixtures.
conftest.pypops the provider keys from the environment so the tests prove it. It asserts onget_metadata()['iterator_call'], which pins the kwarg-rename class of bug precisely, and on view ids being stable across reconstruction, which pinsreplace_force. CI installs pixeltable unpinned with a weekly cron. Against the pre-fix source it reports 14 failures.2.
Memory.search()andMemory.views()(494ca94)MemoryResourcesgains asearch_targetslist populated at each of the seven places an embedding index is created. Video registers its frames under"<col>_frames"so transcript and visuals can be searched separately.search()always passesidx=, because chunked text carries two indexes on one column and a bare.similarity()there is ambiguous.3. Repair the six broken examples (
4ebcb9e)Most collapse to one line. Two keep a raw handle deliberately and say why in a comment: the langchain example filters on metadata as well as ranking, and
images.pyreturns the image alongside its caption. Two grep tests keep positional.similarity()andchunk_views[...]from coming back.Public API
Memory(context=, namespace=, table_name=, if_exists=),memory.Entry/add(), the__getattr__passthrough, and allContextfield names are unchanged.Deliberate breaks, both needing a 0.2.0 bump: the two splitter param dataclasses are renamed (unavoidable — those names are the iterator kwargs), and views/indexes now survive reconstruction instead of being rebuilt.
Still to do
Partial; landing it reviewable rather than sitting on it. Not yet done:
uuid7()primary keys +Context(index=),as_tools()/lookup_tool(),Memory.router()replacing the 280-line FastAPI example, andexamples/app.py.A question worth your judgement
The audit raised whether the wrapper still earns its keep, and I think it partly does not.
FastAPIRouteris verified working against runtime-built tables (add/search/lookup/delete all returned real rows viaTestClient), which makes most ofexamples/fastapi/memory_service.pydeletable. What survives is the one thingTableModelcannot express: a schema chosen at runtime fromMemory(context=json.load(f)), plus the multimodal ingest pipelines. A dynamicTableModelis possible — I verified it — but it leans onsys._getframe(1)and a table-name-keyed registry, so I did not take it.🤖 Generated with Claude Code